AngularJS: two ng-if or one interpolation? - angularjs

A text should be shown depending on condition. One scenario, two ways to implement it.
The first way:
<p ng-if="theType == 'type21'">Alert! Special type...</p>
<p ng-if="theType != 'type21'">Chill...</p>
The second way:
<p>{{theType == 'type21' ? 'Alert! Special type...' : 'Chill...'}}</p>
Which one is better? Pros and cons for each way?

I think that the ng-if - as the documation explains:
The ngIf directive removes or recreates a portion of the DOM tree based on an {expression}. If the expression assigned to ngIf evaluates to a false value then the element is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
The second one only checks the condition once but I think DOM efficiency is more importent.

For simple cases like those, it's just a matter of preference.
For simple expressions, I would go for the second one. It is just a simple expression and won't show any difference in performance. And if you don't expect theType to change, you could use bindOnce syntax to prevent further evaluations - improving performance
{{::(theType == 'type21' ? 'Alert! Special type...' : 'Chill...')}}
See fiddle, you can play removing the ::
For more complex cases, I would rather move interpolation to controller and just using {{textToShow}} and I would leave ng-if for directives that can be removed, or if DOM structure has to change (here you just change text inside <p>, you are not adding new DOM elements)

Related

AngularJS Protractor: select div with ng-show directive

I need to access to a div with a ng-show directive WITHOUT using xpath
<div ng-show="my_error && dirty_field">
Custom error message.
</div>
I tried these but it doesn't work properly
element(by.css('[ng-show=my_error && dirty_field]'));
and
element(by.model('my_error && dirty_field'));
How can I do?
Just to add few points here.
First of all, you definitely need the quotes around the ng-show value in this case:
element(by.css('[ng-show="my_error && dirty_field"]'));
This is because the value contains non-alphanumeric characters. See this related thread:
CSS attribute selectors: The rules on quotes (", ' or none?)
Also, I don't think you should use the dirty_field part in your locator. This sounds like a more technical variable used in the form validation logic. I'd use the "contains" check instead to check the my_error part only (note how I've removed the quotes in this case - the value is alphanumeric):
element(by.css('[ng-show*=my_error]'));
Also note that you can use the $ shortcut instead of element(by.css()):
$('[ng-show*=my_error]');
You cab deal with the properties this way in doing protractor testing.
element.all(by.css('[ui-sref="about_us"]')).first();
element(by.css('[ng-click="clickme()"]')),
element(by.css('[ng-show*=show_me]'));

When using an expression, is there a way to... not replace the previous value?

I know that by doing
{{bubba}}
Will output the bubba value in the current scope, but can I have the expression not... do anything (not update the DOM) based on a value? Essentially, what i'm trying to accomplish is if the value is false, show the previous value.
{{!bubba ? dontDisplayEvalutedExpression : bubba}}
Adding a plunker as an example:
http://plnkr.co/edit/yYy62x2aK2RiDCUxwtAK
You can use this way,
{{bubba === "one" ? "it's true" : "it's false"}}
Working Plunker
Else if you want to generate new dom elements you can use ng-if
The ngIf directive removes or recreates a portion of the DOM tree
based on an {expression}. If the expression assigned to ngIf evaluates
to a false value then the element is removed from the DOM, otherwise a
clone of the element is reinserted into the DOM.

display current value of ng-class in AngularJS

I have to track down a bug related to work of ng-class (sometimes it adds new value without removing old).
So I need a quick reference to see it's current value.
Is there any short (or not) way to bind that to the content?
I mean something like this:
<div ng-class="something">
{{ngClassValueDisplayedHere}}
</div>
I had exactly the same problem with ng-class not removing old value. After days of investigation it turned out that it was ngAnimate who was messing with class changes. removing it from angular module dependencies solved the problem (Angular 1.3).
ng-class can bind to many different things. From the documentation:
Expression to eval. The result of the evaluation can be a string representing
space delimited class names, an array, or a map of class names to boolean
values. In the case of a map, the names of the properties whose values
are truthy will be added as css classes to the element.
So in your example, just display whatever your something is. It's supposed to be an angular expression, which can be evaluated like any other with double-curlies. This will help you debug your ng-class
<div ng-class="something">
{{something}}
</div>
Demo
In case someone else stumbles upon this problem like I did just recently with angular version 1.5.8: https://github.com/angular/angular.js/issues/14582
P.S. Update to 1.5.11 solved the issue related to ngAnimate, prior versions still had the same issue.

How to optimize ng-switch

I'm using angular's ng-switch to build form's fields like this
<div ng-switch="vm.myField">
<my-input-type1 ng-switch-when="type1"></my-input-type1>
<my-input-type2 ng-switch-when="type2"></my-input-type2>
<my-input-type3 ng-switch-when="type3"></my-input-type3>
<my-input-type4 ng-switch-when="type4"></my-input-type4>
<my-input-type5 ng-switch-when="type5"></my-input-type5>
<my-input-type6 ng-switch-when="type6"></my-input-type6>
<my-input-type7 ng-switch-when="type7"></my-input-type7>
<my-input-type8 ng-switch-when="type8"></my-input-type8>
</div>
because of performance it's not good solution (on entering view with it brwser is pausing for a while).
Without ng-switch all is rather ok.
How can I optimize it? ng-if has the same prefomrance issue.
As you probably know from the Angular documentation (https://docs.angularjs.org/api/ng/directive/ngSwitch)
ng-switch works by adding and removing the nested DOM element based on a conditional statement. This can be quite slow for large templates/DOM elements.
One solution I used was to use ng-show/ng-hide (https://docs.angularjs.org/api/ng/directive/ngShow).
These directives do not modify the DOM structure, but instead use CSS to hide/show the elements. It can be faster, but beware that the DOM could become very large if you fall into the trap of trying to contain all of templates/DOM elements of your web site in memory at the same time in this manner.
I think I found a solution.
Insead of ng-switch I'm using
<ng-include src="'/my-fields/directives/'+vm.myField+'.html'"></ng-include> with proper html code.
For now works good but have to test a bit more.
EDIT: now it is much, much faster

Stop AngularJS inserting <span class="ng-scope"></span> using ng-include

I'm using the Foundation layout framework, which automatically floats the last sibling of .column to the right and I really appreciate this is a behaviour. However, AngularJS takes it upon itself to insert span.ng-scope after every div.column, which somehow causes browsers to consider the last span the last sibling of .column (even though it is not).
Specifically the css in Foundation responsible for this is:
[class*="column"] + [class*="column"]:last-child { float: right; }
As I understand it, [attribute*="substring"] should select only siblings that match, so, for the above, only elements whose class attribute contains column (including columns). I would think a span tag whose class attribute that does not contain column should not match (and thus be ignored by :last-child). However, this does not seem to be the case.
Regardless, the span is causing the problem:
Angular buggering it up (jsfiddle)
Works fine without Angular (same jsfiddle, no ng-include)
Is there a way to configure angular to stop inserting those span tags? I would, begrudgingly, modify the css selector to somehow ignore all span tags; however I might eventually need/want to use a span tag.
Since you indicated the div can be moved inside, this works:
<ng-include src="'main.tmpl'"></ng-include>
Then in your template:
<div class="row">
<article id="sidepanels" class="four columns">
...
</div>
I'm not aware of any way to prevent angular from inserting the span tags (I think it keeps track of scopes that way -- for garbage collection).
Also you can try my version of include directive that does not creates a scope: Gist source.
As no scopes are created, AngularJS should not create additional element to mainain scope (it actually use data attributes to store link to scope).

Resources