I have an ng repeat repeating data. - Some of data.image (src) is null, and those with src=null are not to be repeated.
I solved it with a simple ng-if.
<div ng-repeat="data in data">
<div class="ImageContainer">
<img ng-src="{{::data.image}}" ng-if="data.image != null" />
</div>
<div class="LabelContainer">
<p>
{{::data.label}}
</p>
<div>
<div>
but from debugging I noticed this cost me around 500 watchers. Any obvious way to accomplish what im trying without the ng-if or a major JS vanilla function ?
You could filter the list in your controller so you have a method which returns all of the items in the collection where the image property isn't null and bind your repeat to that function.
You can apply one way binding on ng-if that way you can reduce few more watchers.
I agreed with #DoctorMick, filter will reduce number for loop, watchers and it will be fast as well. But if you have some other data along with image and you want to show that data then it will not solve your purpose.
Related
I'm generating a lot of HTML with ng-repeat. I rely on the $index variable in order to index data in my controller.
I do alot of stuff like
ng-submit="validateExistingGuest($index)"
In this case, an undetermined number of HTML forms is generated, hence the index.
Problem is, i sometimes need to use this variable inside a different kind of expression. That would look something like that:
ng-if= "user{{$index}}.valid"
Of course, that doesn't work. I tried ways of constructing that expression, with no success.
How would one go about doing this?
You need something like
ng-if="user[$index].valid
<div ng-repeat="value in user track by $index"> ...
//now you can use the {{$index}}
<div ng-if="user[$index].valid">Show only if valid!</div>
</div>
Why don't you use the object instead the $index?, try
<div ng-repeat="car in cars" >
<p {{car}}></p>
<button ng-click="buy(car)" > buy </button>
</div>
I am using oi-select library, i have customized it according to my project need and i have written directive for it. The problem is its taking too much of time say 10secs to load around 3k array data. I want to minimize the time for it. Here I have created plunker for this.
I have directive which loads all factory data and provides it to oi-select, in my code here is html
<small><b>select {{MPhardwaresListDropDown.length}}</b></small>
<div style="padding-top: 7px">
<div title="" class="selected-multiple-items">
{{MPselectedHardwares.length}} selected
</div>
<grid-multi-select id="hardwareId" clean="clean" optionlist="MPhardwaresListDropDown" colval="name"></grid-multi-select>
</div>
HTML code in directive looks like
<div>
<div ng-repeat="optionVal in tempOptionList | orderBy : ['-originalcheck','+label']" prevent-close>
<div ng-if="optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.label.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.label}}</span>
</label>
</div>
<div ng-if="!optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.val.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check" ng-change="checking(typeFilterOptions)"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.val}}</span>
</label>
</div>
</div>
angular code is too big to mention in this question please refer plunker, but this is how it loops
scope.selection = scope.selectionorg;
scope.checkedall=scope.checkedallorg;
scope.OptionList=parentScope[scope.parentListName].sort();
scope.tempOptionList=[];
var i=0;
for(i=0;i<scope.OptionList.length;i++) {
scope.tempOptionList[i] = {val: scope.OptionList[i], check: false, originalcheck: false};
}
if(scope.optionListSelectedList.length>0) {
angular.forEach(scope.optionListSelectedList, function(obj){
angular.forEach(scope.tempOptionList, function(obj1){
if(obj===obj1.val){
obj1.check=true;
obj1.originalcheck=true;
}
});
});
}
else{
scope.checkedall=false;
}
};
I want something like which will load partial data on scroll it loads more data, any help will be appreciated. Thank you so much.
EDIT
Now i have edited my plunker with limitTo in ng-repeat, for that i have written new directive which will trigger addmoreitems function when scroll will reach bottom. updatedPlunker
Now problem is when i am typing and searching something its searching in only available records with respect to limitTo its not searching in all data, say now the limitTo is 50 then search is happening only in 50 records not in 3k records.
Correct way of doing this kind of requirement is to do with pagination, since you are loading data from the server side, you should make your api to support pagination.
It should accept three parameters such as number of items, start, limit and you should initially get the number of items and repeat it until you get the whole result set.
There should be another request to get the total number of items. By doing this you can retrieve all the elements at once loaded in the client side. Until that you should have a loading indicator, or you could load this data when the login process/application starts.
limitTo will not be able to help with the search , because you are limiting the results.
The problem is not the array, the browser can easly handle that, the problem is that you're rendering all the 3k DOM elements, that's really heavy work even for an actual machine, also since there is bindings in each dom element {{}} they're being watching by AngularJs, I got the same problem and solved using Virtual Repeat from AngularJS Material, what this does is it doesn't render the whole 3k DOM elements generated by the ng-repeat, instead it just renders the ones that are visible, also I've found another library if you don't want to use Angular Material, this seems to work the same way: Angular VS-Repeat
You may try the limitTo filter in ng-repeat in angularjs which takes the additional argument to start the iteration.
https://docs.angularjs.org/api/ng/filter/limitTo
On scroll, you can then change that argument based on the number of items pending or left for rendering or the number of items already rendered. This should help you in approach of selective loading of data on scroll.
I have the following template for directive item in angular. Item contains property type in it's scope. Based on this type I need to render different template.
<item ng-repeat="myItem in items track by myItem.id">
<div ng-switch="::myItem.type">
<div ng-switch-when="type1">
Some complex html with a couple of ng-if and ng-include
</div>
<div ng-switch-when="type2">
Some complex html with a couple of ng-if and ng-include
</div>
<div ng-switch-when="type3">
Some complex html with a couple of ng-if and ng-include
</div>
<div ng-switch-when="type4">
Some complex html with a couple of ng-if and ng-include
</div>
<div ng-switch-when="type5">
Some complex html with a couple of ng-if and ng-include
</div>
<div ng-switch-when="type6">
Some complex html with a couple of ng-if and ng-include
</div>
</div>
</item>
I'd like to rework every ng-switch-when in the standalone directive, and improve performance of ng-repeat (right now it has to make ng-include and set innerHTML for every item and it's a heavy operation for the browser even for list of 50 items).
So my questions are:
how to rework ng-switch-when into standalone directives
how to improve performance of the ng-repeat in this case
Update:
The question related to the AngularJS: Parse HTML events in timeline it's one the reasons why I would like to optimize current structure.
added missed track by
a lot of ng-if and ng-include statements is needed because every widget that represented by ng-switch-when has it's own logic and will be rendered based on some other parameters of myItem.
The problem also that I need to fully rebuild this list of item by some user action.
BTW:
In react I can do something like:
render: function() {
//it's for two items, but can be extended for any number of templates
var child = this.props.type === 'Type A' ? <TemplateA /> : <TemplateB />;
return child;
}
I don't know why you need a lot of IF statements, and why you want use directives for that, but i can help you with advice for ng-repeat.
Use track by $index in ng-repeat to improve performance. And check articles about Angular performance, like this: 11 Tips to Improve AngularJS Performance
Also, if you have interface with a lot of elements which need to render, may be you should look in ReactJS which is faster for rendering.
I would like to save an object in a ngRepeat so that I can use that object in its children, like shown in this code:
<div ng-repeat="bar in foo.bar>
<div ng-repeat="qux in baz.qux" myvalue="{'item1':foo.var1, 'item2':qux.var2}">
<div ng-click="myFirstFunction(myvalue)"></div>
<div ng-click="mySecondFunction(myvalue)"></div>
</div
</div
The object I want to generate and then use is rather large and I'd rather not define it repeatedly for each ngClick directive.
I considered saving it into a scope variable but the object will change for each iteration of the ngRepeat.
Is there a directive or an other way that I can use to store this value for later use?
To avoid the repetition of what is probably a long variable definition, you can use the ngInit directive, whose content will be executed each time a corresponding element is created.
<div ng-repeat="bar in foo.bar>
<div
ng-repeat="qux in baz.qux"
ng-init="myValue = {'item1':foo.var1, 'item2':qux.var2 }"
>
<div ng-click="myFirstFunction(myValue)"></div>
<div ng-click="mySecondFunction(myValue)"></div>
</div>
</div>
However, a complex code in a template is rarely a good idea. Contemplate moving your logic inside a controller, as advised by the documentation:
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
You can just do the naive thing here and it'll work:
<div ng-repeat="bar in foo.bar>
<div ng-repeat="qux in baz.qux">
<div ng-click="myFirstFunction(foo, quz)"></div>
<div ng-click="mySecondFunction(foo, quz)"></div>
</div>
</div>
Angular will know the scope of the repeat when you click.
You could store it in local storage using ng-storage.
https://github.com/gsklee/ngStorage
This would allow you to store it, then use it anywhere in the application.
cookies, you also have ng-cookies
https://docs.angularjs.org/api/ngCookies
try this out! or cookieStorage
I display a list of bars in a NgRepeat and I use the value frecuency to display the width of bars in percentage. From what i see IE 9-10 doesn't like this part: style="width:{{type.frecuency}}%;"
<div class="drp" ng-repeat="type in weeks">
<div style="width:{{type.frecuency}}%;" class="percentBar">
<span ng-if="type.frecuency > 14">{{type.frecuency}}%</span>
</div>
</div>
Is this an issue with Angular on IE or my code is the problem.
Thanks
P.S.
I know that i could make a class but modifying the style attribute is faster.
Solution: ng-style="setBarWidth(type.frecuency);"
scope.setBarWidth = function(width) {
return {width: width+'%'};
};
When using derived values for various HTML attributes, it's always a good idea to use the provided Angular directives to do it. They make sure that the browser sees the values you want it to see and not the binding syntax (in your case {{type.frecuency}})
Here, the ngStyle directive should be used.
<div class="drp" ng-repeat="type in weeks">
<div ng-style="width:{{type.frecuency}}%;" class="percentBar">
<span ng-if="type.frecuency > 14">{{type.frecuency}}%</span>
</div>
</div>
There are similar directives for many other HTML attributes, see the documentation for the full list.