Create a favorite list in AngularJS - javascript

I want to create a favorite list in my application based on user selection. In my app, I use a long JSON file with a bunch of text which is loaded with $http.get().
This is code for displaying content in my view.
<ion-view>
<ion-nav-title></ion-nav-title>
<ion-content>
<div class="card" ng-repeat="list in items | filter: { id: whichid }">
<div class="item item-text-wrap"
ng-repeat="item in list.content | filter: { id2: whichid2 }"
ng-bind-html="item.description.join('')">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
</div>
</div>
</ion-content>
The basic idea for creating a favorite list is to save displayed text in the array. After that, I can easily print that array in a template for the favorite list.
So the problem is how I can save text/data form expression ({{ item.name }}, {{ item.description }}) to the array? Or if anyone has some other idea for making that favorite list.

Pass the item details to a function defined in your controller using ng-click and push it into an array as shown below :
<ion-view>
<ion-nav-title></ion-nav-title>
<ion-content>
<div class="card" ng-repeat="list in items | filter: { id: whichid }">
<div class="item item-text-wrap" ng-click="favouriteThis(item)"
ng-repeat="item in list.content | filter: { id2: whichid2 }"
ng-bind-html="item.description.join('')">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
</div>
</div>
</ion-content>
In your controller :
Write the "favouriteThis" function to push the favourited item every time the user clicks on it :
$scope.favouriteList = [];
$scope.favouriteThis = function (item) {
$scope.favouriteList.push(item); // make sure to check for duplicates before pushing the item, the logic for which i've not included here.
}
As you have all the favourited item details in the "$scope.favouriteList", you can use that information in your favourite list directly. To make it more accurate, while checking for duplicates you can also record the number of times user interacted with a particular item using which you can show the most interacted item on the top of the list.
Hope this helps :)

I would suggest creating a service/controller for this approach since you are making http calls which return JSON objects (use a service, as well as a controller). In the service have your functions such as getFavorites, addToFavorites, deleteFromFavorites etc. These functions will http GET/POST/UPDATE on your favorites list. Then you will want to return the JSON object to a controller. In the controller you'll have control over the scope and set scope variables to display the data in your app.
Here is a basic example:
Service
//****************
//Favorite Factory
//****************
.factory('favoriteFactory', function ($http) {
var favFac = {};
var favorites = [];
favFac.getFavorites = function (userId) {
//$http.get() call to get specific user's favs
};
favFac.addToFavorites = function (name, description) {
//$http.post() call to post to a users favs
};
favFac.deleteFromFavorites = function(userId, itemId) {
//$http.update() call to delete item from users favs
}
return favFac;
});
Controller
//Favorite Controller
.controller('FavoritesCtrl', ['$scope', '$stateParams', 'favoriteFactory', function ($scope, $stateParams, favoriteFactory) {
//Route current user Id to controller. Pass to service to look up their favorites in db
var userId = $stateParams.id;
$scope.favorites = favoriteFactory.getFavorites(userId);
$scope.addToFavorites = function(name, description){
favoriteFactory.addToFavorites(name, description);
}
}])
HTML
<ion-view view-title="Favorites Page" ng-controller="FavoritesCtrl">
<ion-content>
<ion-item collection-repeat="favorite in favorites">
<h3>{{ favorite.name }}</h3>
<p>{{ favorite.description }}</p>
<button type="button" ng-click="addToFavorites(favorite.name, favorite.description)">Add</button>
</ion-item>
</ion-content>

Related

Duplicates in a repeater are not allowed angular.js

I have a view controller with the following code that fetches data from api server:
$scope.recent_news_posts = localStorageService.get('recent_news_posts') || [];
$http({method: 'GET', url: 'http://myapi.com/posts'}).success(function(data) {
if ($scope.$$destroyed) {
return
}
$scope.recent_news_posts = data || [];
localStorageService.set("recent_news_posts", $scope.recent_news_posts);
});
In template:
<md-card class="news-item" ng-repeat="post in recent_news_posts track by post.link" ng-click="openExternalLink(post.link);">
<md-card-title>
<md-card-title-text>
<span class="md-headline">{{ post.title }}</span>
<div class="md-subhead" ng-bind-html="post.content"></div>
</md-card-title-text>
<md-card-title-media>
<img src="{{ post.image }}" class="md-media-lg">
</md-card-title-media>
</md-card-title>
</md-card>
And sometimes I getting reports from sentry like:
[ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track
by' expression to specify unique keys. Repeater: post in
recent_news_posts track by post.link, Duplicate key: function link(),
Duplicate value:
http://errors.angularjs.org/1.5.9/ngRepeat/dupes?p0=post%20in%20recent_news_posts%20track%20by%20post.link&p1=function%20link()&p2=%0A
I read article and added track by, but this didn't help.
I suppose this is maybe because ng-repeat function not finished in time new data from server came. But don't know how to fix this.
Check your data which comes from API. Probably there are some items which have exactly the same post.link inside. If you use track by then the item which is used for tracking needs to be unique!

How to create a firebaseArray with elements inside of a child with unique id?

I have a code structure where I ready the stores that works perfectly. However, I need to list promotions that are in each store and each promotion has a unique id so I would like to know how to list all the promotions that are inside every store follow the code:
in app.js
app.factory('Promos', function($firebaseArray){
var ref = new Firebase('https://myapp.firebaseio.com/');
var refPromoArray = $firebaseArray(ref);
return{
getPromos: function() {
if (refPromoArray) {
return $firebaseArray(ref.child('promotions'));
}
}
})
in controller.js
.controller('PromoListCtrl', function($scope, $state, $stateParams, $ionicLoading, Promos) {
$scope.promoslist = Promos.getPromos();
console.log($scope.promoslist);
})
in promo_list.html
<ion-list ng-if="promoslist.length > 0" can-swipe="true">
<ion-item ng-repeat="promoslist in promo" type="item-text-wrap"
href="#/app/local/{{promo.$id}}">
<img ng-src="{{promo.photo}}" />
{{local.title}}
</ion-item>
</ion-list>
Result
How about something like this:
var ref = firebase.database().ref('/promotions/');
ref.on('value', function(snapshot) {
snapshot.forEach(function(data) {
console.log(data.key + " = " + data.val().yourpoerty);
});
Actually the snapshot is your array. If you want some sort of a normal array only with data, you can create one and add them manually in an array. Good luck.
Hello thank you for the answer your code worked to group the promotions, but now I have another problem, to list I changed the code to create an array with all the promotions as follows:
my code:
var promoslist = [];
promos = ref.child('promotions');
promos.on('value', function(snapshot) {
snapshot.forEach(function(data) {
promoslist.push(data.val());
});
});
**To list via the code below:**
<ion-list ng-if="promoslist.length > 0">
<Ion-item ng-repeat = "promo in promoslist">
  {{Promo}}
</ Ion-item>
</ion-list>
In this case I have 2 stores with 4 promotions but the code is generating an array of two dimensions with the two promotions of each item so ng-repeat only runs twice showing only the results if I change the list to an index ex:
Promoslist [0] or promoslist 1
<Ion-item ng-repeat = "promo in promoslist[0]">
I would like to just create an array of one dimension with all the promotions to list them all at once how do I create a simple array with all?
Follows the image of the result:
Resuult
I found the solution doing a loop-repeat combination in ng-repeat as following code:
<ion-list ng-if="promoslist.length > 0" can-swipe="true" >
<div ng-repeat="promo in promoslist[0]">
<ion-item ng-repeat="promo in promoslist[$index]" type="item-text-wrap" class="item-avatar"
href="#/app/promo/{{promo.idstore}}/{{promo.id}}" ng-if="promo.status==='active'">
<img ng-src="{{promo.image}}"/>
{{promo.title}}
<p>{{promo.store}}</p>
</ion-item>
</div>
</ion-list>

Angular JS ng-repeat element does not update

I have to display a list of products and be able to click on them to get more informations about them.
My list of products is stored in a service named: "productsServices"
I got an html page who display all my products and an other who display details for 1 product. Each html page have their own controller.
When I update a product in my detail page, the changement is done in my productsServices but are not validated in my global product view.
(function ()
{
angular.module('app').service('productsServices', ['$http', '$q', ProductsServices])
that = this
that.products = /*arrayOfData*/;
that.getProducts = function () {
return that.products;
}
})();
(function ()
{
angular.module('app').controller('ProductsController', ['$http', '$q', 'productsServices', ProductsController])
that = this
that.products = productsServices.getProducts;
that.showDetail = function (item) {
$state.go('menu.product', { myParam: item });
}
})();
<!--Global HTML page-->
<ion-content id="content" class="has-header has-subheader" delegate-handle="myScroll">
<ion-list>
<ion-item ng-repeat="product in that.products() track by product.ProductId" href="#" ng-click="that.showDetail({{product}})">
<div class="innerProd">
<img id="productPicture" name="productPicture" ng-src='data:image/png;base64,{{product.ThumbnailPhoto}}'></div>
<div class= "innerProd">
<p>{{product.Name}}</p>
<p>{{product.Price}}$</p>
<ion-option-button class="button button-assertive"
ng-click="doDelete({{product}})">
Supprimer
</ion-option-button>
</div>
</ion-item>
</ion-list>
<ion-infinite-scroll ng-if="moreDataCanBeLoaded()" icon="ion-loading-c" on-infinite="loadMoreData()" distance="100%">
</ion-infinite-scroll>
</ion-content>
So when I check the product sended in the "that.showDetail" method, it's the old product who is sended even if the product have been updated in the services.
I'm not english so sorry for language mistakes in my sentence ^^'
Thank in advance for your help ^^'
Ok I've find the ansewer to my question, I've replace the parameter that I'm sending to my "that.showDetail" function. I'm no more sending the object directly, but the index of my item.
<ion-item ng-repeat="product in that.products() track by product.ProductId" href="#" ng-click="that.showDetail({{$index}})">
I guess that angular doesn't update injected in a ng-click call ^^'
Thank you for your time guys

adding array elements to another array

I have a very big list which is an array named leagues, I need to allow the user to take the elements on that array(list) , and choose those as favorites by clicking a button
$scope.favoriteLeagues = [];
$scope.favoriteLeague = function(league) {
$scope.favoriteLeagues.push(league);
}
so I want to know what am I doing wrong ? the function sometimes allows me to add one as favorite, but once I click on the second one, I got a message of something undefined, and also, the binding is not working, I am unable to see the {{favoriteLeagues.name}} printed.
UPDATED AS REQUESTED
<div>
<strong>Favorites</strong>
{{favoriteLeagues.name}}
</div>
<ion-option-button class="button-light icon ion-star"
on-tap="favoriteLeague(league)">
</ion-option-button>
<div ng-repeat="sport in sportsFilter = (sports | filter:query)">
<strong>{{sport.name}}</strong>
</div>
<ion-item ng-repeat="league in sport.leagues">
<div>{{league.name}}</div>
</ion-item>
</ion-list>
here the controller:
.controller('SportsController', function($scope, $state,
AuthFactory, SportsFactory) {
$scope.favoriteLeagues = [];
$scope.sports = [];
AuthFactory.getCustomer().then(function(customer) {
$scope.customer = customer;
SportsFactory.getSportsWithLeagues(customer).then(function(sports) {
if (sports.length) {
$scope.sports = sports;
}
$scope.isSportShown = function(sport) {
return $scope.shownSport === sport;
};
$scope.favoriteLeague = function(league) {
$scope.favoriteLeagues.push(league);
}
};
});
You haven't pasted the full html, but it should look something like this:
<!-- Use ng-app to auto-bootstrap an AngularJS application-->
<!-- Use ng-controller to attach your view with your SportsController controller -->
<ion-list>
<div>
<strong>Favorites</strong>
<!-- Looping through all the favourite leagues-->
<div ng-repeat="favouriteL in favoriteLeagues">
{{favouriteL.name}}
</div>
</div>
<!-- Looping through all the sports -->
<div ng-repeat="sport in sportsFilter = (sports | filter:query)">
<!-- Bind the sport name -->
<strong>{{sport.name}}</strong>
<!-- Looping through all the leagues -->
<ion-item ng-repeat="league in sport.leagues">
<!-- Display a button which on tap will call favoriteLeague function -->
<ion-option-button class="button-light icon ion-star" on-tap="favoriteLeague(league)">
</ion-option-button>
<!-- Bind the name of the league -->
<div>{{league.name}}</div>
</ion-item>
</div>
</ion-list>
Don't forget to attach the view with your controller using ng-controller.
I can't help you much with angular.js, I've never used it, but the fact that you are accidentally replacing the array with the function probably doesn't help. ng-repeat is trying to loop through favoriteLeagues but fails because that's a function! Look at the comments I put in your code.
$scope.favoriteLeague = []; // creates an array
$scope.favoriteLeague = function(league) { // replaces the array with a function!!!
$scope.favoriteLeagues.push(league); // suddenly leagues takes an S ?
}
To avoid this type of error, you should respect a naming convention for your functions. I like to use action words and verbs for functions. I only use plural forms on arrays and related functions. Here's what I'd do in your case:
$scope.favoriteLeagues = [];
$scope.addToFavoriteLeagues = function(league) {
$scope.favoriteLeagues.push(league);
}
You need to attach your controller to the html in order for the bind to work, usually at the top level parent element, e.g a div, containing the ngrepeat markup.

How can I take one large object, use a sub-object of that as the data for multiple instances of the same controller, and keep them both in sync?

Apologies for the ambiguity of the question :-P
I have a single JavaScript object which contains all my data. And I have a controller which I will use multiple times throughout the application. So the controllers are all working on the same data, the data is added to the application using a service.
To support a read-only/edit mode interaction, I make two copies of the original data source in the service. When the user manipulates data, they are manipulating the edit mode data source. They can then press a button to save the data to the edit mode data source to the read-only mode data source (using angular.copy).
I would also like to have the instances of the controller work on just part of the data source rather than the whole thing.
The behavior I am seeing is angularjs is able to update the parts, keeping them both in sync; but when I press the button to perform the angular.copy, it seems to reassign the variable rather than adjust the value of where it was pointing.
Code below and here's a jsfiddle http://jsfiddle.net/q5ca5quq/1/
<html ng-app='app'>
<body>
<div ng-controller='a_controller as ctrl_1'>
read_mode_inner = {{ ctrl_1.read_mode_inner }}<br>
edit_mode_inner = {{ ctrl_1.edit_mode_inner }}<br>
<br>
<input ng-model='ctrl_1.edit_mode_inner[0].a'>
</div>
<br><br><br>
<div ng-controller='a_controller as ctrl_2'>
read_mode_inner = {{ ctrl_2.read_mode_inner }}<br>
edit_mode_inner = {{ ctrl_2.edit_mode_inner }}<br>
<br>
Change this and press button below <input ng-model='ctrl_2.edit_mode_inner[0].a'> <br>
<button ng-click='ctrl_2.change()'>Copy edit_mode_inner into read_mode_inner</button>
</div>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js'></script>
<script type='text/javascript'>
angular.module('app',[])
.factory('DataService', [function() {
data = {
xx : [{'a':1}, {'b':2}, {'c':3}],
yy : [{'a':1}, {'b':2}, {'c':3}]
}
return {
read_mode : data,
edit_mode : angular.copy(data)
}
}])
.controller('a_controller', ['DataService', function(DataService) {
var self = this;
window.s = self; // For debugging
self.read_mode = DataService.read_mode;
self.edit_mode = DataService.edit_mode;
self.read_mode_inner = self.read_mode.xx;
self.edit_mode_inner = self.edit_mode.xx;
self.change = function(){
self.read_mode_inner = angular.copy(self.edit_mode_inner);
}
}]);
</script>
</body>
</html>
http://jsfiddle.net/q5ca5quq/2/
You can share data between controllers using a service, but if you want all controller instances to update every time the value in service is changed, you'll need something like $watch inside your controller:
$scope.change = function(){
DataService.read_mode.xx = angular.copy($scope.edit_mode_inner);
}
$scope.$watch(function(){return DataService}, function(dataService){
$scope.read_mode_inner = dataService.read_mode.xx;
}, true);
For this to work you need to use angular $scope instead of this/self reference. Notice how using $scope also simplified angular notation in HTML. You don't need to name controllers separately:
instead of:
<div ng-controller='a_controller as ctrl_1'>
read_mode_inner = {{ ctrl_1.read_mode_inner }}<br>
edit_mode_inner = {{ ctrl_1.edit_mode_inner }}<br>
</div>
You only have to:
<div ng-controller='a_controller'>
read_mode_inner = {{ read_mode_inner }}<br>
edit_mode_inner = {{ edit_mode_inner }}<br>
</div>
because $scope takes care of the rest.

Categories

Resources