AngularJS Protractor: select div with ng-show directive - angularjs

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]'));

Related

Why can't I use interpolation with a directive?

I have searched and found a few articles that address "How does one assign a scoped variable to the value of an html attribute tag". See:
https://docs.angularjs.org/guide/interpolation
How to assign angularjs variable value to html element attribute, such as input elememnt's name attribute
In my controller I have defined the following:
$scope.reportFields = "{'summary.imageID':'Image ID' }";
in my html document I have defined the following:
A TEST EXAMPLE
<button ng-json-eport-excel title="CSV" class="bt btn-md btn-success" separator="," data="mdcData" report-fields="{{reportFields}}" filename="'mySearch'">
when I bring the page into the browser I find that the page reports a parse syntax error on defining {{reportFields}} for my button element.
Error: [$parse:syntax] Syntax error: Token '{' invlaid key at column 2 of the expression [{{reportFields}}] starting at [reportFields}}].
if I forgo using a variable and hardcode the value as in:
<button ng-json-eport-excel title="CSV" class="bt btn-md btn-success" separator="," data="mdcData" report-fields="{'summary.imageID':'Image ID' }" filename="'mySearch'">
it compiles and works correctly. If I add the 'ng-if' it compiles correctly as seen below:
<button ng-json-eport-excel title="CSV" class="bt btn-md btn-success" separator="," data="mdcData" report-fields="{{reportFields}}" ng-if="reportFields.length > 0" filename="'mySearch'">
Furthermore when I examine the generated HTML via the inspector I can see that for the 'ahref' tag that angularjs's interpolation has translated 'reportFields' to '"{'summary.imageID':'Image ID' }"' for the ahref tag but it hasn't translate the value for the report-fields tag specified on the button. I have also tried:
ng-if="1==1" filename="'mySearch'">
for my expression which should always evaluate to true. My questions (and I am new to angularjs and I need help understanding) are:
Why do I get a syntax parse error for using the double curly angles with the second HTML element (button) but not the first (href)??
Why does the parse error go away with the 'ng-if' statement
Why doesn't the interpolation happen in the case for
report-fields="{{reportFields}}"
but occurs just fine for:
A TEST EXAMPLE
Thanks for your help in advance.
Pete
What you probably want is:
<a ng-href="reportFields">A TEST EXAMPLE</a>
Although this doesn't make any sense either given the way you've defined reportFields. Notice the ng-href and lack of interpolation. As it is, you are assigning the reportFields expression to the regular HTML attribute href which is not interpreted by Angular.
Angular treats your HTML like a template rather than trying to render it like a browser would. It takes that template and turns it into browser-readable DOM HTML. This helps explain the behavior you're asking about.
Answers to your questions:
Interpolation (curly braces) is actually a directive with special syntax which the parser has to evaluate along with any other directives such as ng-show, ng-repeat, custom directives, etc. There is no guarantee that an interpolation directive will be processed before another directive that tries to bind to it. And in fact, those other directives such as report-fields will get the raw interpolation markup and not the intended data. This is what throws the error. See the last paragraph in the interpolation documentation
Why mixing interpolation and expressions is bad practice
Since href is not actually a directive-- it's an attribute-- Angular doesn't attempt to treat it as a directive or bind it to the interpolation, so no error is thrown. It just outputs the expected result to the DOM: href="parsed value".
ng-if has no special assistance for this issue except it does get parsed before your report-fields directive and then cancels further parsing on the element if false. If you don't get an error in that situation, it's likely only because the if condition evaluated to false and the report-fields directive was never parsed. (Can't see all your code to confirm.)
The interpolation doesn't happen in the error case because the error is thrown before it has a chance to. Again, this is because it gets processed after other directive linking. (I'm not sure why they made that design choice though I imagine there is a good reason.)

ng-include not recognizing appended version number

I appended version number to all of my templates under routeProvider so that it will load the latest templates every time the version number changes. It works just fine until I noticed that there is still one more template that needs to be appended, and that`s the sidebar-menu that was found on ng-include. I appended version number to it using this code
<div ng-include = "'views/sidebar-menu.html?v={{VERSION_NO}}'"></div>
I successfully changed the version number, however, it is still not reflecting my latest changes to the menu template. Any idea why this doesn`t work?
Reason it didn't worked is you put variable name inside single quotes so it will be considered as string .
Try:
<div ng-include = "'views/sidebar-menu.html?v='+VERSION_NO"></div>
This will parse your variable and give you required version.You don't need paranthesis {{ }} as it evaluates expression, hence will evaluate your variable automatically
ng-include takes an expression, so you don't need interpolation syntax {{. Instead, you can set the full string on the scope.
$scope.sidebarTemplate = 'views/sidebar-menu.html?v=' + VERSION_NO;
Now you can do <div ng-include="sidebarTemplate">

AngularJS: two ng-if or one interpolation?

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)

Is it possible to use multiple lines in Angular attributes?

Is is possible to include a newline in an Angular JS expression in an attribute tag? Something like this:
<p ng-repeat="
foo in foos
| filter:{attr: 'something really long'}
| orderBy:bar">
{{foo}}
</p>
EDIT: To clarify, the above doesn't work. I was wondering if there is some other syntax that allows for breaking this kind of expressions into multiple lines.
angular.js parser would be able to handle it, but there's a quick regex check before handing it to the parser (see http://docs.angularjs.org/error/ngRepeat:iexp):
Be aware, the ngRepeat directive parses the expression using a regex
before sending collection and optionally id to the AngularJS parser.
This error comes from the regex parsing.
I filed a bug to loosen this restriction: https://github.com/angular/angular.js/issues/5537, you can hand-patch it in the meantime, it's just 1 character: m (/regex/m).
It is possible for an expressions to span multiple lines. But ng-repeat throws an error if you try to span the expression on multiple lines.
Take a look at this plunker:
Add a new line in the ng-repeat expression and open the browser console to see the error message.
http://plnkr.co/edit/E1O8Iy3VzL3kzj72BDUL?p=preview
Yes, it is possible to use multiline attributes with ANY HTML element, including AngularJS directives.

Conditional outer tag in a directive (i.e. <strong>)

I like a directive that conditionally puts a tag outside some content (but always prints the content), like this:
<p><strong ng-if-always-keep-inner-content="model.condition">{{model.text}}</strong>/p>
so if condition is true I get
<p><strong>yada yada</strong></p>
otherwise I get
<p>yada yada</p>
I could write it myself, but I want to know if it is possible to do with built in directives/options.
I should perhaps say this is used together with Bootstrap, which afaiu recommends using <strong> vs some class with a bold font.
I don't think there is a built in directive. You should write it.
I suggest to use a classic ng-if
<p ng-if="model.condition"><strong>{{model.text}}</strong></p>
<p ng-if="!model.condition">{{model.text}}</p>
In your specific case, you can also use ng-class and set the strong style via css.

Resources