Why can't I use interpolation with a directive? - angularjs

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.)

Related

Angular UI Bootstrap tabset with ng-repeat

I'm trying to upgrade my version of bootstrap-ui from 0.14.x to the latest 1.3.2 and I'm encountering some issues regarding the uib-tabset / uib-tab directives.
What I'm trying to do is dynamically create tabs using ng-repeat and have the 'active' tab be handled by expressions or properties of my repeat model.
<uib-tabset type="pills" active="{{activeItem.Id}}" >
<uib-tab class="arrow_box"
ng-repeat="item in myObject.myCollection"
ng-click="SetActiveItem(item)" id="{{$index}}"
index="{{item.Id}}">
The index="{{item.Id}}" binding does not work at all. So I can't seem to set my tab indexes via an expression, which wouldn't be a problem if I could get the uib-tabset to use the active property once the ng-repeat was complete.
activeItem is a property on $scope of the enclosing controller.
Adding this binding results in an error:
Error: [$parse:syntax] Syntax Error: Token '{' invalid key at column 2 of the expression [{{item.Id}}] starting at [{item.Id}}].
If I omit everything (the index attribute on uib-tab and active attribute on uib-tabset) it doesn't throw any errors but it also doesn't select any tabs by default, meaning I need to click one to activate that tab. Even though the documentation states that the defaults are "defaulting to the first tab".
Any reason ng-repeat no longer works properly with this directive set? I'm probably missing something here but I'm stumped.
Thanks
Edit:
Here is a plunkr link showing the issue I'm having.
https://plnkr.co/edit/DWOILq?p=preview
First I tried a lot to fix it but then I decided to search in google. I found this link .
Your problem is a known problem and will not be fixed. "uib-tab won't toggle active class if uib-tab index is set to dynamic key". You have to take some different approach like use of '$index'.
After trying out a few more things I realized I made a mistake and did not have to include the brackets for the expression for either binding (active or index).
It just didn't seem like they were being evaluated but they actually are.
Here is the code that should work:
activeItemId being a property on the parent controller.
<uib-tabset type="pills" active="activeItemId">
<uib-tab class="arrow_box"
ng-repeat="item in myObject.myCollection"
ng-click="SetActiveItem(item)"
index="item.Id">
</uib-tab>
</uib-tabset>

Seemingly unable to access content generated in the link() function while attempting to unit test an angular directive

I am working on an enterprise project, and as a part of that project we have a directive for creating form fields with appropriate document markup and validation/error handling. As an example, consider something like the following:
<form name="myForm">
<my-form-field label="My Field">
<input type="text" name="myField" id="myField" ng-model="myField" ng-required="true" />
</my-form-field>
</form>
The directive works exactly as expected, properly surrounding my input field with the common/expected markup, adding appropriate event handlers, etc.
One key piece of functionality is a convenience feature where the directive "notices" that the child form field has an attribute value for ng-required of true (either directly, as in this example, or because it is bound to something which is evaluated). This attribute does the "normal" angular thing (hooking into field-level validation in the angular workflow), but then my directive also notices it's presence in the child element, and appends " *" to the label value when taking the label attribute of the directive element and creating an HTML as part of the markup which is generated.
When attempting to unit test this in jasmine/karma, I see the logic in the template() portion of my directive firing (e.g., the tag is generated), but not the items in the link() portion (e.g., adding the asterisk to the label text, updating the for="" attribute of the label tag to match the id="" attribute of the child tag).
After spending the morning researching various demos/tutorials on unit testing angular directives, I found an example which didn't help me much...but did offer a smaller/more basic example of what I am seeing: http://plnkr.co/edit/tirhLwFEXLKSzukbsW1q?p=preview
In this example, seeing the attribute values set by the link() function of the direction is easily handled (see the expectations at lines 23-25), but the rendered html still doesn't seem to be compiled (outputting element.html() anywhere in that it() still shows the angular variables - like {{values.center}} - instead of the compiled values). I recognize that I could put computed values on the scope and then follow this pattern of validating by checking the directive's scope...but in my case, I am further modifying the html within the link() portion (quite successfully actually, despite what the unit tests appear to be showing).
What am I missing in terms of being able to "see"/validate the compiled HTML (e.g., validate that an asterisk was added to the label text when appropriate)? I thought about doing all of my markup work in the template() portion of my directive, but then I can't see how I could inspect the child input element (to get the id for the for="" attribute of the label, and to check if it is required or not so that I know if the asterisk is appropriate for the label text).
EDIT:
- This plunkr is most advanced of my three scenarios, and fails (basically a copy of my code, including trying to combine all the angular template generation/compilation into one common function, based on an example I had seen in a few different places): http://plnkr.co/edit/AxGAUKYFvm2yf1vPsWD3?p=preview
In the third plunkr (see my comment below for the first two, each of which works as expected), the error thrown is actually "TypeError: currentSpec.queue is undefined...", which is likely my culprit - but I can't seem to find a way around it. I actually wasn't seeing this error locally...because I had seen some advice in a few places to change this method within angular-mocks.js:
isSpecRunning = function() {
//return currentSpec && (window.mocha || currentSpec.queue.running);
return !!currentSpec;
};
The second line is the original, which was commented out/replaced with the third line. This removes the error shown in the Plunk, and instead my unit test runs just fine...except for it doesn't seem to show the final compiled version (e.g. the label's for attribute is never populated, and the asterisk is not added to the label text when appropriate). So with the suggested change, my test runs ok - but fails to see the
"final" markup which I see when I actually run my site. Without the change to angular-mock.js, I see the error you see in the plunkr.

How to write attribute without value in JSF?

I am developing AngularJS application in JSF 2. Since, my files are in XHTML format, the following syntax are not accepted:
<div ui-view/>
The compiler complains that ui-view should have a value assigned to. However, it seems AngularJS treat differently if ui-view is assigned with value.
In fact, ui-view is just one example. There are some other cases where attribute values are not expected by angularJS.
How to code this in JSF? I guess I should switch to HTML5, instead of XHTML? How to do so?
Try like this
<div ui-view=""/>

Double curly braces in angularjs avoid w3c validation

I have an html page that uses the following line of code:
... <a href="nolf://{{server.ip}}:{{server.hostport}}/"> ...
When I try to validate the page, I got the following error:
"Bad value nolf://{{server.ip}}:{{server.hostport}}/ for attribute href on element a: Illegal character in scheme data: not a URL code point."
It seems that the validator does not like seeing the curly braces in a URL link. If I remove them, all goes well. They are "unsafe" so they need to be encoded but if I encode them, the AngularJS instructions do not work anymore.
Is there a workaround, a way, to validate this? (original page where this code is can be found here: http://oneoakway.com/nolf/servers/ - there is a validation link in the footer)
Use ng-href instead. This will properly apply the expression to a href attribute.

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.

Resources