Angular Expression in ng-class evaluating after css is applied (late binding) - javascript

I have an ng-class where class is dynamically loaded i.e evaluating $rootscope in expression, late binding is happening
<li role="presentation" style="width:200px" ng-class="{active: true, inactive: false}[{{vm.$rootScope.currentPage}}==1]">Create</li>
Here {{vm.$rootScope.currentPage}} expression is evaluating after class for the control is loaded in place of that if give 1 it is working or and if I check in inspect element I can see [1==1] i.e {{vm.$rootScope.currentPage}} this is returning correct value but class is not getting applied
Can refer this Condition in ng- class not working

you should use [vm.$rootScope.currentPage===1] instead of [{{vm.$rootScope.currentPage}}==1] . no need to use {{}} in []
and need to change
ng-class="{true: 'active', false:'inactive' }" instead of ng-class="{'active': true , 'inactive':false }"
for ng-class="{true: 'active', false:'inactive' }[vm.$rootScope.currentPage==1]" class applied way
if expression value is true then apply class active
if expression value is false then apply class inactive
so use:
<li role="presentation" style="width:200px" ng-class="{true: 'active', false:'inactive' }[vm.$rootScope.currentPage===1]">Create</li>
Also can use ternary operator in ng-class like: ng-class="vm.currentPage===1? 'active': 'inactive'"
<li role="presentation" style="width:200px" ng-class="vm.currentPage===1? 'active': 'inactive'">
Create
</li>
PLUNKER DEMO

Try this- ng-class="vm.$rootScope.currentPage==1?'active':'inactive'".
You're adding multiple extra layers of work for Angular, just for a simple true/false test. Most notably, you had a {{ }} expression in an ng directive. AFAIK, all of these already track the expression you put in, and adding a {{ }} just adds more that it needs to watch.
You may also be able to refer it it simply as currentPage, instead of vm.$rootScope.currentPage, but that depends if you've set up the scoping for whatever component this is.

Related

Hide item that is part of a loop based on a condition

<ul *ngFor="#item of items; #i=index" >
<li [hidden]="{{ item.myattr === 'some_value' }}"> {{ item.val}} </li>
</ul>
I have the following code shown above. I want hide the list if the item has a value equal some value. In this case I have the items, they have an attribute called myattr, and if it is equal to some_value then the item should be hidden. The code I provided though does not work.
You don't need to use interpolation {{}} with property binding [] (actually, you can't):
<li [hidden]="item.myattr === 'some_value'">
Also, read Mistake #1: Binding to the native "hidden" property in http://angularjs.blogspot.com/2016/04/5-rookie-mistakes-to-avoid-with-angular.html
So a better solution is likely
<li *ngIf="item.myattr !== 'some_value'">
You can basically use ng-hide also like;
<li ng-hide="item.myattr =='some_value'"> {{item.val}} </li>
assuming that item.myattr is the same type with some_value.
I think is better to use pipe to filter out unneeded items.
https://angular.io/docs/ts/latest/guide/pipes.html

ng-non-bindable with ng-if in Angular Google Maps

So I read Why is ng-non-bindable required for <ui-gmap-windows> element in Angular Google Maps?
and I get how ng-non-bindable is used in the directive.
My problem is that I'm trying to use ng-if to conditionally reveal some icons in my marker infowindows. ng-if doesn't work with ng-non-bindable, and of course, the info windows don't work without it.
If someone can tell me how to make ng-if work in this situation or give an alternative solution, i'd greatly appreciate it.
<ui-gmap-windows show="show">
<div ng-non-bindable>{{obj.name}}<br>{{distance}} miles
<span class="ion-man" ng-if="obj.men"></span>
<span class="ion-woman" ng-if="obj.women"></span>
<span class="ion-ios-people" ng-if="obj.people"></span>
</div>
</div>
</ui-gmap-windows>
You need to use the templateUrl and templateParameter attributes on the ui-gmap-windows directive. The templateUrl is a property on the object that you're passing in which is a string that is the path to the .html template you want to use. The templateParameter is a property on the object that you are passing which is an object containing the parameters you want passed in. See code below. Also you can reference the docs here: http://angular-ui.github.io/angular-google-maps/#!/api/windows
<ui-gmap-windows idKey="'name'" show="show" templateUrl="'pathToHtmltemplate.html'" templateParameter="'{name: 'name', distance: 1.3, womens_restroom: true}'">
</ui-gmap-windows>
Then in your html template you can use ng-if no problem.
Note you have to reference the object you pass into templateParameter as parameter in the html template so the
pathToHtmlTemplate.html file would look like this
<div>
{{parameter.name}}<br>{{parameter.distance}} miles
<span ng-if="parameter.womens_restroom" class="ion-woman"></span>
</div>
The reason it does not work is because, ng-if (600) has lower priority than ng-non-bindable (1000) and ng-non-bindable is terminal:true so ng-if never compiles due to the terminal nature of ng-non-bindable. You could try using ng-switch (1200) directly on the element with ng-non-bindable though since it has higher priority but not sure if that is applicable in your case if you may have multiple conditions that can be true.
You can as well create your own directive say my-non-bindable anddefine it with configuration, {priority:599, terminal:true} and use it with ng-if say:
.directive('gmapTemplate', function() {
return {
priority: 599,
terminal: true
};
});
and use it with ng-if.
<div gmap-template ng-if="someCond">{{obj.name}}<br>{{distance}} miles
Otherwise you will have to wrap non bindables within element with ng-if
<div ng-if="someCond">
<span ng-non-bindable>{{obj.name}}<br>{{distance}} miles</span>
</div>
angular.module('app', []).directive('gmapNonBindable', function() {
return {
priority: 599,
terminal: true
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div ng-app="app" ng-init="test:123; show:false">
Show-->
<input type="checkbox" ng-model="show" />
<div gmap-Non-Bindable ng-if="show">{{test}}</div>
</div>

Curly braces in ng-repeat - Syntax?

I am trying to filter a JSON list based on one of the contained values. For example, I have JSON objects where they all have name, type, description etc and I am attempting to filter based on the parameter stored in $stateParams (which happens to be type). It works if I hard code the type (e.g. "item in items| filter:{Type:'Grain'}").
Also, I know the value from $stateParams is working as I have it set to the page title. Is there a problem with my below syntax?
<div class="list card">
<div ng-repeat="item in items| filter:{Type:'{{type}}'}">
<a class="item item-icon-left" ng-click="onSelectItemList(item)">
<i class="icon ion-home"></i>
{{item.Name}}
</a>
<div>
Thanks in advance.
Yes, the problem is the filter expects an expression, You should not interpolate ({{) the expression to value.
change
filter:{Type:'{{type}}'}
to
filter:{Type:type}
type is expression evaluated against scope and {{type}} --> Value of expression evaluated against the scope.
Use:
<div ng-repeat="item in items| filter:{Type:type}">
You can't do nested interpolation. Filter is expecting an expression, therefore type is evaluated as is.

Trigger function on a certain element - ngrepeat - angularjs

Good morning,
I'm trying to change the limitTo filter on a certain list, my issue is:
when I click to the trigger who change the filter limit the filter changes on all ng-repeated categories.
my function inside the main controller
$scope.showMore = function(limit) {
if($scope.limitItems === $scope.itemsPerList) {
$scope.limitItems = limit;
$scope.switchFilterText = 'less';
} else {
$scope.switchFilterText = 'more';
$scope.limitItems = $scope.itemsPerList;
}
}
my scenario (I rewrote it in a simplified version)
<li ng-repeat="item in category.items | limitTo: limitItems ">
{{item.title}}
</li>
<li ng-if="limitItems < (category.items.length)">
<a ng-click="showMore(category.items.length)" >Show {{ switchFilterText }}</a>
</li>
Could you explain me what's wrong with me?
I searched how to select a single element to apply the function but I didn't find anything useful
Update:
I found the way to solve my issue in this way:
No functions inside the controller are involved to make this functionality works properly:
<li ng-repeat="category in maincategories" ng-init="limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: limitItems "> {{item.title}}
</li>
</ul>
<a ng-click="limitItems = category.items.length" href>
<b ng-if="category.items.length > maxItemsPerList && limitItems != category.items.length "> Show more </b>
</a>
I'm not really convinced about Angular (I used it in my past and I was impressed by the performance but now I can see logics senseless):
What I learned:
ng-if and ng-click cannot be used in the same content because ng-if creates new scopes so if you put ng-if on top of the "show more" link it will break the code
ng-init cannot be used in the same element of the ng-repeat otherwise the var initialised will not be available inside the ng-repeat block
I think there is another way to do that, maybe more clean but in this specific case I can't do a lot.
ng-if and ng-click cannot be used in the same content because ng-if
creates new scopes so if you put ng-if on top of the "show more" link
it will break the code
Yes, ng-if creates a new scope, but it is possible to mix ng-if and ng-click (and most other directives). To do that, you'll be safer if you always write to atributes of another object instead of a simple variable. It is plain JavaScript prototypal inheritance in play.
<li ... ng-init="category.limitItems = maxItemsPerList">
ng-init cannot be used in the same element of the ng-repeat otherwise
the var initialised will not be available inside the ng-repeat block
True, in the sense that variables are created in the local scope. But again, refer to an object.
I think there is another way to do that, maybe more clean but in this
specific case I can't do a lot.
You don't need to do a lot, it is quite simple to do it right actually.
Some advices:
Use ng-init with care. I know it will tempt us but always try to put logic inside controllers and services;
Avoid assignments inside templates;
Learn how to use controllerAs syntax. It gives you an object to write your models to (the controller), so solves most problems related to scope inheritance;
Do not inject $scope, put your view models inside controllers.
Full code goes like this:
<li ng-repeat="category in maincategories" ng-init="category.limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: category.limitItems "> {{item.title}}
</li>
</ul>
<a ng-if="category.items.length > maxItemsPerList && category.limitItems != category.items.length" ng-click="category.limitItems = category.items.length" href>
<b> Show more </b>
</a>

Angular JS: ng-switch on boolean not working

I am trying to conditionally display a directive based on a boolean value stored in the parent scope. I can't figure out why the below does not work. By, "not work" I mean neither directives are displayed.
<ul class="nav navbar-nav pull-right" ng-switch="userIsAuthenticated">
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when="false"></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when="true"></account-item>
</ul>
Neither directives are shown even thought "userIsAuthenticated" is set to 'false' in my test case. If I add {{userIsAuthenticated}} above the directives 'false' is output as expected.
I've also tried this:
<ul class="nav navbar-nav pull-right" ng-switch={{userIsAuthenticated}}>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when={{false}}></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when={{true}}></account-item>
</ul>
If I remove the conditional ng-switch-when attribute from either of the directives they will display. So I'm know the problem is my ng-switch.
Your usage of ng-switch works in this simplified demo, of course without your account-item directive:
http://plnkr.co/AppN8xmFeIwjaP631lj7
Without seeing the code for account-item, it is hard to guess what might be interfering with it. You might consider using ng-if to handle displaying one item or another.
<ul>
<div ng-if="!userIsAuthenticated">Content when not authenticated</div>
<div ng-if="userIsAuthenticated">Content when authenticated</div>
</ul>
Update
Also make sure you bind to an object property, instead of a primitive boolean. Like: user. authenticated
Since ngSwitchWhen has a priority of 800, you need to set a higher priority to your custom directive (i.e. account-item) in order for it to be compiled before being process by the ngSwitchWhen directive. E.g.:
.directive('accountItem', function () {
return {
...
priority: 900,
...
};
});

Categories

Resources