Angular: ng-repeat (possibly $$hashkey) affecting array lookup - angularjs

Im trying to implement a shopping car solution through angular that will enable a customer to select menus items from a list of items displayed within an <ng-repeat>. These menu items are represented as objects and I store these list of menu item objects in a shopping cart array. Any time a user clicks on the '+' button I have created, an item in included in the cart order array (or increase the order quantity if it already exist), and the view is updated to also include a '-' button to decrement the order. Thus, implicitly, I have a function that checks whether or not the item in the order array in my cart service. All is fine and good until I refresh the page. The cart is instantiated from a cookie. And the cart has the correct items, but the '-' decrement button does not show up next to items that have been selected in the cart. Below is my code.
html partial
<div class="col-xs-4">
<p>
{{category.items[$index].price | currency}}
<a ng-click="menu.addMenuItem(category.items[$index])"> <span class="plus glyphicon glyphicon-plus"></span></a>
<span ng-show="menu.getItemQuantity(category.items[$index])"> <!-- this does not work on refresh-->
{{menu.getItemQuantity(category.items[$index])}}
<a ng-click="menu.subMenuItem(category.items[$index])"> <span class="minus glyphicon glyphicon-minus"></span></a>
</span>
</p>
</div>
relevant controller functions
vm = this;
vm.getItemQuantity = function(item){ //always returns 0 on page refresh
return shoppingCart.getItemQuantity(item);
};
relevant cart service functions
var cart = {};
cart.order=[];
var addItemToOrder = function(item){
var inOrder=isItemInOrder(item);
if(inOrder.check){
cart.order[inOrder.index].quantity+=1;
}else{
var orderItem={
item:item,
quantity:1
};
cart.order.push(orderItem);
}
setCartCookie();
};
//always initially returns check property equal to false on page refresh, although cart is instantiated from cookie
var isItemInOrder = function(item){
for(var i=0; i<cart.order.length; i++){
if(cart.order[i].item==item){
console.log('item in order list');
return {check:true, index:i};
}
}
return {check:false};
};
//more functions
It seems like my issues may be due to this thing $$hashkey that gets added as a property to my menu items, but im not sure what to do. Can someone help resolve this. The application works great till the app is refreshed. The menu item on the page does not display a '-' button.
update
I have taken a picture from chrome tools and it seems like two identical objects that have been separately instantiated are not equating. Is this normal?

Related

ngRepeat doesn't refresh rendered value

I'm having an issue with ngRepeat :
I want to display a list of students in two different ways. In the first one they are filtered by group, and in the second they are not filtered.
The whole display being quite complex, I use a ngInclude with a template to display each student. I can switch between view by changing bClasseVue, each switch being followed by a $scope.$apply().
<div ng-if="currentCours.classesOfGroup !== undefined"
ng-show="bClassesVue">
<div ng-repeat="group in currentCours.classesOfGroup">
<br>
<h2>Classe : [[group.name]]</h2>
<div class="list-view">
<div class="twelve cell"
ng-repeat="eleve in group.eleves | orderBy:'lastName'"
ng-include="'liste_eleves.html'">
</div>
</div>
</div>
</div>
<div class="list-view" ng-show="!bClassesVue">
<div class="twelve cell"
ng-repeat="eleve in currentCours.eleves.all"
ng-include="'liste_eleves.html'">
</div>
</div>
My problem happens when my list of students change (currentCours here). Instead of refreshing the ngRepeat, both lists concatenate, but only in the unfiltered view.
I tried adding some $scope.$apply in strategic places (and I synchronize my list for example) but it doesn't help.
EDIT : the function used to refresh currentCours in the controller. It's called when a "cours" is selected inside a menu.
$scope.selectCours = function (cours) {
$scope.bClassesVue = false;
$scope.currentCours = cours;
$scope.currentCours.eleves.sync().then(() => {
if ($scope.currentCours.classe.type_groupe === 1) {
let _elevesByGroup = _.groupBy($scope.currentCours.eleves.all, function (oEleve) {
return oEleve.className;
});
$scope.currentCours.classesOfGroup = [];
for(let group in _elevesByGroup) {
$scope.currentCours.classesOfGroup.push({
name: group,
eleves: _elevesByGroup[group]
});
}
$scope.bClassesVue = true;
}
});
utils.safeApply($scope);
};
Well, I found a workaround, but I still don't know why it didn't work, so if someone could write an explanation, I would be very thankful.
My solution was simply to open and close the template each time I switch between views.

Web page with two tabs.Tranfer one item to another tab

I need to do app,which doing something like that:
One web page separated on 2 tabs.
In first tab we have list of items. If you click on item it should be transferred to another tab and should be hidden on first tab.
Tools to do this thing is AngularJS.
Any ideas about that? Sorry for this question. I'm noobie in AngularJS.
Here's a really basic example of how you could implement your idea with AngularJS:
Tabs
one easy way to create tabs is to create the markup and use your controller to hold a reference to which tab to show:
// in the controller
$scope.currentTab = 1;
// in the markup
<li class="tab" ng-show="currentTab === 1">
... tab contents
</li>
... repeat for as many tabs as you want
you could then change the tab inside an ng-click directive in your page markup:
// in the markup
<a ng-click="currentTab = 1">change to tab 1</a>
Items
to show items in your tab first create the items in your controller:
// in the controller
$scope.tab1Items = [1, 2, 3, 4, 5];
then display in your tab:
// in the markup
<li class="tab">
<div ng-repeat="item in tab1Items">{{ item }}</div>
...
</li>
Moving the items
This can be done with a function in ng-click that passes the item from one array of items to another:
// in the controller
$scope.moveItem = function(item) {
if ($scope.tab1Items.indexOf(item) > -1) {
$scope.tab1Items.splice($scope.tab1Items.indexOf(item), 1);
$scope.tab2Items.push(item);
}
else {
$scope.tab2Items.splice($scope.tab2Items.indexOf(item), 1);
$scope.tab1Items.push(item);
}
}
// in the markup
<div ng-repeat="item in tab1items" ng-click="moveItem(item)">
...
The concept here is to create a page with one controller that maintains current tab state, as well as the items to display in those tabs. Since you have access to all the items in the same scope it's easy to manipulate and pass those items from one tab to another.
Here's a link to a basic working example in plnkr: http://plnkr.co/edit/PjSXI0JH4uQ1OW8u3f2z?p=preview

connecting angular bootstrap ui paginate to table

I am working on setting up angular pagination on a table that I am creating.
I have worked through the docs. However, I am not seeing how to connect the pagination to the table. My pagination size is matching the array size. Their are the right number of button links to page the page length. However, my table is still at full length making it impossible for me to test my pagination.
Here is my table body
<tbody>
<tr ng-repeat="drink in editableDrinkList | orderBy:'-date'">
<td>[[drink.name]]</td>
<td>[[drink.date |date: format :timezone]]</td>
<td>[[drink.caffeineLevel]]</td>
<td>
<a ng-click="delete(drink._id)">
<i style="font-size:18px; margin-right:5%" class="fa fa-times"></i>
</a>
<a href="" ng-click="update(drink)">
<i style="font-size:22px;" class="fa fa-pencil-square-o"></i>
</a>
</td>
</tr>
</tbody>
I have set my table body outside of the table tag I tried it inside but my pagination controls did not appear.
Here is my pagination
<pagination
total-items="totalItems"
ng-model="currentPage"
items-per-page="itemsPerPage"
ng-change="pageChanged()"
ng-click="setPage(currentPage)">
</pagination>
I also set up a p tag to make sure the tabs are responding
<div>
<p>total Items [[totalItems]]</p>
<p>itemsPerPage [[itemsPerPage]]</p>
<p>current page [[currentPage]]</p>
</div>
The total items, items per page and current page inside of the paragraphare all responding to each click on the pagination controller. However the table is not changing.
Here is my controller
var editabledrinkSet = function(){
editableArray = [];
DrinkLibrary.getAllDrinks().success(function(data){
for (var i = 0; i < data.length; i++) {
if(data[i].editable) {
editableArray.push(data[i])
};
};
$scope.currentPage = 1;
$scope.totalItems = editableArray.length;
$scope.itemsPerPage =10;
$scope.maxSize = 3;
$scope.setPage = function(pageNo) {
$scope.currentPage = pageNo;
}
$scope.pageChanged = function() {
$log.log('Page changed to: ' + $scope.currentPage);
};
$scope.editableDrinkList = editableArray;
});
};
When I get it working I will move most of it out into a directive. I just want to get it set up first.
You can do this with a custom filter.
Add a Custom Filter to ngRepeat
You need to be able to tell the repeat which index is going to be the starting point for each page and how many items to include on the page. You can do this by creating a simple filter (I called it pages). This filter is going to use the currentPage and itemsPerPage values that you already set up in your controller.
<tr ng-repeat="drink in editableDrinkList | orderBy:'-date' | pages: currentPage : itemsPerPage">
Create the Custom Filter
To create a custom filter you pass in your name and then a factory function that returns a worker function. The worker function automatically receives the input array as its first value. The subsequent values are the ones you specified in your filter.
So the pages filter gets the input array and the two values passed to it (currentPage and pageSize). Then you'll just use Array.slice to return the new array to the repeat. Remember, filters are non-destructive. They aren't changing the actual scoped array. They are creating a new array for the repeat to use.
app.filter('pages', function() {
return function(input, currentPage, pageSize) {
//check if there is an array to work with so you don't get an error on the first digest
if(angular.isArray(input)) {
//arrays are 0-base, so subtract 1 from the currentPage value to calculate the slice start
var start = (currentPage-1)*pageSize;
//slice extracts up to, but not including, the element indexed at the end parameter,
//so just multiply the currentPage by the pageSize to get the end parameter
var end = currentPage*pageSize;
return input.slice(start, end);
}
};
});
Add the Pagination Directive to the Page
Here's the cleaned up version of the directive you'll add to your html. Don't use an kind of ngClick or ngChange on the pagination directive. ngModel is set to the currentPage variable so there's nothing else you have to do. Since currentPage is two-way bound via ngModel, when it changes the pagination directive will update as well as the pages filter, which will in turn update your repeated table rows.
<pagination
total-items="totalItems"
ng-model="currentPage"
items-per-page="itemsPerPage"
max-size="maxSize">
</pagination>
It's that simple. If you want to see a working demo, check the Plunker.
Plunker

AngularJS : why after loading more data filter stop working?

there is one filter functionality in my demo I will explain my problem I have one table in which i use infinite scroll is implemented In other words when user moves to bottom it load more data.There is search input field in top .Using this I am able to filter item in table .but I don't know why it is not working
When you search "ubs" and "ing" first time .it works perfectly .But when you load more data other words when user scroll to bottom and load more data the again it try to filter "ubs" and "ing" it not give any result why ?
<label class="item item-input">
<img src="https://dl.dropboxusercontent.com/s/n2s5u9eifp3y2rz/search_icon.png?dl=0">
<input type="text" placeholder="Search" ng-model="query">
</label>
secondly Actually I am implementing infinite scroll so only 100 element display .can we search element from 2000 (which I am getting from service )and display data the search result ?
Update :
Here's a Plunker with everything working together. I have separated all of the pieces into individual JS files, as it was getting unruly:
Plunker
Search
The built in filter will only return results from the current view data that the ng-repeat is displaying. Because you're not loading all of the data into the view at once, you'll have to create your own search functionality.
In the demo, click the search icon to show the search box, then type your search value and press the ENTER key or click the search button to return the results.
Since you want to check whether the user pressed ENTER you have to pass both the event and the querystring to the function, so you can check for the enter keycode. The function should also run when someone clicks or taps the search button. I set ng-model="query" on the input, so query is the reference in the view. Therefore, you'll add ng-click="searchInvoices($event, query)" to your search button, and ng-keyup="searchInvoices($event, query)" to the input. And, finally, to make it easy to clear the input field, add a button that displays when the input is not empty with ng-show="query" and attach a click event with ng-click="query=null; resetGrid()".
Add the searchInvoices function to your controller. It will only run the search if either the query is empty (because you need to reset the view if the person uses the backspace key to empty the input) OR if the user pressed ENTER OR if the event was a click event in case the user clicks the search button. The inner if statement, prevents the search from running if the query is empty and just resets the view. If the query is not empty, against the total dataset and builds an array of matching results, which is used to update the view.
The last line sets the scroll position to the top of the scrollview container. This makes sure that the user sees the results without having to click somewhere in the scrollview container. Make sure you inject the $ionicScrollDelegate into your controller for this to work and set delegate-handle="invoicegrid" on your ion-scroll directive.
$scope.searchInvoices = function(evt, queryval) {
if (queryval.length === 0 || evt.keyCode === 13 || evt.type === 'click') {
if (queryval.length === 0) {
$scope.invoice_records = $scope.total_invoice_records;
} else {
var recordset = $scope.total_invoice_records;
results = [];
var recordsetLength = recordset.length;
var searchVal = queryval.toLowerCase();
var i, j;
for (i = 0; i < recordsetLength; i++) {
var record = recordset[i].columns;
for (j = 0; j < record.length; j++) {
var invoice = record[j].value.toLowerCase();
if (invoice.indexOf(searchVal) >= 0) {
results.push(recordset[i]);
}
}
}
$scope.invoice_records = results;
$ionicScrollDelegate.$getByHandle('invoicegrid').scrollTop();
}
}
};
Lastly, you need to modify the loadMore() function that is used by the infinite scroll directive, so that it doesn't try to load additional data when scrolling through the search results. To do this, you can just pass the query into loadMore on the directive like: on-infinite="loadMore(query)", then in your function, you can just run the broadcast event when the query exists. Also, removing the ngIf will ensure that the list remains dynamic.
$scope.loadMore = function(query) {
if (query || counter >= $scope.total_invoice_records.length) {
$scope.$broadcast('scroll.infiniteScrollComplete');
} else {
$scope.counter = $scope.counter + showitems;
$scope.$broadcast('scroll.infiniteScrollComplete');
}
};
You used filter in wrong way inside ng-repeat like ng-repeat="column in invoice_records | filter:query" instead of ng-repeat="column in invoice_records | query"
<div class="row" ng-repeat="column in invoice_records |filter:query">
<div class="col col-center brd collapse-sm" ng-repeat="field in column.columns" ng-show="data[$index].checked && data[$index].fieldNameOrPath===field.fieldNameOrPath">{{field.value}}</div>
<div class="col col-10 text-center brd collapse-sm"></div>
</div>
Demo Plunkr

ng-repeat doesn't seem to be working

I am trying to pull all items from my array called 'collections'. When I input the call in my html I see only the complete code for the first item in the array on my page. I am trying to only pull in the 'edm.preview' which is the image of the item. I am using Angular Firebase and an api called Europeana. My app allows the user to search and pick which images they like and save them to that specific user.
here is the js:
$scope.users = fbutil.syncArray('users');
$scope.users.currentUser.collections = fbutil.syncArray('collections');
$scope.addSomething = function (something) {
var ref = fbutil.ref('users');
ref.child($rootScope.currentUser.uid).child('collections').push(something);
}
$scope.addSomething = function(item) {
if( newColItem ) {
// push a message to the end of the array
$scope.collections.$add(newColItem)
// display any errors
.catch(alert);
}
};
and the html:
<ul id="collections" ng-repeat="item in collections">
<li ng-repeat="item in collections">{{item.edmPreview}}</li>
</ul>
First, remove the outer ng-repeat. You want to only add the ng-repeat directive to the element which is being repeated, in this case <li>.
Second, from your AngularJS code, it looks like you want to loop over users.currentUser.collections and not just collections:
<ul id="collections">
<li ng-repeat="item in users.currentUser.collections">{{item.edmPreview}}</li>
</ul>
And third, you're defining the function $scope.addSomething twice in your JavaScript code. Right now, the second function definition (which, incidentally, should be changed to update $scope.users.currentUser.collections as well) will completely replace the first.

Resources