Angularjs - Pagination appear after search filter - angularjs

I am newbie on AngularJS.
Search result can be displayed in one page but why the next and previous button is showing ?
http://jsfiddle.net/2ZzZB/1473/
<input type="text" id="txtNotessearch" ng-model="search_notes" class="form-control input-sm" placeholder="SEARCH">
...
<ul>
<li ng-repeat="item in data | filter:search_notes | startFrom:currentPage*pageSize | limitTo:pageSize">
{{item}}
</li>
</ul>
Correct me, if I am wrong.

Because NumberOfPages is not filtered.
Instead of using $scope.data.length, you should use the length after the filter.
function MyCtrl($scope, $filter) { //Do not forget to inject $filter
$scope.currentPage = 0;
$scope.pageSize = 10;
$scope.data = [];
$scope.numberOfPages=function(){
var myFilteredData = $filter('filter')($scope.data,$scope.search_notes); //Filter the data
return Math.ceil(myFilteredData.length/$scope.pageSize);
}
for (var i=0; i<45; i++) {
$scope.data.push("Item "+i);
}
}
In addition, I would modify the ng-disable next button to
button "ng-disabled="(currentPage + 1) == numberOfPages()"
And I would add to search_notes onchange currentPage=1.
Here you have the fiddle
http://jsfiddle.net/rLots5zd/

In case, you want to hide the next and previous button when the result can be displayed in one page, please see the fiddle here: http://jsfiddle.net/rLots5zd/3/

Related

How to set a default value for AngularJS ng-repeat?

<li ng-repeat="item in quantityList track by $index" ng-model="isPageValid='true'">
<input value="item.quantity" ng-blur="validateQuantity(item)" ng-model="item.quantity">
</li>
I'm trying to set a default value for a status variable each time an ng-repeat loop occurs. I've tried ng-model, but that doesn't work. For instance,
I'd like to set isPageValid="true" before each time the ng-repeat loop runs. 'True' is will be the default value, and the validation function will test whether isPageValid should be set to 'false'.
I'd like the ng-repeat loop to run each time the ng-blur is exercised.
NOTE: I understand the way I'm using ng-model is incorrect, but this is just to illustrate the issue.
HTML:
<li ng-repeat="item in quantityList track by $index" ng-model="isPageValid='true'">
<input value="item.quantity" ng-blur="validateQuantity(item)" ng-model="item.quantity">
</li>
JS:
scope.validateQuantity = function(item){
var qty = item.quantity;
if(parseInt(qty) >=1 && parseInt(qty) <= 200){
item.isQuantityValid = true;
}else{
item.isQuantityValid = false;
scope.isPageValid = false;
}
}
The loop creates a list of input boxes. The objective is to create a global validation value called isPageValid which is 'false' if the validation by the JS fails for any input box. Note, when ng-blur is exercised, the JS validation runs and loop should re-run.
I believe ng-init could help...
<ul ng-init="isPageValid='true'">
<li ng-repeat="item in quantityList track by $index" >
<input ng-blur="validateQuantity(item)" ng-model="item.quantity">
</li>
</ul>
Note that it would be better practice to initialise isPageValid in the controller that is in the same scope as your validateQuantity function.
Here is an example of how you could initialise isPageValid in your controller and update the value after each call to validateQuantity...
In your controller:
scope.isPageValid = true;
function updatePageValid() {
scope.isPageValid = scope.quantityList.every(item => item.isQuantityValid);
}
scope.validateQuantity = function(item) {
var qty = parseInt(item.quantity);
item.isQuantityValid = (qty >= 1 && qty <= 200);
updatePageValid();
});

Rendering issue using Ng-repeat, track by and limitto together

In my project, i am using ng-repeat with limitto filter and track by $index
<button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button>
<button ng-disabled="currentPage >= cil.cilChemicals.length/pageSize - 1" ng-click="currentPage=currentPage+1">Next</button>
<div ng-repeat="chemical in Chemicals | startFrom: currentPage * pageSize | limitTo:pageSize track by $index">
<span ng-init="initDetails(chemical)"> {{chemical.details}} </span>
</div>
Controller code :
$scope.currentPage = 0;
$scope.pageSize = 50;
Module.filter('startFrom', function() {
return function(input, start) {
start = +start;
return input.slice(start);
}
});
With this setup, initDetails(chemical) is called for all chemicals during first time ng-repeat rendering thus not called for each page which is rendered perfectly for the first time but when we repeat the page then ng-repeat starts re rendering from in between the array.
Please suggest why it starts from in between and when we use trackby chemical.chemicalIdthen angular js calls initDetails(chemical)for each page.
The way you have ng-init, runs in each iteration.
Except (enters track by):
when you use trackby $index the first element has always the $index 0 regardless of page number,
so angular doesn't re-render the html
but when you change the trackby to chemical.chemicalIdthen angular re-renders the html in evey iteration.
Actually angular will re-render only the items that have diff in the track by expression, either it is an index or id, than the previous items.
Also I agree with #ste2425's comment. that is not the intended use of ng-init
just a note:
LimitTo filter accepts 2 parameters, limit and begin,
limit is the page size, and begin is the index to start counting, so there is no need to create a custom filter for that.
You can essentially do this:
<button ng-disabled="currentPage == 0" ng-click="previousPage()">Previous</button>
<button ng-disabled="currentPage >= cil.cilChemicals.length/pageSize - 1" ng-click="nextPage()">Next</button>
<div ng-repeat="chemical in Chemicals | limitTo:pageSize:startFrom track by $index">
<span ng-init="initDetails(chemical)"> {{chemical.details}} </span>
</div>
And your controller
$scope.currentPage = 0;
$scope.pageSize = 50;
scope.startFrom = 0
$scope.nextPage = function() {
$scope.currentPage++;
$scope.startFrom = $scope.currentPage * $scope.pageSize;
};
$scope.previousPage = function() {
$scope.currentPage--;
$scope.startFrom = $scope.currentPage * $scope.pageSize;
};

dynamically change options in dropdown list in angular js

i tried to create dynamically changing dropdown list in angularJS
angulars.js
var option1Options = ["Class","Category","Option","Question","Group"];
var option2Options = [["Group","ProductModel"],
["Class","ProductModel"],
["Class","Group","ProductModel"],
["Group","ProductModel"],
["ProductModel"]];
$scope.myCtrl= function()
{
$scope.options1 = option1Options;
$scope.options2 = [];
$scope.getOptions2 = function(){
var key = $scope.options1.indexOf($scope.child);
$scope.options2 = option2Options[2];
};
}
page.html
<div id="CreateChild" ng-controller="myCtrl">
<select ng-model="child" ng-options="option for option in options1" ng-change="getOptions2()">
</select>
<select ng-model="parent" ng-options="option for option in options2">
</select>
</div>
in angulars.js i was unable to get the index of first dropdown list array. the value of key is assigned as -1 and the option2 is assigned as undefined.
can any one help me with this
I did a small workaround for this requirement, though it is not a straight answer, I believe this would help you...
Add this to your controller...
$scope.getOptions1Idx = function(){
var mySelectedOption = $scope.child;
var i = 0;
for(i=0;i< option1Options.length;i++){
if(option1Options[i]==mySelectedOption){
break;
}
}
return i;
}
and change your getOptions2 function as follows
$scope.getOptions2 = function(){
$scope.options2 = option2Options[getOptions1Idx()];
};
This can be done in much better fashion by avoiding for loop provided if you choose to change your array structure with predefined index some thing like var option1Options = [{id:0,option:"Class"},{id:1,option:"Category"},{id:2,option:"Option"},{id:3,option:"Question","Group"}];
Had a very similar problem with this. In terms of styling I found my way around it by creating a list instead of a select option
<div class='btn-group'>
<button class='form-control col-md-3' data-toggle='dropdown'>
{{value}} <span class='caret'></span>
</button>
<ul class='dropdown-menu'>
<li ng-repeat='type in callType' class='col-md-3'>
<a href='#' ng-click='select(type)'>{{type.name}}</a>
</li>
</ul>
</div>
Then the controller is used to take in the objects, call a method to change each object and then set a default for the drop down list. You can see it at the link below.
http://plnkr.co/edit/nwXmMif8vjj92pQmalb2

Binding Input to Angular controller function

I have a shopping cart that shows the name of the flavor and quantity, then the total $ of each flavor. Then at the bottom of all the flavor, I have the calculation of the entire shopping cart.
HTML:
<div ng-repeat="flavor in cart track by $index">
<div>
<span class="flavor-name no-margin">{{flavor.name}} x <input type="text" name="{{flavor.name}}" size="1" ng-model="flavor.quantity"></span>
<span class="flavor-price no-margin">{{flavor.price * flavor.quantity | currency }}</span>
<div class="clearfix"></div>
</div>
</div>
<div ng-if="cart.length > 0">
<span class="cart-total-text no-margin">Total:</span>
<span class="cart-total no-margin">{{ total | currency}}</span>
<div class='clearfix'></div>
</div>
In my Controller:
function total() {
var total = 0;
for (var i = 0; i < $scope.cart.length; i++) {
var add = $scope.cart[i].price * $scope.cart[i].quantity;
total = total + add;
}
return total;
}
$scope.$watchCollection("cart", function() {
$scope.total = total();
});
And my cart looks like this:
{
'name': 'Strawberry',
'price': 4.5,
'quantity': 0,
},
{
'name': 'Mint',
'price': 3.5,
'quantity': 0,
}, etc
Right now, the total is working for when I add new things to the cart, but how do I make angular "listen" to when the quantity of the flavor is changed in my input box? Thanks!
You should be able to put the total() function on the scope and bind it to the span:
In the HTML:
<span class="cart-total no-margin">{{total() | currency}}</span>
In the controller:
$scope.total = function() {
var total = 0;
for (var i = 0; i < $scope.cart.length; i++) {
var add = $scope.cart[i].price * $scope.cart[i].quantity;
total = total + add;
}
return total;
}
Whenever something changes (like the quantity), Angular should trigger a digest cycle and reevaluate all the bound expressions including the total() method.
IMO that's the easiest way. If you really want to go with the $watch route (or understand why your current code is not working) you have to understand how Angular "watches" objects.
$scope.$watch by defaults only fires when the reference of the object changes.
$scope.$watchCollection goes a little further and fires when elements are added or removed from a collection (even though the collection reference stays the same).
In your case, changing the quantity of a flavor does not add or remove an item in the collection, which is why your code isn't triggered.
What you really want is a "deep watch" of the cart object. That can be achieved by passing a 3rd argument to the $watch method (a boolean called objectEquality):
$scope.$watch("cart", function() {
$scope.total = total();
}, true); // Tells Angular to test object equality.
This will make Angular look for change in the properties of the watched object (not just the reference), which should fire when the flavor.quantity changes.
You can watch a function if it is in the scope.
$scope.$watch('total()', function(){
});

AngularJs delete record on nested grouped rows

I'm trying to build a simple page to group record and then add a button to eliminate some records.
The problem is that the record eliminated that has the same name is deleted from the wrong grouped list. And also if a list have no grouped records should disappear, and instead is always there.
Fiddle: http://jsfiddle.net/Tropicalista/qyb6N/15/
// create a deferred object to be resolved later
var teamsDeferred = $q.defer();
// return a promise. The promise says, "I promise that I'll give you your
// data as soon as I have it (which is when I am resolved)".
$scope.teams = teamsDeferred.promise;
// create a list of unique teams
var uniqueTeams = unique($scope.players, 'team');
// resolve the deferred object with the unique teams
// this will trigger an update on the view
teamsDeferred.resolve(uniqueTeams);
// function that takes an array of objects
// and returns an array of unique valued in the object
// array for a given key.
// this really belongs in a service, not the global window scope
function unique(data, key) {
var result = [];
for (var i = 0; i < data.length; i++) {
var value = data[i][key];
if (result.indexOf(value) == -1) {
result.push(value);
}
}
console.log(result)
console.log(Math.ceil(result.length / 10))
$scope.noOfPages = Math.ceil(result.length / 10);
return result;
}
$scope.currentPage = 1;
$scope.pageSize = 5;
$scope.maxSize = 2;
$scope.deleteItem = function(item){
//code to delete here
var index=$scope.players.indexOf(item)
$scope.players.splice(index,1);
};
Here is a sample of something expanding on the tip from SpykeBytes
<div ng-repeat="location in journey.locations">
<div id="location_div_{{ $index }}">
<label class="journey-label">Location name</label>
<input class="journey-input" id="location_{{ $index }}" type="text" ng-model="location.location_name" />
<button ng-show="editable" tabindex="-1" class="journey-button remove" ng-click="removeItem(journey.locations, $index)">
Remove location
</button>
Then in my controller I set up an action that takes deletes the individual item
$scope.removeItem = function(itemArray, index) {
return itemArray.splice(index, 1);
};
To hide the group when nothing is listed, you need to get the filtered list and then use ng-show to drive the display. This is a bit tricky:
<div ng-show="currentList.length>0" ng-repeat="team in teams| startFrom:(currentPage - 1)*pageSize | limitTo:pageSize | filter:searchInput"> <b>{{team}}</b>
<li ng-repeat="player in (currentList = (players | filter: {team: team}))">{{player.name}}
<button class="btn btn-small" type="button" ng-click="deleteItem(player)">Delete</button>
</li>
</div>
However I am not seeing the problem you said about removing from wrong group. Can you let me know how to reproduce it?
Index won't help you here because the {{$index}} that ng-repeat provides is within the groupings. That is, each grouping restarts the $index variable. You are going to need a unique identifier for each record though. Without that there is no way to be sure that the record you want to remove is the right one.
As far as the groupings, you can recreate the model whenever you delete something. This wouldn't work with the sample data in the Fiddle, but it works when you're dealing with a real datasource.
You can instead pass the index of the object if it is within ng-repeat.

Resources