AngularJS, ng-repeat, checkboxes and toggleAll - angularjs

There's no question. Searching over the internet, you can easily find examples with how ng-repeat and checkboxes work. All these examples include only few checkboxes. But, have you tried to create several hundred checkboxes, then use some toggle button to check/uncheck all checkboxes? The app becomes totally unresponsive. In browser it is kind of okay, but testing the app on device (iPad4, iPad mini etc), the app gets totally unresponsive.
I've created a Plunker example here: http://plnkr.co/edit/wfa3TIp3BYaPvzX8ehAf?p=preview
Try to test toggle checkbox with at least 500 entries so you could see the delay. The question now is, is there any way to improve the performance? Checking recording of Timeline, this is the result I'm getting for 500 entries (:
19.131 ms Scripting
150.104 ms Rendering
55.543 ms Painting
138.402 ms Other
2.95 s Idle
As you can see, rendering takes the precious time and we cannot afford that kind of time to be lost.
HTML:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width" />
<title>Ionic Framework Example</title>
<link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet"/>
<link href="index.css" rel="stylesheet"/>
<script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
<script src="script.js"></script>
</head>
<body>
<ion-view view-title="main module">
<ion-content ng-controller="StartCtrl">
<div class="list">
<div class="item item-divider">
Options
</div>
<a class="item item-icon-left" href="#" ng-click="generateEntries()">
<i class="icon ion-plus-circled"></i>
Add more entries
</a>
<a class="item item-icon-left" href="#" ng-click="clearEntries()">
<i class="icon ion-trash-b"></i>
Clear entries
</a>
<div class="item item-icon-left" href="#">
<i class="icon ion-person-stalker"></i>
Entries in array
<span class="badge badge-assertive">{{entries.length}}</span>
</div>
<li class="item item-checkbox">
<label class="checkbox">
<input type="checkbox" ng-model="checkedAll" ng-click="toggleAll()">
</label>
Toggle all
</li>
<div class="item item-divider">
Entries
</div>
<li class="item item-checkbox" ng-repeat="entry in entries track by $index">
<label class="checkbox">
<input type="checkbox" ng-model="entry.checked" name="entry.id">
</label>
{{entry.id}} - {{entry.name}}
<span class="badge badge-light">{{$index}}</span>
</li>
</div>
</ion-content>
</ion-view>
</body>
</html>
JS:
// Code goes here
var app = angular.module('myApp', []);
app.controller('StartCtrl', function ($scope) {
// bind data from service
// this.someData = Start.someData;
$scope.entries = [];
$scope.generateEntries = function () {
var names = ['Mark', 'John', 'Maria', 'Lea', 'Marco'];
var obj = {};
for (var i = 0; i < 50; i++) {
obj = {'id': Math.floor(Math.random() * 10000), 'name': names[Math.floor(Math.random() * names.length)]};
$scope.entries.push(obj);
}
};
$scope.clearEntries = function () {
$scope.entries = [];
};
$scope.toggleAll = function () {
for (var i = 0; i < $scope.entries.length; i++) {
$scope.entries[i].checked = $scope.checkedAll;
}
};
});
Thanks to everyone that will participate in this discussion.

Use entry in entries track by entry.id instead of $index. Having 500 entries this improved the stats alot for me:
Using track by $index
65.660 ms Scripting
246.985 ms Rendering
129.748 ms Painting
1.23 s Other
3.31 s Idle
Using track by entry.id
46.534 ms Scripting
30.827 ms Rendering
17.631 ms Painting
226.515 ms Other
3.18 s Idle

Both replies, from #Numyx and #jaycp were very helpful. I've made a few improvements to the code:
1. I'm using track by entry.id instead of track by $index which speeded up the rendering as #Numyx said,
2. I've used ion-infinite-scroll so I don't load all results (5000 for example) at once, but only approx. 25. Scrolling to bottom loads more.
3. I'm using 2 datasets. 1 for ALL entries and 1 for view entries. View Entries array is filled when we scroll. The more we scroll, the more entries from ALL array are added to view array,
4. I'm using $timeout to show $ionicLoading and hiding $ionicLoading when rendering is finished,
So, when using toggle button, now we aren't making all entries as checked but only those, which are in visible array.
Here is the updated code:
start.html
<ion-view view-title="main module">
<ion-content>
<div class="list">
<div class="item item-divider">
Options
</div>
<a class="item item-icon-left" href="#" ng-click="start.generateEntries()">
<i class="icon ion-plus-circled"></i>
Add more entries
</a>
<a class="item item-icon-left" href="#" ng-click="start.clearEntries()">
<i class="icon ion-trash-b"></i>
Clear entries
</a>
<div class="item item-icon-left" href="#">
<i class="icon ion-person-stalker"></i>
Entries in array
<span class="badge badge-assertive">{{start.entries.length}}</span>
</div>
<li class="item item-checkbox">
<label class="checkbox">
<input type="checkbox" ng-model="start.checkedAll" ng-click="start.toggleAll()">
</label>
Toggle all
</li>
<div class="item item-divider">
Entries {{start.entriesView.length}}
</div>
<li class="item item-checkbox" ng-repeat="entry in start.entriesView track by entry.id">
<label class="checkbox">
<input type="checkbox" ng-model="entry.checked" name="entry.id">
</label>
{{entry.id}} - {{entry.name}}
<span class="badge badge-light">{{$index}}</span>
</li>
<ion-infinite-scroll
on-infinite="start.loadMore()"
ng-if="start.canLoadMore()"
immediate-check="false"
distance="1%">
</ion-infinite-scroll>
</div>
</ion-content>
</ion-view>
start-ctrl.js
'use strict';
angular.module('main')
.controller('StartCtrl', function (Utility, $timeout, $scope) {
// bind data from service
// this.someData = Start.someData;
this.entries = [];
this.entriesView = [];
this.numEntriesToCreate = 5000;
this.numEntriesToAdd = 25;
var self = this;
$scope.$on('$stateChangeSuccess', function () {
console.log('START');
self.loadMore();
});
/**
* generate more entries
*/
this.generateEntries = function () {
var names = ['Gregor', 'Mathias', 'Roland', 'Jonas', 'Marco'];
var obj = {};
for (var i = 0; i < self.numEntriesToCreate; i++) {
obj = {'id': Math.random() + Math.random() * 10000, 'name': names[Math.floor(Math.random() * names.length)]};
self.entries.push(obj);
}
};
this.clearEntries = function () {
self.entries = [];
};
this.toggleAll = function () {
self.startLoading();
console.log(self.checkedAll);
// we wait for spinner to appear (500ms), then start..
$timeout(function () {
for (var i = 0; i < self.entriesView.length; i++) {
self.entriesView[i].checked = self.checkedAll;
}
self.finishedLoading();
}, 500);
};
this.loadMore = function () {
if (self.canLoadMore()) {
// self.startLoading();
$timeout(function () {
self.entriesView = self.entriesView.concat(
self.entries.slice(self.entriesView.length, self.entriesView.length + self.numEntriesToAdd) // exact items from our original entries
);
$scope.$broadcast('scroll.infiniteScrollComplete');
// self.finishedLoading();
}, 500);
}
//
};
this.canLoadMore = function () {
return (self.entriesView < self.entries) ? true : false;
};
this.startLoading = function () {
Utility.startTimer();
Utility.showLoading();
};
this.finishedLoading = function () {
$timeout(function () {
Utility.hideLoading();
console.log('execution took ' + Utility.endTimer() + 'ms.');
});
};
console.log('init. creating ' + self.numEntriesToCreate + ' entries');
self.generateEntries();
});
utility-serv.js
'use strict';
angular.module('main')
.service('Utility', function ($ionicLoading) {
this.opt = {
startTime: null
};
this.showLoading = function () {
$ionicLoading.show({template: '<ion-spinner></ion-spinner>'});
};
this.hideLoading = function () {
$ionicLoading.hide();
};
this.startTimer = function () {
this.opt.startTime = new Date().getTime();
};
this.endTimer = function () {
return ((this.opt.startTime) ? new Date().getTime() - this.opt.startTime : null);
};
});
I've also published the full example on GitHub. Project was generated using generator-m. You can simply clone the github and run it using gulp watch command.

Related

two way binding not working even with dot notation

I'm starting with AngularJS and I am using a controller variable to navigate an array of questions, and it is working when using nextQuestion function, index gets updated and the next question is shown in the view, but if I try to obtain the same value (index) in a different function, it always returns 0.
I have seen on other questions that you should use an object to contain the variable to not manipulate primitive types directly in the controller, but it still does not work.
My controller:
myApp.controller('SurveyController',['$scope','$http', '$location','$routeParams','surveyMetrics','DataService',function($scope,$http, $location,$routeParams,surveyMetrics,DataService){
console.log('LOADED QUIZ CONTROLLER');
var vm = this;
vm.scope = {
index: 0
};
vm.surveyMetrics = surveyMetrics;
vm.surveyQuestions = DataService.surveyQuestions;
vm.DataService = DataService;
/*
vm.getQuestions = function(){
$http.get('/api/questions').then(function(response){
$scope.questions = response.data;
});
}
*/
/*
vm.activateSurvey = function(){
surveyMetrics.changeState(true);
}
*/
vm.getCurrentIndex = function(){
return vm.scope.index;
}
vm.nextQuestion = function () {
console.log('NEXT QUESTION!');
console.log('NUMBER OF QUESTIONS: '+ vm.surveyQuestions.length);
var currentIndex = vm.getCurrentIndex();
var newIndex = currentIndex+1;
scope = {};
if (currentIndex == vm.surveyQuestions.length) {
newIndex = vm.surveyQuestions.length -1;
}
vm.scope.index = newIndex;
console.log('Inside Object: '+vm.scope)
console.log('vm.index'+vm.scope.index);
console.log('vm.indexFunction'+vm.getCurrentIndex());
}
/*
vm.previousQuestion = function () {
console.log('PREVIOUS QUESTION!');
console.log('NUMBER OF QUESTIONS: '+ vm.surveyQuestions.length);
if (vm.scope.index == 0) {
vm.scope.index = 0;
}else{
vm.scope.index--;
}
}
*/
vm.activeSurveyQuestion = function(questionId,index){
console.log('question id and index',questionId,index);
if (questionId == index) {
var navBtn = document.getElementById('navBtn_'+index);
navBtn.classList.add('active');
}
}
vm.navigateSurvey = function () {
var answerPane = document.getElementById('answer-pane');
document.onkeydown = function (e) {
console.log('INSIDE KEYDOWN: ')
e.preventDefault();
var pressedKey = e.keyCode;
console.log('PRESSED KEY IN SURVEY: ' + pressedKey);
if (pressedKey === rightArrow) {
console.log('survey - right arrow pressed');
document.getElementById('nextQuestionBtn').click();
console.log('FUCKING INDEX FML!: '+vm.getCurrentIndex()+' | '+vm.scope.index);
var questionType = DataService.getQuestionType(vm.scope.index);
console.log('Survey Controller: question type: '+questionType);
}
if (pressedKey === leftArrow) {
console.log('survey - left arrow pressed');
document.getElementById('previousQuestionBtn').click();
}
(...)
My View:
<!--Satisfaction Survey-->
<div ng-controller="SurveyController as survey" ng-init="survey.getSurvey();">
<!--
<p ng-repeat="question in survey.surveyQuestions" ng-show ="survey.surveyMetrics.surveyActive">
{{question.question}}
</p>
-->
<!--Survey Modal -->
<div class="modal fade" id="surveyModal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="text-center"> Customer Satisfaction Survey</div>
<div class="modal-header">
<h4 class="modal-title">{{survey.surveyQuestions[survey.getCurrentIndex()].question}}</h4>
</div>
<div class="modal-body survey" id="answer-pane">
<div class="row">
<div class="col-sm-2 survey-left-arrow" ng-click="survey.previousQuestion();" id="previousQuestionBtn">
<p>‹</p>
</div>
<div class="col-sm-8">
<!-- <p ng-repeat="answer in survey.surveyQuestions[survey.index].answers">{{answer}}</p> -->
<p ng-repeat="answer in survey.surveyQuestions[survey.getCurrentIndex()].answers">
<button type="button" class="btn" id="answerId_{{survey.getCurrentIndex()}}"
ng-class="{'survey-check-box': (survey.surveyQuestions[survey.getCurrentIndex()].type !== 'SingleChoice'),
'survey-btn_{{($index+1)}}': (survey.surveyQuestions[survey.getCurrentIndex()].type === 'SingleChoice')}">
<input type="checkbox" ng-if="survey.surveyQuestions[survey.getCurrentIndex()].type !== 'SingleChoice'"> {{answer}}
</button>
</p>
</div>
<div class="col-sm-2 survey-right-arrow " ng-click="survey.nextQuestion();" id="nextQuestionBtn">
<p>›</p>
</div>
</div>
</div>
<div class="text-center">
<strong> <p>Question: {{survey.surveyQuestions[survey.scope.index].questionNum}} of {{survey.surveyQuestions.length}}</p> </strong>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
<!-- <nav aria-label="Survey navigation">
<ul class="pagination pagination-sm justify-content-center">
<div ng-repeat="question in survey.surveyQuestions" >
<li class="page-item">
<a class="page-link" id = "navBtn_$index" ng-click="survey.index = $index">{{question.id}}</a>
</li>
</div>
</ul>
</nav> -->
</div>
</div>
I would like for the controller to change the variable and the view to update accordingly
Thank you for your time and thank you in advance.
It's 'survey.scope.index' not 'survey.index' in your HTML. I think you may be unclear the difference between using 'this' and '$scope'. You're mixing the two together which is not necessary. I would suggest removing 'scope' and just reference it in your HTML as 'survey.index'.

working with multiple <video> tags using videojs() function

I am trying to run videos on multiple video tags using videojs but woth no results. I am getting the error TypeError: The element or ID supplied is not valid. (videojs) I am initiating different id properties for each <video> tag based on the id's that they have received through a REST based call.
Should I be having an array of videoplayers instead of one as suggested by other links? Here is my source code:
video.js:
(function () {
'use strict';
angular
.module('controller.video', [])
.controller('Video', ['$scope', 'model', '$sce', function ($scope, model, $sce) {
$scope.ids = [];
$scope.videos = {};
$scope.titles = {};
$scope.specialtyvideos = null;
$scope.likes = {};
$scope.comments = {};
$scope.getVideos = function () {
model.get('specialty', 'Colorectal').then(function (res) {
$scope.specialtyvideos = res.data;
for (var i=0 ; i<$scope.specialtyvideos.length ; i++) {
$scope.videos[$scope.specialtyvideos[i]._id] = $sce.trustAsResourceUrl($scope.specialtyvideos[i].src);
$scope.titles[$scope.specialtyvideos[i]._id] = $scope.specialtyvideos[i].title;
$scope.ids.push($scope.specialtyvideos[i]._id);
}
});
};
$scope.specialtiesVideo = function (id)
{
var element = ""+id;
console.log(id);
console.log(videojs);
var vjs = videojs(id);
vjs.aspectRatio("16:9");
vjs.autoplay(false);
vjs.controls(true);
};
$scope.getLikes = function (id) {
model.get('likes', id).then(function (res) {
$scope.likes[id] = res.data.length;
});
};
$scope.getComments = function (id) {
model.get('comments', id).then(function (res) {
$scope.comments[id] = res.data.length;
});
};
$scope.initialize = function () {
$scope.getVideos();
};
}]);
})();
html file (view using angularjs):
<ion-view id="page17" class=" " ng-controller="Video" ng-init="initialize();">
<ion-content class="has-header">
<div class="list card" ng-repeat="i in ids">
<div class="item item-avatar">
<img src="">
<h2>{{ titles[i] }}</h2>
</div>
<div class="container" ng-init="specialtiesVideo(i)">
<div class="videocontainer">
<video class="video-js" id="{{ i }}" src="{{ videos[i] }}"></video>
</div>
</div>
<div class="item tabs tabs-secondary tabs-icon-left">
<a class="tab-item" href="#">
<i class="icon ion-thumbsup" ng-init="getLikes(i);"></i>
{{ likes[i] }} Likes
</a>
<a class="tab-item" href="#">
<i class="icon ion-chatbox" ng-init="getComments(i);"></i>
{{ comments[i] }} Comments
</a>
<a class="tab-item" href="#">
<i class="icon ion-share"></i>
Share
</a>
</div>
</div>
</ion-content>
</ion-view>
here are the dependencies in the main index html page:
<link href="lib/video.js/dist/video-js.css" rel="stylesheet">
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="lib/ngstorage/ngStorage.min.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="js/ng-cordova.min.js"></script>
<script src="cordova.js"></script>
<!-- OTHER REQUIRED DEPENDENCIES-->
<script src="lib/video.js/dist/video.min.js"></script>
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
<link href="css/ionic.app.css" rel="stylesheet">
-->
<script>
videojs.options.flash.swf = "/lib/video.js/dist/video-js.swf"
</script>
I found the solution to my problem when I realized that my angularjs code was executing much faster than the rendering of the webpage. I used a dynamic array of videojs for each of the many videos that I had to play in a single page. (Though I think a single videojs instance would do).
All I had to do next was to delay the particular function using the $timeout which is a wrapper for window.setTimeout by angularjs.
Here is the code snippet that solve the error:
$scope.specialtiesVideo = function (id)
{
var element = id;
console.log(id);
var confirm = document.getElementById(id);
console.log(document.getElementById(id));
$scope.videoplayers[id] = videojs(id);
console.log("videojs enabled for the video identified by ID :"+id);
$scope.videoplayers[id].aspectRatio("16:9");
$scope.videoplayers[id].autoplay(false);
$scope.videoplayers[id].controls(true);
};
$timeout(function() {
for (var id=0;id < $scope.ids.length;id++) {
$scope.specialtiesVideo($scope.ids[id]);
}
}, 20000);
where 20000 is the number of milliseconds I gave as a window for the page to render and the stack to clear after executions after which I executed the function shown in the code above.

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.

Angular controller value seeming to revert to original value

After originally setting my controller weddingPrice value a change caused by a function does not seem to change the variable. Its probably a simple error- can anyone help?
When sendWeddingQuoteInfo() is called it seems to send the original weddingPrice, not the one which has been updated when the return journey toggle has been checked.
What should happen is that the wedding price should be set at the start through the local setup function. Then if the return journey toggle is switched to on, the returnJourneyChange() should be fired, changing the global weddingPrice. When the Submit Quote button is pressed this should take the updated weddingPrice and send it to the next page. Currently this does not happen- no idea why.
//wedding.html
<ion-view view-title="Wedding Pax Num" can-swipe-back="true">
<ion-content ng-controller="WeddingPaxCtrl">
<div class="card">
<div class="item centerText" style="background-color: brown;">
<h2>CALCULATE WEDDING</h2>
</div>
</div>
<div class="padding20Top padding20Bottom">
<h2 class="centerText">Select number of Passengers</h2>
<input ng-model="passengerNo" class="col col-50 col-offset-25 quoteTFieldWithListItems" type="textfield">
<h6 class="centerText">PASSENGERS</h6>
</div>
<ion-toggle ng-model="returnJourney.checked" ng-change="returnJourneyChange()">Return Journey
</ion-toggle>
<ion-toggle ng-show="returnJourney.checked" ng-model="midnightReturn.checked" ng-change="midnightReturn()">Return Journey After Midnight</ion-toggle>
<div>
<h6 class="logText centerText"> {{ getCostBreakDown() }}</h6>
</div>
</ion-content>
<div class="endOfPageButton">
<a href="#/app/home/tester">
<button class="button button-full button-clear" ng-click="sendWeddingQuoteInfo()">Submit Quote</button>
</a>
</div>
</ion-view>
//controller.js
app.controller('WeddingPaxCtrl', function($scope, $stateParams, PassData, WhichQuote, CostOfMarriage, StoreQuoteData, WeddingData, DataThrow) {
var item = WeddingData.getWeddingDataObject();
$scope.passengerNo = 49;
$scope.returnJourney = { checked: false };
$scope.midnightReturn = { checked: false };
var weddingPrice;
$scope.returnJourney;
$scope.midnightReturn;
setup();
var sendInfo;
function setup() {
console.log("setup called");
weddingPrice = CostOfMarriage.getPrice(item[0], $scope.passengerNo, $scope.returnJourney.checked, $scope.midnightReturn.checked);
}
$scope.returnJourneyChange = function() {
console.log("return j called");
weddingPrice = 1000;
console.log("wedding price is now" + weddingPrice);
}
$scope.midnightReturn = function() {
}
$scope.getCostBreakDown = function() {
}
$scope.sendWeddingQuoteInfo = function() {
// var weddingPrice = $scope.weddingPrice;
console.log("WeddingPrice is " + weddingPrice + weddingPrice);
var sendInfo = ["Wedding Hire", item[0], $scope.passengerNo, "Extra", weddingPrice];
StoreQuoteData.setQuoteData(sendInfo);
WhichQuote.setInfoLabel("Wedding");
}
})
I think your ng-controller attribute is not at the right place. So the scope of your submit button is different.
I've moved the controller to ion-view element then the click is working as expected.
Please have a look at the demo below or here at jsfiddle.
(I've commented a lot of your code just to make the demo work.)
var app = angular.module('demoApp', ['ionic']);
app.controller('WeddingPaxCtrl', function($scope, $stateParams) { //, WeddingData, DataThrow) {
//var item = WeddingData.getWeddingDataObject();
$scope.passengerNo = 49;
$scope.returnJourney = {
checked: false
};
$scope.midnightReturn = {
checked: false
};
var weddingPrice;
$scope.returnJourney;
$scope.midnightReturn;
setup();
var sendInfo;
function setup() {
console.log("setup called");
weddingPrice = 100; //CostOfMarriage.getPrice(item[0], $scope.passengerNo, $scope.returnJourney.checked, $scope.midnightReturn.checked);
}
$scope.returnJourneyChange = function() {
console.log("return j called");
weddingPrice = 1000;
console.log("wedding price is now" + weddingPrice);
}
$scope.midnightReturn = function() {
}
$scope.getCostBreakDown = function() {
}
$scope.sendWeddingQuoteInfo = function() {
// var weddingPrice = $scope.weddingPrice;
console.log("WeddingPrice is " + weddingPrice + weddingPrice);
//var sendInfo = ["Wedding Hire", item[0], $scope.passengerNo, "Extra", weddingPrice];
//StoreQuoteData.setQuoteData(sendInfo);
//WhichQuote.setInfoLabel("Wedding");
}
})
<script src="http://code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<link href="http://code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet" />
<ion-view ng-app="demoApp" view-title="Wedding Pax Num" can-swipe-back="true" ng-controller="WeddingPaxCtrl">
<ion-content>
<div class="card">
<div class="item centerText" style="background-color: brown;">
<h2>CALCULATE WEDDING</h2>
</div>
</div>
<div class="padding20Top padding20Bottom">
<h2 class="centerText">Select number of Passengers</h2>
<input ng-model="passengerNo" class="col col-50 col-offset-25 quoteTFieldWithListItems" type="textfield">
<h6 class="centerText">PASSENGERS</h6>
</div>
<ion-toggle ng-model="returnJourney.checked" ng-change="returnJourneyChange()">Return Journey
</ion-toggle>
<ion-toggle ng-show="returnJourney.checked" ng-model="midnightReturn.checked" ng-change="midnightReturn()">Return Journey After Midnight</ion-toggle>
<div>
<h6 class="logText centerText"> {{ getCostBreakDown() }}</h6>
</div>
</ion-content>
<div class="endOfPageButton">
<!---<a href="#/app/home/tester">-->
<button class="button button-full button-clear" ng-click="sendWeddingQuoteInfo()">Submit Quote</button>
<!--</a>-->
</div>
</ion-view>

Getting blank screen

I am trying to see my cards on template, the issue that I cant see it only after I am doing $state.reload(); or open side menu,
My template looks like:
<ion-view>
<ion-nav-title> {{'nearPlaces_title'| translate}}
</ion-nav-title>
<ion-content>
<div class="bar bar-header item-input-inset">
<input id="autocomplete" type="search" placeholder="Search" g-places-autocomplete ng-model="myScopeVar"/>
</div>
<ion-refresher pulling-text="Pull to refresh" on-refresh="doRefresh()">
</ion-refresher>
<div class="list">
<a ng-repeat="item in items" class="item card"
href="#/tab/details/{{item.queId}}">
<div class="row">
<div class="col col-25">
<img ng-src="{{ item.entrancePhotoUrl }}" style="height:90%;width:90%">
</div>
<div class="col col-50" >
<div >
{{ item.name }}
</div>
<p style="text-wrap: normal;">
{{ item.streetAddress }}
</p>
</div>
<div class="col col-25">
<wj-radial-gauge
value="item.waitTimeEstimationSec"
min="{{config.minTimeToWaite}}"
max="{{config.maxTimeToWaite}}"
start-angle="-60"
sweep-angle="240"
is-read-only="true"
show-ranges="true">
<wj-range wj-property="pointer" thickness="0.5"></wj-range>
<wj-range min="0" max="{{max*.33}}" color="rgba(100,255,100,.2)"></wj-range>
<wj-range min="{{max*.33}}" max="{{max*.66}}" color="rgba(255,255,100,.2)"></wj-range>
<wj-range min="{{max*.66}}" max="{{max}}" color="rgba(255,100,100,.2)"></wj-range>
</wj-radial-gauge>
</div>
</div>
</a>
</div>
</ion-content>
also no errors on console
Update:
.controller('PlaceslistsCtrl', function ($scope, LocationService, PlacesService, $state) {
$scope.items = [];
LocationService.getNearPlaces().then(function (data) {
for (var i = 0; i < data.length; i++)
$scope.items.push(data[i].attributes);
$scope.config = configData;
PlacesService.setData($scope.items);
var input = document.getElementById('autocomplete');
var autocomplete = new google.maps.places.Autocomplete(input, {
types: ['(establishment)'],
componentRestrictions: {country: "il"}
});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
});
//$state.reload();
});
It looks like LocationService.getNearPlaces() returns promise, but if it doesn't use build-in services for that, you should call $scope.$apply() in the end of callback
.controller('PlaceslistsCtrl', function ($scope, LocationService, PlacesService, $state) {
$scope.items = [];
LocationService.getNearPlaces().then(function (data) {
for (var i = 0; i < data.length; i++)
$scope.items.push(data[i].attributes);
$scope.config = configData;
PlacesService.setData($scope.items);
var input = document.getElementById('autocomplete');
var autocomplete = new google.maps.places.Autocomplete(input, {
types: ['(establishment)'],
componentRestrictions: {country: "il"}
});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
});
//$state.reload();
$scope.$apply();
});

Resources