Sorting item with checkbox checked at the top of the list in AngularJS - angularjs

Im comparing two object arrays and displaying them with those that have the same object checkbox checked. I now want to sort the list the with checkbox check to be first as a default state.
I also want to sort by clicking the header to sort by ascending order .
<div>header</div>
<div class="search" ng-repeat="items in products ">
<label >{{item.name}}</label>
<input type="checkbox" ng-model="item.checked"
ng-click="checked(item,productlist)"
ng-checked=" checked(item,productlist)">
</div>
I tried using orderBy:[‘-checked’,’item’]: true to sort the checked ones on top but its not working. Any ideas

A few problems here...
You have ng-repeat="items in products", but then you refer to item instead of items within this scope.
You have this checked() method that you're passing to ng-click and ng-checked. What exactly is this doing? You should never be using ng-checked in conjunction with ng-model - https://docs.angularjs.org/api/ng/directive/ngChecked
By passing '-checked' to orderBy, items will be ordered by their checked value in reverse, i.e. they will be ordered false first, true last. Then you're passing true as the reverse parameter to orderBy, so the list will be reversed again. You're reversing the ordering twice unnecessarily - https://docs.angularjs.org/api/ng/filter/orderBy
This might work better...
<div>header</div>
<div class="search" ng-repeat="item in products | orderBy: 'checked'">
<label>{{item.name}}</label>
<input type="checkbox" ng-model="item.checked">
</div>

Related

When ng-init in ng-repeat is replays?

I have a simple ng-repeat to build a HTML list from a javascript array.
Each item can be moved using an input to get the new rank. This input is binded to a variable rank. This variable is initialized using the ng-init directive.
Code looks like this :
<li ng-repeat="item in ctrl.getItems()">
<div ng-init="rank = $index">
[$index: {{$index}}]
{{item}}<br/>
<label>
Move to
<input type="number" ng-model="rank"/>
</label>
<button type="button" ng-click="ctrl.moveItem($index, rank)">
Ok
</button>
</div>
</li>
At runtime, when I change the input value and click to the Ok button, function ctrl.moveItem is called and item is really moved in the ctrl.getItems() array.
So the ng-repeat is replayded and items appears in the new order.
BUT variable rank is not reinitialized and 2 items appears with the same rank.
The sample is here : https://jsfiddle.net/nlips/4ng34b7b/
My question is not so much about moving items in a list, but I need to understand how ng-init works in the context of ng-repeat.
I did not find anything on this subject in the AngularJS official documentation.
From AngularJS docs:
The ngInit directive allows you to evaluate an expression in the current scope.
Now. You are working with different scope.
You are using ngInit into the transcluded scope, overriding $scope.rank each time it repeats that portion of template.
If you want to persist your rank you should init it into the ngRepeat scope.
Try with:
<li ng-repeat="item in ctrl.getItems()" ng-init="rank = $index">
[$index: {{$index}}]
{{item}}<br/>
<label>
Move to
<input type="number" ng-model="rank"/>
</label>
<button type="button" ng-click="ctrl.moveItem($index, rank)">
Ok
</button>
</li>
EDITED ANSWER
Ok, i got it.
The ngInit expression is evaluated only when that part of template is going to be rendered into the DOM.
So, when the page is loaded for the first time your expression is fired and each rank is evaluated correctly.
But, when you make changes on an item that is already rendered, your template is not going to be rendered again, so your ng-init will not be fired.
If you want that ng-init to be executed again you have to remove the item from the DOM and then append it back, into the new position.
There are several alternatives to this approach, but i hope this clarifies what was going on.

Multiple filters for ng-repeat with track by

I have a ng-repeat with filters put as shown below before. For some reason it didnt work in the web server and i think its because of duplicate items not allowed in browser DOM so i had to put "track by item.id". Please see the code below.
<div ng-repeat="item in items | filter:search:date | filter:filterFrontPage track by item.id">
The only issue with above code is that items are not loaded by having "filterFrontPage" filter as well which filters a boolean value from an item. The whole thing works fine when i have it changed to the following:
<div ng-repeat="item in items | filter:search:date track by item.id">
Thus, using the above, how would i add one more filter to filter a boolean value in a variable. I cant seems to get this working by using "filter:search:date:{isActive:true}". Please let me know as to what can be done to get this working.
Update 1:
I have removed "date" as i used it long ago. "search" is for the following and that works fine.
<input class="form-control" type="text" placeholder="Search posts" ng-model="search.$" />
Ideally filterFrontPage is written to filter items by isActive variable true/false;
I think you need to use parentheses around the filtered collection, i.e.:
<div ng-repeat="item in (items | filter:search:date) track by item.id">

Set value of ngmodel using index in ng-repeat

I have this array and iterate the value using ng-repeat. Using ng-repeat I would be able to use $index which represent the iterator.
Before I have this code where the user input the question order number:
<div ng-repeat="q in entries">
<input type="text" ng-model="q.orderNo">
</div>
But the client requested for a draggable feature, to drag drop the questions to be sorted out. eg. the user dragged Question #1 to Question #2 place then their place will change thus the question order number will re-index. With this the user input for order number won't be needed no more but I still have to set the q.orderNo and bind the $index to it when I passed to my api.
<div ng-repeat="q in entries">
<input type="text" ng-model="q.orderNo = $index"> //this is what I want to accomplish
</div>
I want to assign the $index to q.orderNo, how would I do that?
<input type="text" ng-model="q.orderNo" ng-init="q.orderNo = $index">

Ng-model's attribute in a ng-repeat input checkbox gets always literal or give error

So i need to know the extras of a car than a user wants to include in his preferences.
I'm trying to create input checkboxes from an array obtained by an ajax request and generate the inputs by ng-repeat. The major objective is to know the checkboxes selected by the user. I'd like that my approach to be create an auxiliar array which contains the selected ones, but i don't know how to set a unique ng-model to every item in the ng-repeat iteration so i can know the list of selected items. I guess there is something left in my knowlege of angular. Here is what i have for now..
In the controller...
$http.get('/ajax/ajax_get_extras/'+$scope.car.version+'/false').success(function(data) {
$scope.extras = data;
});
$scope.addExtra = function(){ // ... manage the auxiliar array }
In the html ...
<div ng-controller="Controller">
<form novalidate class="simple-form">
<span ng-repeat="extra in extras">
<input type="checkbox" ng-model="extra.id" ng-change="addExtra()" name="extra_{{extra.id}}" >{{extra.name}} - <strong>{{extra.real_price | onlynumber | currency}}</strong>
</span>
</form>
</div>
And i'm stuck since the extra.id doesnt transform to the real extra.id and stays as a string "extra.id" >_<
I tried extra_{{extra.id}}, extra.id, {{extra.id}}, $index as posibles ng-model and none works.
In AngularJS 1.1.5 there is "track by" that you can use in ngRepeat.
So you can:
<input type="checkbox" ng-repeat="e in extra track by $index" ng-model="extra[$index]">
Here is a example: http://plnkr.co/edit/6lNo6R5EPsNGHUU6ufTE?p=preview

Referencing scope children in filters (AngularJS)

I'm an AngularJS newbie, and am putting together a pretty basic proof-of-concept for my boss. It's listings for car hire, with a results list in the main area of the view populated via some external JSON, and a filters panel down the side. You can see the Plunker I've created here:
http://plnkr.co/lNJNYagMC2rszbSOF95k
I've been able to successfully reference child objects/values in my ngRepeat:
<article data-ng-repeat="result in results | filter:search" class="result">
<h3>{{result.carType.name}}, {{result.carDetails.doors}} door, £{{result.price.value}} - {{ result.company.name }}</h3>
<ul class="result-features">
<li>{{result.carDetails.hireDuration}} day hire</li>
<li data-ng-show="result.carDetails.airCon">Air conditioning</li>
<li data-ng-show="result.carDetails.unlimitedMileage">Unlimited Mileage</li>
<li data-ng-show="result.carDetails.theftProtection">Theft Protection</li>
</ul>
</article>
...however, I've so far been unable to access the 2nd level child objects in my search filter. So, for example, where I'm filtering by 'car type' (see below), I'd like to be able to use 'search.carType.name' as my ngModel, to be as specific as possible - but this doesn't work, although just using 'search.carType' works fine. Can anyone advise on what I'm doing wrong?
<h4>Car type:</h4>
Compact <input type="checkbox" data-ng-model="search.carType" ng-true-value="Compact" ng-false-value="" /><br>
Intermediate <input type="checkbox" data-ng-model="search.carType" ng-true-value="Intermediate" ng-false-value="" /><br>
Premium <input type="checkbox" data-ng-model="search.carType" ng-true-value="Premium" ng-false-value="" /><br>
Your search object is being populated correctly, but filter isn't consuming it in the way you expect. Looking at the implementation of filter (https://github.com/angular/angular.js/blob/master/src/ng/filter/filter.js), it appears to only go one layer of child-properties deep when it is given an object as a filter definition.
Ajay's suggestion will work, but you would then need to chain additional filters to accomodate your other parameters. You could change both car type and company to specify x.name in the ng-model and then alter the filter to filter:search.carType.name|filter:search.company.name. If you were only going to have a small number of parameter types, I'd handle it this way.
On the other hand, the nice thing about your current approach is that it's transparent. There's no need to the filter call to be changed if the number of parameters changes elsewhere. If you might have a relatively large number of those, or if they were dynamic, I would take a more scalable approach. Write a filter FUNCTION that consumes the search object, and goes more than one level deep in comparing the children to the filtered data.
Here is a nice post by Anton Kropp on deep object filtering: FILTER ON DEEP OBJECT PROPERTIES IN ANGULARJS
The relevant code:
function initFilters(app){
app.filter('property', property);
}
function property(){
function parseString(input){
return input.split(".");
}
function getValue(element, propertyArray){
var value = element;
_.forEach(propertyArray, function(property){
value = value[property];
});
return value;
}
return function (array, propertyString, target){
var properties = parseString(propertyString);
return _.filter(array, function(item){
return getValue(item, properties) == target;
});
}
}
And the HTML
<ul>
only failed: <input type="checkbox"
ng-model="onlyFailed"
ng-init="onlyFailed=false"/>
<li ng-repeat="entry in data.entries | property:'test.status.pass':!onlyFailed">
<test-entry test="entry.test"></test-entry>
</li>
</ul>
And a JSFiddle here

Resources