How to push data to array in factory/service (ANGULAR/IONIC) - javascript

I want to create an app that work like this : https://ionic-songhop.herokuapp.com
As you can see, when we click favorite button, the item will store in factory and we can invoke in another page (favorite page)
In my case : i use service to store the item data and create factory to store the pushed item.
Here's my code : (I store data in service)
.service('dataService',function(){
var service=this;
this.playerlist = [
{ name: 'Leonel Messi', ava:"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68" },
{ name: 'Cristiano Ronaldo', ava:"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68" },
{ name: 'Zlatan Ibrahimovic', ava:"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68" },
{ name: 'Wayne Rooney', ava:"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68" },
{ name: 'Michael Carrick', ava:"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68" },
{ name: 'Phil Jones', ava:"https://pbs.twimg.com/profile_images/473469725981155329/E24vfxa3_400x400.jpeg" },
{ name: 'Angel di Maria', ava:"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68" }
];
})
.factory('User', function() {
var play = { favorites: []}
play.addToFavorites = function(song) {
play.favorites.unshift(song);
}
play.removeFromFavorites = function(player, index) {
play.favorites.splice(index, 1);
}
return play;
})
Controller :
.controller('ChooseTabCtrl', function($scope, dataService, User) {
$scope.dataService=dataService;
$scope.addToFavorite = function (item) {
User.favorites.unshift(dataService.playerList.indexOf(), 1);
}
})
But when i click the favorite button on each item, the list dont show in favorite page.
Is it possible to do like this in Ionic app?
Here's my codepen : http://codepen.io/harked/pen/WvJQWp

There are a few issues with the code in your codepen...
In the controller you are referencing dataService.playerList.indexOf() when the player object is actually playerlist (all lowercase). Also, I assume you want to actually get the indexOf the player so that line needs to change to:
User.favorites.unshift(dataService.playerlist.indexOf(item));
// remove the `, 1` otherwise you'll be adding a `1` to the array everytime
and in your view, you need to change the following:
// wrong
ng-click="addToFavorite(item)"
// right
ng-click="addToFavorite(player)"
Next, in your ListTabCtrl change the following:
$scope.players=dataService;
// to
$scope.players=dataService.playerlist;
Then in the view:
<ion-item ng-repeat="player in favorites" class="item item-avatar" href="#">
<img ng-src="{{players[player].ava}}">
<h2>{{players[player].name}}</h2>
<p>Back off, man. I'm a scientist.</p>
<ion-option-button class="button-assertive" ng-click="removePlayer(player, $index)">
<i class="ion-minus-circled"></i>
</ion-option-button>
</ion-item>
I have posted a working example of your code on jsbin: http://jsbin.com/lukodukacu/edit?html,css,js,output

Related

How do I pop values of an array on deselection of chips: AngularJS

I'm implementing a filter using chips of Angular Material. When user clicks on chips, the value associated to that chip should be stored in the array. The same way, when user deselects the chip, that value should pop from the array.
I was able to store the values but selecting and deselecting are causing addition of duplicate values in the array and I'm not able to pop them out when user deselects the chip.
Here's the excerpt from my code.
HTML Code
<div layout="row">
<md-chips ng-repeat="filter in filters" readOnly="true">
<md-chip ng-style="{'background': filter.isActive ? 'green' : '' }" class="chipStyling" ng-click="setActiveFilter(filter,$index)">
{{filter.name}}
</md-chip>
</md-chips>
</div>
JS Code
angular.module('BlankApp', ['ngMaterial', 'ngMessages'])
.controller('ctrl', function($scope) {
$scope.selectedFilters = []
$scope.isActive = false;
$scope.filters = [{
name: 'Google',
code: 'g',
isActive: false
},
{
name: 'Facebook',
code: 'f',
isActive: false
},
{
name: 'Twitter',
code: 't',
isActive: false
}
]
$scope.setActiveFilter = function(filter, index) {
filter.isActive = !filter.isActive;
if (filter.isActive) {
$scope.selectedFilters.push(filter.code);
console.log('selected filter - ' + $scope.selectedFilters)
}
}
});
I created a Code Pen on this issue. Could anyone please review and correct me what I was doing wrong.
Use a splice method for remove element from array
$scope.setActiveFilter = function(filter, index) {
filter.isActive = !filter.isActive;
if (filter.isActive) {
$scope.selectedFilters.push(filter.code);
console.log('selected filter - ' + $scope.selectedFilters)
} else {
let index = $scope.selectedFilters.indexOf(filter.code);
if (index > -1) {
$scope.selectedFilters.splice(index, 1);
}
}
}

Angular select using ng-options and ng-selected

I am having an issue with a select. I have a profile view that utilizes the profileCtrl. In that controller I get the users info from the db and put it into scope. I also use a service to grab all the info from the config table in the db and insert that into rootScope. My question and answers come from the config table (rootScope) the user's selected answer comes from the user info (scope). I need a select that preselects whatever answer the user has in the db. Below is my code.
Profile Controller:
app.controller('profileCtrl', function ($scope, $log, $http, $timeout, Data, Auth, dataShare, $sessionStorage, $rootScope, $confirm) {
$timeout(function() {
// get user's info from db and put it into scope
Data.get('profile/'+$rootScope.user.uid).then(function(data){
$scope.profs = data.data;
$scope.buttonText = 'Update Profile';
});
}, 100);
// get the configs from the configs service and put it in the rootScope
dataShare.getconfigs().then(function(data){
$rootScope.configs = data;
// get the answers from the config table for the select's options
$scope.availableAnswers = [
{ answer: $rootScope.configs[0].a1 },
{ answer: $rootScope.configs[0].a2 },
{ answer: $rootScope.configs[0].a3 },
{ answer: $rootScope.configs[0].a4 },
{ answer: $rootScope.configs[0].a5 }
];
});
// function executed on change from the select
$scope.selectedItemChanged = function() {
$log.log($scope.selectedAnswer);
}
// inline edit title
$scope.updateUser = function(data) {
Data.put('config/'+data.id, {profile_page_title:data.profile_page_title}).then(function (result) {
Data.toast(result);
});
};
$scope.saveProfile = function (profile) {
profile.roles = $rootScope.user.roles;
if(profile.uid.length > 0){
Data.put('profile/'+profile.uid, profile).then(function (result) {
$sessionStorage.user = profile;
$rootScope.user = $sessionStorage.user;
Data.toast(result);
});
}else{
Data.post('profile', profile).then(function (result) {
$rootScope.name = profile.name
Data.toast(result);
});
}
};
});
HTML: (I have condensed the code to be read easily)
<section class="row" id="" ng-repeat="profile in profs">
<div class="col-xs-12" id="questionWidget">
<h4>{{configs[0].question}}</h4>
<!-- user's answer from db -->
{{profs[0].answer}}
<select ng-model="selectedAnswer" ng-change="selectedItemChanged()" ng-options="a.answer for a in availableAnswers">
</select>
</div>
</section>
Alright, so I made a plunk, and this is what I came up with. This will set the select option if the professor's current answer exists in the list.
The reason ng-init didn't work is because the selectedAnswer model is actually expecting to see an object with property 'answer'. In other words, selectedAnswer is the entire object, not just the answer itself.
https://plnkr.co/edit/CRraZXY2jsmiV1oJx6VN?p=preview
$scope.profs = [
{ answer: 'answer2' }
]
$scope.availableAnswers = [
{ answer: 'answer1' },
{ answer: 'answer2' },
{ answer: 'answer3' },
];
angular.forEach($scope.availableAnswers, function(availableAnswer){
if ($scope.profs[0].answer === availableAnswer.answer)
$scope.selectedAnswer = availableAnswer
});
-- Old Answer --
Have you tried ng-init?
ng-init="selectedAnswer = profs[0].answer"
I have made a very complex plunkr, I was a bit slower than #brianslattery I guess.
Here's my take:
https://plnkr.co/edit/5fnB6oWrZ9WhujN7eH0T?p=preview
<section class="row" id="" ng-repeat="profile in profs">
<div class="col-xs-12" id="questionWidget">
<h4>{{configs[0].question}}</h4>
<!-- user's answer from db -->
Answer: {{profile.answer}}<br>
<select name="selector" ng-model="profile.answer" ng-change="selectedItemChanged(profile)" ng-options="a as a.answer for a in availableAnswers track by a.answer">
</select>
</div>
</section>
The app will look something like this:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($rootScope, $scope, $timeout, $log, $filter) {
$timeout(function() {
// get user's info from db and put it into scope
$scope.profs = [ { name: 'prof1', answer: 'a3' }, { name: 'prof2', answer: 'a2' } ];
$scope.buttonText = 'Update Profile';
appendAnswerObjects();
}, 100);
// get the configs from the configs service and put it in the rootScope
$timeout(function(data){
$rootScope.configs = [ { question: 'Question?', a1: 'a1', a2: 'a2', a3: 'a3', a4: 'a4', a5: 'a5' } ];
// get the answers from the config table for the select's options
$scope.availableAnswers = [
{ answer: $rootScope.configs[0].a1 },
{ answer: $rootScope.configs[0].a2 },
{ answer: $rootScope.configs[0].a3 },
{ answer: $rootScope.configs[0].a4 },
{ answer: $rootScope.configs[0].a5 }
];
appendAnswerObjects();
}, 200);
// function executed on change from the select
$scope.selectedItemChanged = function(profile) {
$log.log(profile);
}
function appendAnswerObjects() {
if($scope.profs && $scope.profs.length && $scope.availableAnswers && $scope.availableAnswers.length) {
$scope.profs.forEach(function(profile) {
profile.answer = $filter('filter')($scope.availableAnswers, { answer: profile.answer })[0];
});
}
}
});
There are several issues to consider. NgOptions likes to use objects rather than values. Also, your 'ng repeat' is calling for profs[0] instead of using the now repeated profiles variable.
Other issue to consider is that your 'answer' would not be 'profile dependent', but will be one for the entire scope, I'm pretty sure that is NOT what you want.
So, the plunkr I created makes individual answers for each. It also takes into account the possibility of getting either the profiles first or the available answers first, no matter which comes first, which is, I believe, Important.
Let me know if this helps.
Best regards,
Rafa.

sharing state between different controllers in angular

I have two controls : Left Side Navigation and the right pane that changes the content on clicking of any item on left navigation.
Here is the html (angular view):
<nav class="navigation">
<ul class="list-unstyled" ng-controller="NavigationController as navigation">
<li ng-repeat="nav in navigation.tabs" class="has-submenu">
{{nav.name}}
<ul class="list-unstyled" ng-show="nav.subNav">
<li ng-repeat="subnav in nav.subNav">{{subnav.name}}</li>
</ul>
</li>
</ul>
</nav>
<section class="content" ng-controller="ContentSwitcher as content">
{{content.tab}}
<div class="warper container-fluid" >
<div class="container-scroll"></div>
</div>
</section>
And here is the controller
(function () {
var app = angular.module('provisioning', []);
app.service('contentService',function(){
var tab = 'Dashboard';
return {
getTab : function(){ return tab; },
setTab : function(value){ tab = value}
}
});
app.controller('NavigationController',['contentService','$log', function(cs,log){
this.tabs = [
{
name: 'Dashboard'
},
{
name: 'Manage',
subNav: [
{
name: 'Account'
},
{
name: 'Facility'
},
{
name: 'Doctors'
},
{
name: 'Patients'
},
{
name: 'Nurses'
},
{
name: 'Device Inventory'
}
]
},
{
name: 'Health Tracker'
},
{
name: 'Reports'
},
{
name: 'Settings'
},
{
name: 'Logout'
}
];
var template = this;
this.changeContent = function(tab){
cs.setTab(tab);
}
}]);
app.controller('ContentSwitcher', ['contentService',function(cs){
this.tab = cs.getTab();
}]);
})();
Also, is it best way to achieve what I intend to do in angularjs? I created a service and shared the variable in the two different controllers. However it doesn't work. The content on right never gets updated on clicking any of the item on left menu.
My answer to a previous question may help. It uses a type of observer pattern.
AngularJs update directive after a call to service method
Your service would change to allow all interested controller or directives to either generate or listen for certain events and access the associated data.
You need to tell your controller that the value of the tab has changed and then update it with the new value from the service. The second controller (ContentSwitcher) can not know, that the value changed and you simply assigned the string value of getTab once to this.tab.
One way to archive this automatically is changing the type of the variable inside your serivce from string to an object (e.g. tab = {name:'Dashboard'}) and then call $scope.$apply after you made changes to it.
In your first controller you still assign this.tab = service.getTab() (which will be an object) and in your view {{tab.name}}.

AngularJS Dynamic Filter Configuration fail to resolve

I'm a newbie to AngularJS with some fair knowledge with KnockoutJS also.
I'm trying to implement a search feature on 'products' in my ViewModel that is configurable by the end user by combining..
Search by 'name' of product
Search by 'tags' of product
in combination with search operations
CONTAINS
STARTS WITH
EQUALS
I believe you understood the functionality I am trying to build up.
The following is the ViewModel I'm using.
var InstantSearchController = function ($scope) {
var self = this;
$scope.filtersAvailable = [
{
displayText: 'Tag',
filterMethod: 'tagFilter',
description: 'Filter by Tags'
},
{
displayText: 'Description',
filterMethod: 'descriptionFilter',
description: 'Filter by description'
}
];
$scope.selectedFilter = $scope.filtersAvailable[1];
$scope.filterBehaviorsAvailable = [
{
displayText: 'CONTAINS',
regexPrefix: '',
regexPostfix: ''
},
{
displayText: 'STARTS WITH',
regexPrefix: '^',
regexPostfix: ''
},
{
displayText: 'EQUALS',
regexPrefix: '^',
regexPostfix: '$'
}
];
$scope.selectedFilterBehavior = $scope.filterBehaviorsAvailable[0];
$scope.products = [
{
name: 'Household Product',
description: 'Description household',
tags: ['personal', 'home']
},
{
name: 'Office product',
description: 'Business equipments',
tags: ['office', 'operations', 'business']
},
{
name: 'Misc products',
description: 'Uncategorized items',
tags: ['noclass']
}
];
}
Now, the following is my filters list.
var app = angular.module('InstantSearchModule', []);
//FILTERS BEGIN
app.filter('descriptionFilter', function () {
var filterFunction = function (data, filterBy) {
if (filterBy == null || filterBy === '')
return data;
var filtered = [];
var regExp = new RegExp(filterBy, 'gi');
angular.forEach(data, function (item) {
if (item.description.match(regExp))
filtered.push(item);
});
return filtered;
};
return filterFunction;
});
app.filter('tagFilter', function () {
var tagFilter = function (data, filterBy) {
if (filterBy == null || filterBy === '')
return data;
var filtered = [];
var regExp = new RegExp('^' + filterBy, 'gi');
debugger;
angular.forEach(data, function (item) {
var isMatching = false;
angular.forEach(item.tags, function (t) {
isMatching = isMatching || (t.match(regExp) != null);
});
if (isMatching)
filtered.push(item);
});
return filtered;
};
return tagFilter;
});
// FILTERS END
I have created a working part to configure search criteria including the 'filterString'(in a textbox), search operand[tags or description](with a select list) and a search mode[starts with / contains / equals](with another select list). Both of the filters are working fine if I specify the filter functions (tagFilter or descriptionFilter) directly in AngularJS directives as follows [JSFiddle Here].
<div data-ng-repeat="p in products|tagFilter:filterString|orderBy:'description.length'">
<h4 style="margin-bottom: 5px">{{$index+1}}. {{p.name}}</h4>
<div>
{{p.description}}
<button data-ng-repeat="t in p.tags|orderBy:'toString()'">{{t}}</button>
</div>
</div>
I was expecting the following to work for me as {{selectedFilter.filterMethod}} is rendering the value successfully, but is showing an error. Please see the HTML I tried to use for it.JSFiddle Here
<div data-ng-repeat="p in products|{{selectedFilter.filterMethod}}:filterString|orderBy:'description.length'">
<h4 style="margin-bottom: 5px">{{$index+1}}. {{p.name}}</h4>
<div>
{{p.description}}
<button data-ng-repeat="t in p.tags|orderBy:'toString()'">{{t}}</button>
</div>
</div>
I have attached the error I'm receiving in Google Chrome developer tools along with the resultant HTML to the subject. Please see below.
As you can see in the HTML, the filter method is not resolved and so, its not working for me. Do you guys have an advice what I am doing wrong?
If I understand it correctly all you need is a way to dynamically change filters. Everything else seems to be working.
I dont think you can use the syntax you are trying to use but you can make a third filter that injects the two others and chooses the right one depending on the parameters you send in.
New filter:
app.filter('multiFilter', function (descriptionFilterFilter, tagFilterFilter) {
var filterFunction = function (data, filterBy, filterRegExp, selectedFilter) {
if(selectedFilter.displayText === 'Description') {
return descriptionFilterFilter(data, filterBy, filterRegExp);
}
else {
return tagFilterFilter(data, filterBy, filterRegExp);
}
};
return filterFunction;
});
As you can see it also takes the filterRegExp and the selectedFilter as parameters. I also changed your old filters to take selectedFilter as a parameter.
Also notice that you have to append "Filter" to the filter name in order to inject it.
You call the new filter like this
multiFilter:filterString:filterRegExp:selectedFilter
So the div could loke something like this
<div data-ng-repeat="p in products|multiFilter:filterString:filterRegExp:selectedFilter|orderBy:'description.length'"
title="{{selectedFilter.filterMethod}}">
<h4 style="margin-bottom: 5px">{{$index+1}}. {{p.name}}</h4>
<div>
I made a working fork of your fiddle
Your fiddle is not working and has other error but, the reason filters are not loading is that you have used global controller function and not registered with your app module for the injection to work. Your filter belong to module InstantSearchModule but you controller does not.
Try the module registration syntax
app.controller('InstantSearchController',function($scope) {
});
see the Angular guide on controller https://code.angularjs.org/1.2.15/docs/guide/controller
Update: As it turns out the issue is not with dependency injection. It is because you cannot use expression to dynamically change filter. When i set to fixed filter it works fine
<div data-ng-repeat="p in products|descriptionFilter:filterString|orderBy:'description.length'"
title="{{selectedFilter.filterMethod}}">
You would have to either combine then or find a way to do select filtering.
See my fix here
http://jsfiddle.net/cmyworld/pW9EZ/1/

I need to filter an array in AngularJS

I need to filter an array that shown below by "all permission value" to send it on the server. It is an example. Thanks for any help.
<!DOCTYPE html>
<html ng-app>
<head>
<script src="./angular.js"></script>
<script>
function MainCtrl($scope) {
$scope.accounts = [{ name: 'KL', company: 'Alpha', permission: 'all'},
{ name: 'Jem', company: 'Altes', permission: 'no' },
{ name: 'Osama', company: 'Solar', permission: 'no' },
{ name: 'Victor', company: 'Osteriks', permission: 'all' }];
// I'd like to get here a filtered array by "all permission" from the View block filter
$scope.filteredAccounts = // [ { name: 'KL', company: 'Alpha', permission: 'all'},
// { name: 'Victor', company: 'Osteriks', permission: 'all' }];
}
</script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="account in accounts | filter: { permission: 'all' }">
<p>{{ account.name }}</p>
</div>
</body>
</html>
I didn't feel like including underscore or lodash in my project just because i wanted the findWhere function so i added it to angular myself:
simply include this outside of angular and change the "angular.element(document)" to the element you booted your app on.
angular.findWhere = function(collection,search) {
var $filter = angular.element(document).injector().get("$filter"),
refined = $filter('filter')(collection,search);
return refined[0];
}
then you just use it like this:
angular.findWhere($scope.accounts,{permission: 'all'});
Most simple way is use lodash and write
$scope.filteredAccounts = _.where($scope.accounts,{permission: 'all'});
You can write custom filters in AngularJS. Here is a link to a document that talks about how to create one: https://docs.angularjs.org/tutorial/step_09
Your custom filter might look something like this:
myApp.filter('permissionFilter', function () {
return function (accounts, permission) {
var filtered = [];
angular.forEach(accounts, function (account) {
if (account.permission === permission) {
filtered.push(account);
}
});
return filtered;
};
});
And the usage would be something like this:
<div ng-controller="MainController">
<div ng-repeat="account in accounts | permissionFilter:'all'">
<p>{{ account.name }}</p>
</div>
</div>
This would allow you to use this filter to filter any kind of permission. In other words, you can change 'all' to 'no' and it will return Jem and Osama. You could take that a step further and bind to the filter variable, so you could let the user decide what they wanted to filter based on.
Here is a Plunker with a working example.

Categories