Angularjs ng-repeat to repeat depending on a variable in the controller - javascript

This is a partial view. Ng-repeat repeats over a json file that contains 50 email records.
<table class="table table-hover" ng-controller="emailViewController">
<tbody data-ng-controller="settingsController">
<tr ng-repeat="email in emails" >
<td><input type="checkbox" ng-checked="checkAllEmail" ng-model="selectedEmail"/>
<a href="#">
<span class="glyphicon glyphicon-star-empty"></span>
</a></td>
<td><label ng-bind="email.from"></label></td>
<td><label ng-bind="email.subject"></label></td>
<td><label ng-bind="email.time"></label></td>
</tr>
</tbody>
</table>
settingsController.js
(function() {
'use strict';
var settingController = function (fetchDataService, $scope, savePreferenceService, $localStorage) {
$scope.url = 'app/mock/settings.json';
$scope.save = {};
fetchDataService.getContent($scope.url)
.then(function(response){
$scope.contacts = response.data.contacts;
$scope.languages = response.data.languages;
$scope.conversations = response.data.conversations;
$scope.undoSend = response.data.undoSend;
$scope.save = response.data.userPreferences;
});
$scope.setPreference = function () {
savePreferenceService.setPreferences($scope.save.selectedLang, $scope.save.converse, $scope.save.selectedNumber, $scope.save.selectedNumberContact, $scope.save.reply, $scope.save.signature);
}
$scope.conversation = $localStorage.selectedNumber;
};
angular.module('iisEmail')
.controller ('settingsController',
['fetchDataService', '$scope', 'savePreferenceService', '$localStorage', settingController]);
}());
I am having trouble figuring out how to get ng-repeat to iterate the JSON file depending on the value of $scope.conversation. So, for example, if $scope.conversation is 10, I want ng-repeat to iterate only 10 times. I don't want to display the remaining 40 emails. Does anyone have any ideas on how to achieve this functionality?
UPDATE
With the help of #Prashank's comment, I figured it out. Here is the code using the limitTo filter.
<table class="table table-hover" ng-controller="emailViewController">
<tbody data-ng-controller="settingsController">
<tr ng-repeat="email in emails | limitTo: conversation" >
<td><input type="checkbox" ng-checked="checkAllEmail" ng-model="selectedEmail"/>
<a href="#">
<span class="glyphicon glyphicon-star-empty"></span>
</a></td>
<td><label ng-bind="email.from"></label></td>
<td><label ng-bind="email.subject"></label></td>
<td><label ng-bind="email.time"></label></td>
</tr>
</tbody>
</table>

You can use limitTo or slice and do the following,
here is a sample,
Using limitTo:
<div ng-repeat="item in items | limitTo:needed">
{{item.name}}
</div>
limitTo

Well there are two ways of doing this.
The easier way would be to use $index in your ng-repeat to hide/show the items as follows:
<tr ng-repeat="email in emails" ng-hide="conversation.length() < $index">
//same as in the question
</tr>
The way I would do it(although its a little lengthy) is using a filter as follows:
HTML:
<tr ng-repeat="email in emails | conversationFilter: conversation">
//same as in the question
</tr>
Filter:
app.filter('conversationFilter', function() {
return function(collection, conversation) {
return collection.slice(0, conversation);
}
})

Related

filter is really slow on ng-repeat in angularjs

I am displaying data from a web api in a table using an ng-repeat. I want to be able to filter that data using several different textboxes and select lists that align with the table columns (8 filters total). This table usually contains 10000+ rows. Here is how I am currently doing the filtering:
HTML:
<div ng-app="myApp" ng-controller="myCtrl">
<div class="col-md-12">
<h1>Multiple Filter Example</h1>
<hr/>
<input type="text" placeholder="ID Filter" class="form-control" ng-model="IdFilter" />
<input type="text" placeholder="Title Filter" class="form-control" ng-model="TitleFilter" />
<input type="text" placeholder="URL Filter" class="form-control" ng-model="UrlFilter" />
<br />
<table class="table">
<thead>
<tr>
<th>Album ID</th>
<th>ID</th>
<th>Title</th>
<th>URL</th>
<th>Thumbnail URL</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in data | filter:{id:IdFilter} | filter:{title: TitleFilter} | filter:{url: UrlFilter} ">
<td>{{ item.albumId }}</td>
<td>{{ item.id }}</td>
<td>{{ item.title}}</td>
<td>{{ item.url }}</td>
<td>{{ item.thumbnailUrl}}</td>
</tr>
</tbody>
</table>
</div>
</div>
Here is my JS:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
var vm = this;
vm.IdFilter = "";
vm.TitleFilter = "";
vm.UrlFilter = "";
$http.get("https://jsonplaceholder.typicode.com/photos")
.then(function(response) {
$scope.data = response.data;
console.log($scope.data);
});
});
plnkr example
As you can see the filter works but it is really really slow. Is there a better way to do this and speed it up? When typing into the filter text box there is a lag after i push a key on my keyboard.
Each filter function iterates through your data once. So if you have 8 filters and 10,000+ rows, you're looking at 80,000+ rows iterated.
I'd recommend writing a custom filter that loops through your 10,000+ rows once and applies all 8 filters at once.
app.filter('uberFilter', function() {
return function(data, filterCriteria) {
var filtered = [];
angular.forEach(data, function(item) {
if( passFilter(item, filterCriteria) {
filtered.push(item);
}
});
return filtered;
}
})
Your filterCriteria should contain all the filter criteria
filterCriteria = {
id: ...
, title: ...
, url: ...
}
passFilter() should check the item against all the filterCriteria's criteria
Your html should pass filterCriteria into the custom uberFilter
<tr ng-repeat="item in data | uberFilter:filterCriteria">
This should hopefully speed things up by a factor of 8.

How can I change the innerHTML of div with a variable, inside the scope function in angular js?

I have 2 HTML pages. In my index.html page you can see products information that comes from JSON file. I need to have detail of product in detail.html page when people click on particular product. Alert can show the details but unfortunately the innerHTML of my <p> does not change, please guide me.
<div ng-app="myApp" ng-controller="AppController">
<div id="serachWrapper">
<h1 id="headerTopic">Our Products</h1>
<input id="searchInput" name="search" type="text" placeholder="Search products" ng-model="searchquery"/>
<table id="searchTable">
<thead id="tbHead">
<tr class="tbRow">
<th class="tbTopics">ID</th>
<th class="tbTopics">Name</th>
<th class="tbTopics">Color</th>
<th class="tbTopics">Type</th>
<th class="tbTopics">Capacity</th>
<th class="tbTopics">Price</th>
</tr>
</thead>
<tbody id="tbBody">
<tr class="tbRow" ng-repeat="x in myData | filter:searchquery ">
<td class="tbcontents" >{{x.id}}</td>
<td class="tbcontents">{{x.name}}</td>
<td class="tbcontents">{{x.color}}</td>
<td class="tbcontents">{{x.type}}</td>
<td class="tbcontents">{{x.capacity}}</td>
<td class="tbcontents">{{x.price}}</td>
</tr>
</tbody>
</table>
</div>
And this is my Angular js code:
var app = angular.module('myApp', ['ngSanitize']);
app.controller('AppController', AppController);
function AppController($scope , $http) {
$http.get("products.json").success(function(myData){
$scope.myData = myData;
$scope.go = function(item){
var detail = item.detail;
var productDetail = angular.element(document.getElementById('product-detail')).html();
productDetail = detail;
alert(detail)
};
});
}
You can keep both codes in the page and to solve this with an ng-if directive
Angular ng-if directive
<body ng-app="ngAnimate">
<label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
Show when checked:
<span ng-if="checked" class="animate-if">
This is removed when the checkbox is unchecked.
</span>
</body>
Why is your $scope.go function defined inside the http.get request?
Try:
function AppController($scope , $http) {
$http.get("products.json").success(function(myData){
$scope.myData = myData;
});
$scope.go = function(item) {
var detail = item.detail;
var productDetail = angular.element(document.getElementById('product-detail')).html();
productDetail = detail;
alert(detail)
};
}

How to make dynamic content-editable table using angular JS?

Caveat: I've just started with client side scripting and Angular JS is the first thing I'm learning and now I feel I should've started with javascript.
PS: I don't wanna use any third party libraries. I wanna learn to code.
Anyway,I have dynamic table which I want to make editable using content-editable=true attribute of HTML.
Problem: How to I get the edited data? whenever I click on submit and pass the this object to the check() function. I doesn't contain edited values. is there a possible way to pass only edited value if it's dirty. It has pagination so If g to the next page the edited values are gone. I know I've give unique Id to every td element with $Index concatenated to it. But I don't know how should I proceed.
Any help or guidance will be appreciated. Controllers and others are defined in my route.
<div>
<form ng-submit="check(this)">
<table class="table table-striped table-hover">
<tbody>
<tr ng-repeat="data in currentItems">
<td contenteditable="true >{{data.EmpNo}}</td>
<td contenteditable="true">{{data.isActive}}</td>
<td contenteditable="true">{{data.balance}}</td>
<td contenteditable="true">{{data.age}}</td>
<td contenteditable="true">{{data.eyeColor}}</td>
<td contenteditable="true">{{data.fname}}</td>
</tr>
</tbody>
<tfoot>
<td>
<div class="pagination pull-right">
<li ng-class="{'disabled': previousPage}">
<a ng-click="previousPage()" >Previous</a>
</li>
<li ng-repeat="page in pageLengthArray track by $index">
<a ng-click="pagination($index)">{{$index+1}} </a>
</li>
<li disabled="disabled">
<a ng-click="nextPage()" ng-class="{'disabled':nextPage}>Next </a>
</li>
</div>
</td>
</tfoot>
</table>
<input type="submit" value="Submit">
</form>
$scope.currentPage=0;
$scope.pageSize=10;
$scope.currentItems;
$scope.tableData;
$http.get('../json/generated.json').then(function(response){
$scope.tableData=response.data;
$scope.pageLength=Math.ceil($scope.tableData.length/$scope.pageSize);
$scope.currentItems=$scope.tableData.slice($scope.currentPage,$scope.pageSize);
$scope.pageLengthArray= new Array($scope.pageLength);
});
$scope.pagination=function(currentPage){ $scope.currentItems=$scope.tableData.slice($scope.pageSize*currentPage,$scope.pageSize*currentPage+$scope.pageSize);
$scope.currentPage=currentPage;
}
$scope.nextPage=function nextPage(argument) {
$scope.currentPage++; $scope.currentItems=$scope.tableData.slice(($scope.pageSize*$scope.currentPage),($scope.pageSize*($scope.currentPage)+$scope.pageSize));
}
$scope.previousPage=function previousPage(argument) {
$scope.currentPage--;
$scope.currentItems=$scope.tableData.slice(($scope.pageSize*$scope.currentPage),($scope.pageSize*($scope.currentPage)+$scope.pageSize));
}
In the usual case, you can not get a change model for contenteditabe because to change the model used ngModel.
But we can create a directive that we have updated the value of the model.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope, $timeout) {
$scope.data = {
EmpNo: "123"
};
})
.directive('contenteditable', function($timeout) {
return {
restrict: "A",
priority: 1000,
scope: {
ngModel: "="
},
link: function(scope, element) {
element.html(scope.ngModel);
element.on('focus blur keyup paste input', function() {
scope.ngModel = element.text();
scope.$apply();
return element;
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<table>
<tr>
<td ng-model="data.EmpNo" contenteditable="true"></td>
</tr>
</table>
<pre>{{data|json}}</pre>
</div>
</div>
I would store any object that gets modified in a seperate array using the ng-keyup directive. When the form is submitted, you will have an array of only elements which have been modified. You may have some UX issues if your pagination is done by server as when you change page and come back, it will show your old data, but hopefully this helps.
$scope.check = function () {
// check modifiedItems
console.log(modifiedItems);
};
// store modified objects in a seperate array
var modifiedItems = [];
$scope.modifyItem = function (data) {
// check if data has already been modified and splice it first
for(var i = 0, j = modifiedItems.length; i < j; i++) {
var currentItem = modifiedItems[i];
if (currentItem.id === data.id) {
modifiedItems.splice(i, 1);
break;
}
}
// add to modified
modifiedItems.push(data);
console.log('modifiedItems: ', modifiedItems);
};
HTML
<form ng-submit="check()">
<table class="table table-striped table-hover">
<tbody>
<tr ng-repeat="data in currentItems">
<td ng-repeat="(key, value) in data" contenteditable="true"
ng-keyup="modifyItem(data)">
{{data[key]}}
</td>
</tr>
</tbody>
<tfoot>
</table>
<input type="submit" value="Submit">
</form>

Ng-repeat over localStorage data

This is my partial view
<table class="table table-hover" ng-controller="emailViewController">
<tbody>
<tr >
<td><input type="checkbox" ng-checked="checkAllEmail" ng-model="selectedEmail"/>
<a href="#">
<span class="glyphicon glyphicon-star-empty"></span>
</a></td>
<td><label ng-bind="add"></label></td>
<td><label ng-bind="subject"></label></td>
<td><label ng-bind="emailContent" ></label></td>
</tr>
</tbody>
</table>
I have this data stored in localStorage:
I want to ng-repeat over the values of the keys ngStorage-add and ngStorage-subject. I tried the following:
1) <td><label ng-bind="add" ng-repeat="item in localStorage.getItem('ngStorage-add')"></label></td>.
2) ng-repeat="item in localStorage.getItem('ngStorage-add') track by value"
It did not work however. Does anyone have ideas on how to achieve this?
EDIT
emailViewController:
(function() {
'use strict';
var emailViewController = function (fetchDataService, $scope,$filter,$timeout, $localStorage) {
$scope.to = [];
var url = 'app/mock/emails.json';
fetchDataService.getContent(url)
.then(function(response){
$scope.emails = response.data;
$scope.loadEmails('Primary');
angular.forEach($scope.emails, function(key) {
$scope.to.push(key.to);
});
});
$scope.information = {
add: [],
subject: [],
emailContent: []
};
$scope.typeaheadOpts = {
minLength: 1
};
$scope.$on("decipher.tags.added", function(info, obj) {
$timeout(function(){
tagAdded(info, obj);
});
});
function tagAdded(info, obj) {
for (var i = 0; i < $scope.to.length; i++) {
if ($scope.to[i] === obj.tag.name) {
$scope.to.splice(i, 1);
}
}
}
$scope.close = function(){
//console.log("hide");
$('.modal').modal('hide');
};
$scope.loadEmails = function(searchCriteria){
$scope.filteredEmails = $filter('filterByCategory')
($scope.emails,searchCriteria);
};
$scope.submit = function (add, subject, emailContent) {
if (! ($localStorage.add instanceof Array) ) {
$localStorage.add = [];
}
$localStorage.add.push(add);
if (! ($localStorage.subject instanceof Array) ) {
$localStorage.subject = [];
}
$localStorage.subject.push(subject);
if (! ($localStorage.emailContent instanceof Array) ) {
$localStorage.emailContent = [];
}
$localStorage.emailContent.push(emailContent);
//$scope.update();
};
$scope.clear = function() {
$scope.information.add = [];
$scope.information.subject = '';
$scope.information.emailContent = '';
}
$scope.$on('loadEmail',function(event,data){
$scope.loadEmails(data);
});
};
angular.module('iisEmail')
.controller ('emailViewController',
['fetchDataService', '$scope','$filter', '$timeout', '$localStorage', emailViewController]);
}());
ATTEMPT 1
This is returning the array - $localStorage.add
This is returning the first element of the array (a in this case) - $localStorage.add[0][0]
ATTEMPT 2
I injected $localStorage in the directive that was loading the template. It still does not work.
(function() {
'use strict';
var drafts = function ($localStorage) {
return {
templateUrl : "app/partials/draftsView.html"
};
};
angular.module('iisEmail').directive("drafts", ['$localStorage', drafts]);
}());
ATTEMPT 3
I stored the $localStorage data in a controller variable and got ng-repeat to iterate over the variable.
<td><label ng-repeat="item in addition track by $index">
{{item}}
</label></td>
Doing so produces a result, but the entire array is appearing in one line. I want a to be displayed in one line, and b to be displayed in the next line.
ATTEMPT 3 UPDATE
This is the updated partial view
<table class="table table-hover" ng-controller="emailViewController">
<tbody>
<tr >
<td><input type="checkbox" ng-checked="checkAllEmail" ng-model="selectedEmail"/>
<a href="#">
<span class="glyphicon glyphicon-star-empty"></span>
</a></td>
<td><label ng-repeat="item in addition track by $index">
{{item}}
</label></td>
<td><label ng-bind="subject"></label></td>
<td><label ng-bind="emailContent" ></label></td>
</tr>
</tbody>
</table>
ATTEMPT 4
I modified the partial view as follows, and achieved exactly what I was looking for. Now, values of the keys are displayed in new lines. However, they are being displayed like so:
`[a]
[b]
[c]`
I dont want the array symbol to be shown. Here is the modified partial view.
<table class="table table-hover" ng-controller="emailViewController">
<tbody>
<tr ng-repeat = "(key, value) in addition" >
<td><input type="checkbox" ng-checked="checkAllEmail" ng-model="selectedEmail"/>
<a href="#">
<span class="glyphicon glyphicon-star-empty"></span>
</a></td>
<td> {{ value }} </td>
<td><label ng-bind="subject"></label></td>
<td><label ng-bind="emailContent" ></label></td>
</tr>
</tbody>
</table>
SOLUTION
<table class="table table-hover" ng-controller="emailViewController">
<tbody>
<tr ng-repeat = "(key, value) in localStorage.add" >
<td><input type="checkbox" ng-checked="checkAllEmail" ng-model="selectedEmail"/>
<a href="#">
<span class="glyphicon glyphicon-star-empty"></span>
</a></td>
<td ng-repeat = "value in value"> {{value }} </td>
<td><label ng-bind="subject"></label></td>
<td><label ng-bind="emailContent" ></label></td>
</tr>
</tbody>
</table>
LocalStorage can only store string values, i believe you saved the JSON object into local storage by serializing it using JSON.stringify()
so, in order to get the JSON object from localstorage, you can use JSON.parse() function like below,
var add = JSON.parse(localStorage.getItem('ngstorage-add'))
var subject = JSON.parse(localStorage.getItem('ngstorage-subject'))
then you can iterate over it.
Read more about LocalStorage
In order your angular directive should work, you have to make it available to the scope, so you have to do in your controller, something like this:
.controller('Ctrl', function(
$scope,
$localStorage
){
$scope.localStorage = $localStorage;
});

My Angular view is not updated with data form server

I am using Angularjs and i make a call form server to retrieve data. Data are successfully retrieved but my view is not updated. I don't understand why. Here are the code i use.
Angularjs and html code :
<div class="row custom-margin" ng-controller="ListCtlr" ng-init="initData()">
<form class="form-inline" role="form" id="formId" name="formId">
<div class="form-group">
<label for="searchInput">Data to search</label>
<input ng-model="searchInput" placeholder="Enter term to search">
</div>
<button type="submitSearch" class="btn btn-primary" ng-click="search()">Go</button>
</form>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr class="info">
<th colspan="4" class="centertext">Name</th>
<th colspan="3" class="centertext">Age</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in persons">
<td>{{person.name}}</td>
<td>{{person.age}}</td>
</tr>
</tbody>
</table>
</div>
</div>
Controller code :
function ListCtlr($scope, $http, $location,$filter) {
$scope.formId = {searchInput: ''};
$scope.search = function () {
var url='server/search/'+this.searchInput;
$http.get(url)
.success(function (data) {
$scope.persons = data;
})
.error(function(data){
$scope.error = data;
});
};
}
When i inspect the data retrieved form server i get the following JSON data :
[{"name":"John","age":12},{"name":"Mary","age":25},{"name":"Garry","age":28}]
What's missing please ?
Change
<tr ng:repeat="person in persons">
<td>{{person.name}}</td>
<td>{{person.age}}</td>
</tr>
to
<tr ng-repeat="person in persons">
<td>{{person.name}}</td>
<td>{{person.age}}</td>
</tr>
The problem is that your ListCtlr controller is placed on a div that does not contain your ng-repeat.
To solve this, create an outer div, put the ng-controller on that div:
<div ng-controller="ListCtlr">
... (place contents of your html here) ...
</div>
This ensures that ListCtlr's scope includes the ng-repeat.
Note: Be sure to remove the ng-controller="ListCtlr" defined in your inner div.

Categories