AngularJS - Convert Object to Array for UI Bootstrap Pagination - angularjs

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.

Related

angular js :click event for quantity increase and decrease button inside products list build with ng-repeat reflecting each list quantity number

I am having issue with the product list quantity buttons. I am new to angular js and trying to build product list which is having quantity increase and decrease button. I have build the list with ng-repeat, for increase and decrease button i am calling a function qty_incr() and qty_decr() it works but reflecting on every list item. I want to reflect only the same product. The same thing with jquery can done like this
$('.qty_incr').click(function(){
$(this).parents('.cart_product').children('#incr_decr_no').text(parseInt($(this).parents('.cart_product').children('#incr_decr_no').text() + 1));
})
Wondering how could same can achieve with angularjs, here is my code
<p ng-show="loading" class="loading"><img src="assets/img/Spinner-1s-200px.gif" width="50" /></p>
<div class="cart_products" ng-hide="loading" ng-repeat="product in data.products">
<div class="cart_product">
<div class="cart_pro_img"><img src="{{product.product_image}}" /></div>
<div class="cart_pro_detail">
<div class="cart_pro_title">{{product.product_name}}</div>
<div class="cart_pro_price">₹ {{product.price}}</div>
<div class="cart_pro_incr_decr">
<i class="fa fa-minus-circle" ng-click="qty_decr($parent.item)" style="color:#FF4646"></i>
<span id="incr_decr_no" ng-init="$parent.item.quantity=1">{{item.quantity}}</span>
<i class="fa fa-plus-circle" ng-click="qty_incr()" style="color:#68C549"></i>
<i class="fa fa-times-circle-o" {{item.product_id}}></i>
</div>
</div>
</div>
JS
app.controller("cart", function($scope, $http){
var guestid = localStorage.getItem('guestid');
$scope.loading = true;
$http.get(baseurl+"webservice.php?req=cartproducts&guestid="+guestid)
.then(function(response){
$scope.loading = false;
$scope.data = response.data;
//console.log($scope.data);
//$rootScope.cartcount(guestid);
});
$scope.qty_incr = function(item){
$scope.item.quantity = $scope.item.quantity + 1;
}
$scope.qty_decr = function(item){
if($scope.item.quantity > 1){
$scope.item.quantity = $scope.item.quantity - 1;
}
}
})
I have solved it by modifying the html and js code
My HTML
<div class="cart_products" ng-hide="loading" ng-repeat="product in data.products">
<div class="cart_product">
<div class="cart_pro_img"><img src="{{product.product_image}}" /></div>
<div class="cart_pro_detail">
<div class="cart_pro_title">{{product.product_name}}</div>
<div class="cart_pro_price">₹ {{product.price}}</div>
<div class="cart_pro_incr_decr">
<i class="fa fa-minus-circle" ng-click="qty_decr(item)" style="color:#FF4646"></i>
<span id="incr_decr_no" ng-init="item.quantity=1">{{item.quantity}}</span>
<i class="fa fa-plus-circle" ng-click="qty_incr(item)" style="color:#68C549"></i>
<i class="fa fa-times-circle-o" ng-click="removefromcart(product.product_id)"></i>
</div>
</div>
</div>
and JS is:
app.controller("cart", function($scope,$rootScope, $http){
var guestid = localStorage.getItem('guestid');
$http.get(baseurl+"webservice.php?req=cartproducts&guestid="+guestid)
.then(function(response){
$scope.data = response.data;
console.log($scope.data);
})
$scope.qty_incr = function(item){
item.quantity = item.quantity + 1;
}
$scope.qty_decr = function(item){
if(item.quantity > 1){
item.quantity = item.quantity - 1;
}
}
})

How to delete multiple checked items from a list in angular

I have the following string;
$scope.Array="5678,9876,0988"
I am displaying it in my html as follows:
<li ng-repeat="ArrayItem in Array.split(',')">
<input type="checkbox" >
{{Zip}}
</li>
this displays all the string items separately along with a check box. I would like to know how to select multiple items from the list on the UI, and on the press of delete, delete these items from Array.
e.g. check 5678, 9876, and click Delete. The Array would now only have 0988.
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.toDelete = {};
$scope.Array="0988,9876,0988";
$scope.delete = function(){
$scope.temp = $scope.Array.split(',');
for(var prop in $scope.toDelete){
if($scope.toDelete[prop])
$scope.temp[prop] = undefined;
}
$scope.toDelete = {};
$scope.Array = $scope.temp.filter(function(x){ return x !== undefined; }).join(',');
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller="ctrl">
<ul>
<li ng-if='Array' ng-repeat="ArrayItem in Array.split(',') track by $index">
<input type="checkbox" ng-model='toDelete[$index]'>{{ArrayItem}}
</li>
</ul>
Array: {{Array | json}}
<br>
<input type='button' value='Delete' ng-click='delete()'/>
</div>

Angularjs cannot perform crud operations from accordion

I am trying to add a form to a bootstrap accordion and post a text value. Without accordion the form works alright. When i added the form to the accordion i am not able to pass text box value to angularjs controller. Basically i am not able to perform the CRUD operations as i am not able to pass the values. Minimum code related to the issue.
main.html
<div ng-controller="myController">
<accordion class="accordion" close-others="oneAtATime">
<accordion-group ng-repeat="group in groups" is-open="status.isOpen[$index]" >
<accordion-heading>
{{group.groupTitle}} <i class="fa chevron-icon" ng-class="{'fa-chevron-down': status.isOpen[$index], 'fa-chevron-right': !status.isOpen[$index]}"></i>
</accordion-heading>
<div ng-include="group.templateUrl"></div>
</accordion-group>
</accordion>
</div>
controller.js
App.controller('myController', ['$scope', 'Parts', function($scope, Parts) {
var original = $scope.parts;
$scope.submit = function() {
if ($scope.parts.a_id == null) {
$scope.createNewPart();
} else {
$scope.updatePart();
}
};
$scope.createNewPart = function() {
Parts.resource1.create($scope.parts);
};
$scope.updatePart = function() {
Parts.resource2.update($scope.parts)
};
$scope.oneAtATime = true;
$scope.groups = [{
groupTitle: "ADD 1",
templateUrl: "file1.html"
}, {
groupTitle: "ADD 2",
templateUrl: "file2.html"
}];
$scope.status = {
isOpen: new Array($scope.groups.length)
};
for (var i = 0; i < $scope.status.isOpen.length; i++) {
$scope.status.isOpen[i] = (i === 0);
}
} ]);
file1.html
<div>
<form name="myForm" ng-submit="submit()">
<div class="row">
<label class="col-md-2 control-lable" for="part">Add1</label>
<input type="text" ng-model="parts.part" id="part" required />
</div>
<div class="row">
<input type="submit" value="{{!parts.id ? 'Add' : 'Update'}}" ng-disabled="myForm.$invalid">
</div>
</form>
//inside table
<tr ng-repeat="a in availableparts >
<td>{{a.id}}</td>
<td>{{a.apart}}</td>
<td><button type="button" ng-click="editPart(a.id)" >Edit</button>
<button type="button" ng-click="deletePart(a.id)">Remove</button>
</td>
</tr>
</div>
Initially, you need to set $scope.parts to empty object {}, so when you access $scope.parts.id you will have null, instead of error saying id of undefined

Pass variable into ng-repeat filter

I'm new to Angular and I'm trying to pass a variable into the filter but I'm having no luck. It works as intended when I hard code the value but it doesn't seem to be accepting the value.
JS
var app = angular.module('footballApp', []);
app.controller("TeamCtrl", function($scope, $http){
$http.get("clubs.php/teams").success(function (data){
$scope.teams = data.team;
}).error(function (){
alert("an error has occured");
});
$http.get("players.php/players").success(function (data){
$scope.players = data.player;
console.log($scope.players);
}).error(function (){
alert("an error has occured");
});
});
HTML
<script type="text/ng-template" id="team-single.html">
<div class="team-box">
<div class="badge">
<img src="logo.png" width="100" height="100"></div>
<div class="team-name">{{x.club_name}}</div>
<p><b>Club Manager:</b> {{x.club_manager}}</p>
<p><b>Ground:</b> {{x.club_ground}}</p>
<p><b>Nickname:</b> {{x.club_nickname}}</p>
<div class="team-p">{{x.club_info}}</div>
<div class="key-players">
Key Players
</div>
<div class="players">
<ul class="player-list">
<li ng-repeat="player in players | filter: { club_name: '{{player.club_name}}' }" data-toggle="modal" data-target="#{{player.id}}">{{player.player_name}}</li>
</ul>
</div>
</div>
</script>
<div class="row teams" ng-controller="TeamCtrl">
<div class="container">
<div class="col-md-4" ng-repeat="x in teams" ng-include="'team-single.html'"></div>
</div>
</div>
Everything works fine apart from the part where I use ng-repeat with a filter to filter the list that shows by club_name. Can anyone tell me where I'm going wrong?
The variable doesn't need to be in curly braces.
<ul class="player-list">
<li ng-repeat="player in players | filter: { club_name: player.club_name }" data-toggle="modal" data-target="#{{player.id}}">{{player.player_name}}</li>
</ul>
e.g. player.club_name instead of '{{player.club_name}}'

`ng-click` not calling the controller function some times

I have 2 popup's which is controller by a service in a page. on click on the div element i am calling the controller function. after i added the 2nd popup directive especially some times the ng-click not working properly. how to solve this?
here is my service.js
"use strict";
angular.module("tcpApp").service("modalService", function ( $rootScope) {
//header configuration
$rootScope.hideModal = function() {
$rootScope.showModal = false;
};
this.changePass = function ( configId ) {
$rootScope.showModal = true; //this is for global
$rootScope.currentConfigPopView = 'views/tools/'+configId+'.html';
$rootScope.$apply();
};
this.showSummary = function () {
this.showSummaryPop = true;
}
this.hideSummary = function () {
this.showSummaryPop = false; //this is within controller
}
});
html:
<div class="statusBoard board8" ng-click='modal.showSummary("board8")'>
<span><img src="../images/iconDetails.png" alt="icon plan and actual"></span>
<h2>{{boardAssets.board8.title}}</h2>
<span class="row dwo">
<span class="catg">Total Issue Reported</span>
<span class="cd issueData">{{contractor.IssueDetails.TotalIssue}}</span>
</span>
<span class="row mas">
<span class="catg">Issue Resolved</span>
<span class="cd resolveData">{{contractor.IssueDetails.IssueResolved}}</span>
</span>
<span class="row rfi">
<span class="catg">Issue Remaining</span>
<span class="cd remainData">{{contractor.IssueDetails.IssueRemaining}}</span>
</span>
</div>
<body-footer></body-footer>
<config-popup></config-popup> //after i added this directive getting ng-click issue.
<modal-popup></modal-popup>
config popup html:
<div class='ng-modal' ng-if="showModal">
<div class='ng-modal-overlay' ng-click='hideModal()'></div>
<div class='ng-modal-dialog' ng-style='dialogStyle'>
<div class='ng-modal-dialog-content'>
<ng-include src="currentConfigPopView"></ng-include>
</div>
</div>
</div>

Resources