Angular UI Bootstrap tabset with ng-repeat - angularjs

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>

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

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.

Why do I get "Unknown element 'pagination' or...." angular/ui.bootstrap pagination

I'm using pagination (angular/ui.bootstrap) for my list. The pagination control shows on my site but in VS it says "Unknown element 'pagination' or element cannot be placed here". http://i.imgur.com/CRbTfBZ.png
I've placed the pagination tag right after my table (ng-repeat), and inside my ng-controller div.
I'm using thsese cdns
ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js
angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js
netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css
EDIT: Dont know why, but it works even if VS throws an warning.
Because that is Angular Custom Directive, so VS will think that it's not a valid element.
Looking at the angular-ui source code, pagination accept restrict 'EA'. So if you really don't want to see the warning, you can try to do something like this :
<div data-pagination></div>
For more info about restrict option:
https://docs.angularjs.org/guide/directive

Angular ui-select2 - hide initial drawing/rendering of component

I'm evaluating using Angular with ui-select2 and wondered whether someone could provide help on hiding the flicker/conversion of the <select> into the select2 component.
It's only very brief, but users can see the component change styles.
Is there a way of hiding it until the select has been modified by select2?
I came across the same problem and I had a look at the source code. The directive is deliberately initialised later by using a timeout. In the code there is a comment saying "Initialize the plugin late so that the injected DOM does not disrupt the template compiler".
My solution (you can see it in this jsplunker: http://plnkr.co/edit/fXjDxs?p=preview ) is to set the visibility of the select tag to "hidden".
<select ui-select2 ng-model="....." style="visibility: hidden; ">......
When the component is loaded that tag is replaced with a div. In ui-select2.js I have added a line (line 208) to set its visibility to "visible".
elm.prev().css({"visibility": "visible"});

Hide Angular brackets until javascript loaded

I have a few bits of HTML like
<p class="noresults">{{numberOfContacts}} Results Are Available</p>
Is it possible for me to hide {{numberOfContacts}} until Angular has loaded? So it would just say Results Are Available
I've seem some solutions such as hiding the entire body until Angular has loaded, but I'd rather not do that if possible.
Yes, use ng-cloak. Simply add class="ng-cloak" or ng-cloak to an element like this
Using directive <div ng-cloak></div>
Using class <div class="ng-cloak"></div>
It's simply a set of CSS rules with display: none !important and as Angular has rendered your DOM it removes the ng-cloak so an element is visible.
use <span ng-bind="numberOfContacts" /> instead of {{numberOfContacts}}
Sometimes, even if I used the ng-cloak, I could still see the braces for a few seconds. Adding the following style resolved my issue:
[ng-cloak]
{
display: none !important;
}
Please see this link link for more explanation.
Hope it helps :D
This is typically only an issue when working with complex content on really slow devices. In those instances, there can be a brief moment when the browser displays the HTML in the document while AngularJS is parsing the HTML, getting ready, and processing the directives. In this interval of time, any inline template expressions you have defined will be visible to the user. Most devices nowadays have pretty good browsers which are quick enough to prevent this from being an issue. There are two ways to solve the problem.
Avoid using inline template expressions and stick with ng-bind directive.
(Best) Use the ng-cloak directive which will hide the content until Angular has finished processing it. Basically, the ng-cloak directive uses CSS to hide the elements and angular removes the CSS class when the content has been processed, ensuring that the user never sees the {{ and }} characters of a template expression.
One strategy to consider is using the ng-cloak directly to the body element, which will ensure that the user will see an empty browser while AngularJS loads. However, you can be more specific by applying it to parts of the document where there are inline expressions.
I have seen issues with ng-cloak not working when added to an element. In the past, I have worked around this issue by simply adding ng-cloak class to element.
You can use ng-bind instead of expression like
<span ng-bind="data"></span>

Resources