$watchGroup in angularjs is not working - angularjs

I am using ng-repeat to show items of objects in array, im using also filter with pagination.
in the below im using $watch to watch Filter.type.car and its work fine , but when i tried to watch Filter.type.car and Filter.type.Motorbike together using $watchGroup as below its now working.
$scope.currentPage = 0;
$scope.pageSize = 10;
$scope.getData = function () {
return $filter('filter')($scope.vichicles);
};
$scope.numberOfPages=function(){
return Math.ceil($scope.vichicles/$scope.pageSize);
};
$scope.$watchGroup(['Filter.type.car','Filter.type.motorbike'], function(term){
$scope.filtered = filterFilter($scope.vichicles, term);
$scope.getData = function () {
return $filter('filter')($scope.filtered)
};
$scope.numberOfPages=function(){
return Math.ceil($scope.getData().length/$scope.pageSize);
};
$scope.totalItems = $scope.filtered.length;
});
and this is the view
<div class="pagination">
<div class="pager">
<span class="current-page">{{currentPage+1}}</span>
<span class="total-pages">{{numberOfPages()}}</span>
</div>
<span class="total-items">{{totalItems}}</span>
<span class="item-description">Vichicles</span>
<button class="btn btn-clear arrow arrow-up" ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button>
<button class="btn btn-clear arrow arrow-down" ng-disabled="currentPage >= getData().length/pageSize - 1" ng-click="currentPage=currentPage+1">Next</button>
</div>
<ul class="list">
<li ng-repeat="v in vichicles | filter: search |searchFilter:Filter.type | searchFilter:Filter.level| startFrom:currentPage*pageSize | limitTo:pageSize">

Use separate Watch instead of watchGroup since those are not array
$scope.$watch('Filter.type.car', function() {
alert('hey, car has changed!');
});
$scope.$watch('Filter.type.motorbike', function() {
alert('hey, bike has changed!');
});

Related

Angular - Filtering data based on bootstrap dropdown selection

I'm trying to filter a list based on a selection from a bootstrap dropdown, but cannot seem to get it to work. Here's my code:
<body>
<div class="toolbar-wrapper" >
<div class="btn-group container" role="group" ng-controller="filterController">
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
{{filter}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li ng-repeat="severity in severityLevels">{{severity}}</li>
</ul>
</div>
</div>
</div>
<div class="logMessages" ng-controller="logController">
<div >
<ul>
<li class="message" ng-repeat="logData in data | filter: filter | limitTo: quantity">
{{logData.timestamp}} : {{logData.severity}} : {{logData.message}}
</li>
</ul>
</div>
</div>
</body>
Javascript:
var app = angular.module('UnifyLog', []);
app.controller('filterController', function($scope) {
$scope.severityLevels = [
'ERROR',
'WARN',
'INFO',
'DEBUG'
];
$scope.filter = '';
$scope.resetFilter = function() {
$scope.filter = '';
};
$scope.changeSeverity = function(severity) {
$scope.filter = severity;
}
})
.controller('logController', function ($scope, $http) {
$http.get("https://clock/settings/get_log_messages", {}).then(
function (response) {
$scope.data = response.data;
},
function (response) {
console.log("fail:" + response);
}
);
$scope.quantity=100
});
I know you can use ng-model data binding with a select directive, but I want to use a bootstrap dropdown.
The code above is updating the scope.filter variable but the list of log messages does not filter.
The http request is getting back a json array with log data objects containing message, timestamp, and severity fields.
Try calling $scope.apply():
$scope.changeSeverity = function(severity) {
$scope.filter = severity;
$scope.apply()
}
Look here for more information, and consider another approach (use real data-binding instead of a custom callback): https://github.com/angular/angular.js/wiki/When-to-use-$scope.$apply()
I would create a customer severity filter like so:
.filter('severityFilter', function () {
return function (input, severity) {
var out = [];
if (input && input != []) {
angular.forEach(input, function (thing) {
if(thing.severity === severity){
out.push(thing);
}
});
}
return out;
}
})
Then apply it like this:
ng-repeat="logData in data | severityFilter: severity | limitTo: quantity">

AngularJS - Convert Object to Array for UI Bootstrap Pagination

I know this might have been already answered, but I have been unsuccessful in applying any solutions I found to my example. I am trying to enabled pagination on my products list page using UI Bootstrap and allow the user to filter said results with a search.
I have an object that is defined as such:
$scope.products = [{"_id": ObjectID("0000000"), "name":"Product", "description": "Product Description"}];
So far I have been able to pull my products from a restangular service, but I am getting the following console error: Error: [filter:notarray] Expected array but received {} when I added the pagination/search functionality. I know I need to convert my object to an array, but I have not been able to successfully convert it. The two examples I tried are posted below. Any help will be greatly appreciated.
My Products list view
<div ng-controller="paginationCtrl" class="row">
<div class="col-lg-12">
<div class="pull-left">
<form>
<div class="input-group product-searchs" ng-controller="SearchCtrl">
<label class="sr-only" for="searchProducts">Search</label>
<span class="input-group-addon search-icon"><i class="fa fa-search" aria-hidden="true"></i></span>
<input type="text" class="form-control" id="searchProducts" ng-model="search.name" placeholder="Search for products">
<span class="input-group-addon clear-icon">
<button type="button" ng-click="clearSearch()">
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
</button>
</span>
</div>
</form>
</div>
</div>
<div class="col-lg-12">
<div class="list-group">
<a ui-sref="productDetails({id:product._id})" class="list-group-item clearfix" ng-repeat="product in filteredProducts = (products | filter: search | startFrom: (currentPage - 1) * itemsPerPage | limitTo: itemsPerPage | orderBy:orderProp)">
<div class="page-header clearfix">
<h2 class="pull-left"><i class="fa fa-diamond"></i> {{product.name}}</h2>
<span class="pull-right product-price {{product.price | currency}}</span>
</div>
{{product.description}}
</a>
</div>
</div>
<div class="col-lg-12">
<pagination class="pull-right" page="currentPage" total-items="totalItems" ng-model="currentPage" max-size="maxSize" ng-change="pageChanged() items-per-page="itemsPerPage" num-pages="numPages"></pagination>
</div>
</div>
I know I might have some extra dependencies injections, which I will remove once I get it working correctly.
My controller
angular.module('gemStoreApp')
.controller('paginationCtrl', ['$scope', '$log', 'filterFilter', 'productsService', 'Restangular', '$filter', function ($scope, $log, filterFilter', productsService, Restangular, $filter) {
$scope.search = {}
$scope.filteredProducts = [];
//Option 1
angular.forEach($scope.products, function(product) {
$scope.filteredProducts.push(product);
});
//Option 2
//for (var key in $scope.products) {
//var tempProducts = {};
//tempProducts[key] = $scope.products[key];
//$scope.filteredProducts.push(tempProducts);
//});
$scope.currentPage = 1;
$scope.maxSize = 100;
$scope.itemsPerPage = 10;
$scope.$watch('search', function(newVal, oldVal) {
$scope.filteredProducts = filterFilter($scope.products, newVal);
$scope.totalItems = $scope.filteredProducts.length;
}, true);
//I also tried this
//$scope.$watch('search, function(newSearch) {
//$scope.filteredProducts = $filter('filter')($scope.products, $scope.search);
// $scope.totalItems = $scope.filteredProducts.length;
//});
}])
My service
angular.module('gemStoreApp.productService',['ngResource'])
.factory('productsService', function(Restangular) {
return Restangular.service('products');
});
My Products Controller
angular.module('gemStoreApp')
.controller('ProductsCtrl', ['$scope', 'productsService', function ($scope, productsService) {
$scope.products = {};
productsService.getList().then(function(products){
$scope.products = products;
});
}]);
Added the Restangular setRestangularFields and setRequestInterceptor methods to my app.js file, which gets the search/filtering function working, but I am still getting an Expected array but received {} error
RestangularProvider.setRestangularFields({
id: '_id.$oid'
});
RestangularProvider.setRequestInterceptor(function(elem, operation) {
if (operation === 'put') {
elem._id = undefined;
return elem;
}
return elem;
});
I have created a Plunker version that appears to be working correctly..
Plunker example.
Created an updated Plunker with the issue I am seeing my local. The issue is that the Pagination is not working correctly. It is displaying only 10 items as I want it to, but clicking on the two does not switch to page 2. Plunker example
I saw several things wrong in your example. I believe you don't need the ng-controller="SearchCtrl" in your html.
Also, the main point of having the filter in your ng repeat is to not use the watch event of the search text input. So you should use the products array and apply the filter against it. If you want to use filteredProducts, I left a function in my example below. I initialized the variables since I don't have access to your restless apis.
var app = angular.module('myApp', []);
app.filter('startFrom', function() {
return function(input, start) {
if(input) {
start = +start; //parse to int
return input.slice(start);
}
return [];
}
});
app.controller('paginationCtrl', ['$scope', '$log', '$filter', function ($scope, $log, $filter) {
$scope.search = {name: "Product"}
$scope.products = [{"_id": "0000000", "name":"Product", "description": "Product Description"},
{"_id": "0000000", "name":"Product 2", "description": "Product Description 2"}];
$scope.filteredProducts = [];
$scope.currentPage = 2;
$scope.maxSize = 100;
$scope.itemsPerPage = 10;
$scope.startFrom = ($scope.currentPage - 1) * $scope.itemsPerPage;
var filterProducts = function(newVal){
$scope.filteredProducts.splice(0, $scope.filteredProducts.length);
angular.forEach($scope.products, function(product) {
if(product.name == newVal){
$scope.filteredProducts.push(product);
}
});
}
$scope.$watch('search.name', function(newVal, oldVal) {
filterProducts(newVal);
$scope.totalItems = $scope.filteredProducts.length;
}, true);
$scope.$watch('currentPage', function(newVal, oldVal) {
$scope.startFrom = ($scope.currentPage - 1) * $scope.itemsPerPage;
}, true);
$scope.$watch('itemsPerPage', function(newVal, oldVal) {
$scope.startFrom = ($scope.currentPage - 1) * $scope.itemsPerPage;
}, true);
}])
<!DOCTYPE html>
<html>
<head>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="paginationCtrl as ctrl" class="row">
<div class="col-lg-12">
<div class="pull-left">
<div class="input-group product-searchs" >
<label class="sr-only" for="searchProducts">Search</label>
<span class="input-group-addon search-icon"><i class="fa fa-search" aria-hidden="true"></i></span>
<input type="text" class="form-control" ng-model="search.name" placeholder="Search for products" />
<span class="input-group-addon clear-icon">
<button type="button" ng-click="clearSearch()">
<i class="glyphicon glyphicon-remove" aria-hidden="true">test</i>
</button>
</span>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="list-group">
<a ui-sref="productDetails({id:product._id})" class="list-group-item clearfix" ng-repeat="product in products | filter: search.name | limitTo: itemsPerPage | startFrom: 0 ">
{{product.name}}
{{product.description}}
<br/>
</a>
</div>
</div>
<div class="col-lg-12">
<pagination class="pull-right" page="currentPage" total-items="totalItems" ng-model="currentPage" max-size="maxSize" ng-change="pageChanged()" items-per-page="itemsPerPage" num-pages="numPages"></pagination>
</div>
</div>
</div>
</body>
</html>
Edit
After watching your plunker, you do not need to add the filteredProducts to your controller since you are doing the filtering in your view. By adding the filteredProducts to your view, it will also be accessible in your controller with $scope.filteredProducts.
So replace the bottom of your html with the code below and also delete all that code that handles the filteredProducts from your controller:
<div class="col-lg-12">
<div class="list-group">
<a ui-sref="productDetails({id:product._id})" class="list-group-item clearfix" ng-repeat="product in filteredProducts = (products | filter: search.name | limitTo: itemsPerPage | startFrom: 0) ">
{{product.name}}<br/>
{{product.description}}
</a>
</div>
</div>
<div class="col-lg-12">
<pagination class="pull-right" page="currentPage" total-items="filteredProducts.length" ng-model="currentPage" max-size="maxSize" ng-change="pageChanged()" items-per-page="itemsPerPage" num-pages="numPages"></pagination>
</div>
If you want to do everything manually (which I do not recommend), you have to change your html to be product in filteredProducts, when it starts add all your products to the filteredProducts list and keep your code in the controller. Also you will have to fill the filteredProducts list again when the search is empty.

Angularfire remove items by checkbox

I am using Angularfire and I'd like to remove the items by checkbox.
And there is a button that can do the "checkAll" and another do the "uncheckAll"
HTML
<div ng-app="app" ng-controller="Ctrl">
<li ng-repeat="item in newslist">
<input type="checkbox"> {{item.newsTitle}}
</li>
<br>
<button ng-click="checkAll()">check all</button>
<button ng-click="uncheckAll()">uncheck all</button>
<button ng-click="newslist.$remove(item)">Remove</button>
</div>
JS
var app = angular.module("app", ["firebase"]);
app.value('newsURL', 'https://XXX.firebaseio.com/XXX/');
app.factory('newsFactory', function($firebase, newsURL) {
return $firebase(new Firebase(newsURL)).$asArray();
});
app.controller('Ctrl', function($scope, $firebase, newsFactory) {
$scope.newslist = newsFactory;
$scope.checkAll = function() {
};
$scope.uncheckAll = function() {
};
});
plunker here
I don't know how to remove the items by checkbox or how to make the "checkAll" button work.
I will be appreciate if someone could tell me your answer.
Here is function you would need to check/uncheck and for removing items:
$scope.checkAll = function() {
$scope.newslist.forEach(function(el) {
el.checked = true;
$scope.newslist.$save(el);
});
};
$scope.uncheckAll = function() {
$scope.newslist.forEach(function(el) {
el.checked = false;
$scope.newslist.$save(el);
});
}
$scope.remove = function() {
$scope.newslist.forEach(function(item) {
if (item.checked) {
$scope.newslist.$remove(item);
}
});
};
Demo: http://plnkr.co/edit/GsGVsGGjNwW4i1kTOuko?p=preview
I added checked property for ngModel directive.
HTML becomes:
<li ng-repeat="item in newslist">
<input type="checkbox" ng-model="item.checked">{{item.newsTitle}}
</li>
<button ng-click="checkAll()">check all</button>
<button ng-click="uncheckAll()">uncheck all</button>
<button ng-click="remove()">Remove</button>

ng-repeat store separate values

I want to store the users vote's inside a cookie, the problem is that inside the ng-repeat I have a value called session.upVoteCount. But it is supposed to be a separate value for each event list item. Is it possible to store each upVoteCount separately and then retrieve them separately again?
<li ng-repeat="session in event.sessions | filter:query | orderBy:sortorder" class="span11">
<div class="row session">
<div class="col-sm-1 well votingWidget">
<div class="votingButton" ng-click="upVoteSession(session)">
<span class="glyphicon glyphicon-arrow-up"></span>
</div>
<div class="badge badge-inverse">
<div>{{session.upVoteCount}}</div>
</div>
<div class="votingButton" ng-click="downVoteSession(session)">
<span class="glyphicon glyphicon-arrow-down"></span>
</div>
</div>
</div>
</li>
and in my controller I have this:
$scope.upVoteSession = function(session) {
session.upVoteCount++;
};
$scope.downVoteSession = function(session) {
session.upVoteCount--;
};
First, I don't recommend to use term 'session', but 'votes'. However, it's your call.
I simplify your problem in this example
http://plnkr.co/edit/l7tQRbuOtEDJetY5eTsf?p=preview
Javascript:
function MyCtrl($scope) {
$scope.votes = {};
$scope.vote = function(key, val) {
$scope.votes[key] = $scope.votes[key] || 0;
$scope.votes[key]+= val;
};
}
Html:
<li ng-repeat="no in [1,2,3,4,5]">
{{no}} : {{votes[no]}} <br/>
upvote
downvote
</li>
Hi guys I solved it myself, I could not get it to work with JSfiddle so I have uploaded the entire thing. Click on server.bat and browser to localhost:8000/eventdetails.html and you will see it working.
https://mega.co.nz/#!1d9yiYiA!zTzdztLAmhVDVYOvvVLINETI2bo_WjxCBteWYm2VUKc
controller:
eventsApp.controller('EventController',
function EventController($scope, $cookieStore, eventData) {
$scope.sortorder = 'name';
var ape = eventData.getEvent();
ape.then(function (banana) {
$scope.event = banana;
angular.forEach(banana.sessions, function (value, key) {
var storecookie = ($cookieStore.get(value.name));
if (typeof storecookie !== "undefined") {
value.upVoteCount = storecookie;
}
});
});
$scope.upVoteSession = function (session) {
session.upVoteCount++;
$cookieStore.put(session.name, session.upVoteCount);
};
$scope.downVoteSession = function (session) {
session.upVoteCount--;
$cookieStore.put(session.name, session.upVoteCount);
};
}
);

AngularJS - Using a custom filter and ngClick to update same ng-repeat

I have a simple ng-repeat. The ng-reapeat can be populated in 2 ways, firstly by typing a value and clicking the Submit button, secondly, entering values and the list automatically updates.
The issue i am finding is that when a user clicks on Sumbit, the ng-repeat that uses the filter does not update.
Heres a plunker (type Help): http://plnkr.co/edit/1qR6CucQdsGqYnVvk70A?p=preview
HTML:
<div id="fixed" directive-when-scrolled="loadMore()">
<ul>
<li ng-repeat="i in items | limitTo: limit | filter: search">{{ i.Title }}</li>
</ul>
</div>
<br>
<input ng-model="searchText">
<button ng-click="performSearch(searchText)">Submit</button>
<div>
<strong>Result Search onClick</strong>
<ul>
<li ng-repeat="item in filtered">
<p>{{ item.Title }}</p>
</li>
</ul>
</div>
JS:
app.controller('MainCtrl', function($scope, $filter) {
$scope.limit = 5;
var counter = 0;
$scope.loadMore = function() {
$scope.limit += 5;
};
$scope.loadMore();
$scope.performSearch = function(searchText) {
$scope.filtered = $filter('filter')($scope.items, $scope.search);
}
$scope.search = function (item){
if (!$scope.searchText)
return true;
if (item.Title.indexOf($scope.searchText)!=-1 || item.Title.indexOf($scope.searchText)!=-1) {
return true;
}
return false;
};
});
app.directive("directiveWhenScrolled", function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.directiveWhenScrolled);
}
});
};
});
Any ideas how i can use a single ng-repeat that bot updates on Click and whilst a user types in values?
It's the model name you pass to filter
it's
<li ng-repeat="i in items | limitTo: limit | filter: search">{{ i.Title }}</li>
should be
<li ng-repeat="i in items | limitTo: limit | filter: searchText">{{ i.Title }}</li>

Resources