Bootstrap dropdown header using ngRepeat - angularjs

I want to populate a BootStrap dropdown using ngRepeat. I can populate the items just fine but what if I want to have one header per iteration?
<ul class="dropdown-menu">
<li class="dropdown-header">Dropdown header 1</li>
<li ng-repeat="order in orders">{{order.name}}</li>
</ul>
With this I can get only one header understandably.

Use ngRepeatStart and ngRepeatEnd
<li ng-repeat-start="order in orders" class="dropdown-header">Dropdown header {{$index}}</li>
<li ng-repeat-end="order in orders">{{order.name}}</li>

You can use ng-repeat-start and ng-repeat-end.
<ul class="dropdown-menu">
<li ng-repeat-start="order in orders" class="dropdown-header">Dropdown header {{$index}}</li>
<li ng-repeat-end>{{order.name}}</li>
</ul>
You also don't have to write: ng-repeat-end='order in orders'. you can just mention ng-repeat-end
Read more about it here: ng-repeat start and end

You didn't specify, but I assume you're using the jQuery library that goes along with it. I would highly suggest using Angular UI Bootstrap instead. They've taken care of this for you. I'm sure there are other components you'd like to use as well.
https://angular-ui.github.io/bootstrap/#/dropdown
Update I now realize I misunderstood your question, but I would still take a look at the Angular UI Boostrap library if you're not using it already.

Related

How to process an array (or manually process ng-repeat)?

Using: AngularJS v1.3.15
Disclaimer: I know virtually nothing about angularjs. But I'm "forced" to use it because its being used in a framework that I am using.
I want to modify some html/angularjs that looks like this:
<ul>
<li ng-repeat="provider in model.externalProviders">
<a class="pure-button" href="{{provider.href}}">{{provider.text}}</a>
</li>
</ul>
I can see what is going on here... ng-repeat causes an iteration on the elements of the model.externalProviders collection/array. It works fine, but I have no control over content/styling individual <a> elements depending on the provider. I would like to change the content/appearance of the <a> element depending on type.
The relevant part of the model looks like this:
"externalProviders": [
{
"type": "Google",
"text": "Sign-in with Google",
"href": "https://localhost:44302/external?provider=Google&signin=04e029cf1018403f1757b097fbfb1ecb"
}
],
So I thought maybe there is a way to "select" or "pick" from externalProviders by type... If that type exists, then render the appropriate markup, e.g.:
<ul>
<!-- if model.externalProviders has item with type=="Google"... -->
<li>
<a class="pure-button button.google" href="<i class="fab fa-google"></i>{{provider.href}}">{{provider.text}}</a>
</li>
<!-- if model.externalProviders has item with type=="Facebook"... -->
<li>
<a class="pure-button button.facebook" href="<i class="fab fa-facebook"></i>{{provider.href}}">{{provider.text}}</a>
</li>
</ul>
Not sure what the proper search terms would be so I had trouble finding any info that might solve my problem. Is something like this possible with AngularJS? If so, how would I accomplish it?
As #Major Sam commented, the ngClass might work for less simple scenarios, but I don't even need to go that far. Luckily I have control over the type property and the css, so I can make my type and css selector match the font awesome icon class selector for the icon. This works:
<li ng-repeat="provider in model.externalProviders">
<a class="pure-button button-{{provider.type}}" href="{{provider.href}}"><i class="fab fa-{{provider.type}}"></i>{{provider.text}}</a>
</li>
Drawbacks: Doesn't allow you to change the actual markup (like, e.g., not include the icon if font awesome didn't have one for that provider).

How to insert "Dynamic Content" in uib-accordion developed with ng-repeat?

I have created an uib-accordion in my angular project, where all the uib-accordion-group elements have been created under ng-repeat(for efficiency ofcourse).
<uib-accordion close-others="oneAtATime">
<ul ng-model="some_code">
<li ng-repeat="some_code">
<div uib-accordion-group heading="{{some_code}}">
{{some_code}}
</div>
</li>
</ul>
</uib-accordion>
The problem is I want to insert different and dynamic content in the uib-accordion-group(s) but being in ng-repeat I can't use template-url. I have almost 6 rows to be displayed. Should I fall back to developing each accordion row individually(which would increase code size, hence I don't want) or do we have any such provision in uib-accordion (which maybe I am currently not aware of)?
Suggestions please.
try to use ng-class and change the styles dynamically
The html code you want to put in each accordion. Are you getting it dynamically or you have the code you just want to put different code on different condition.

Angular Directive with Replace=false creates list html that is non semantic

We are thinking about moving to directives that use replace=false, as I hear replace=true is being deprecated. One problem I cant figure out how to fix, is for all directives that are based on lists, the resultant html is not semantic.
For example a list directive might be something like:
<tg-list tg-compact="true">
<tg-list-item>foo</tg-list-item>
<tg-list-item>bar</tg-list-item>
</tg-list>
this will currently (with replace=true) compile to:
<ul class="o-list o-list--compact">
<li class="o-list-item">
foo
</li>
<li class="o-list-item">
bar
</li>
</ul>
with replace=false we will end up with either:
<tg-list class="o-list-icon o-list-icon--compact">
<ul>
<tg-list-item>
<li class="o-list-item">foo</li>
</tg-list-item>
<tg-list-item>
<li class="o-list-item">bar</li>
</tg-list-item>
</ul>
</tg-list>
or:
<tg-list class="o-list-icon o-list-icon--compact">
<tg-list-item>foo</tg-list-item>
<tg-list-item>bar</tg-list-item>
</tg-list>
The first is bad because it is definitely non semantic, the second is bad because although it is "semantic" (as in html5, custom tags are ok), we lose anything that ul>li would have given us (a screen reader, for example, may have special ways to treat them).
Perhaps we can use the html5 'role' attributes? But if so i think the list item would need add the role to the element in the link function?
Dunno, but how have you lot solved this?
You can configure directives to have restrict set to A which stands for attribute. That way you can use it as:
<ul tg-list tg-list-compact=true>
<li tg-list-item>
foo
</li>
<li tg-list-item>
bar
</li>
</ul>
So what I did in the end is make the list-item wrap itself in an "li" in its link function.
The list-item link:
public link = ($scope: angular.IScope, $element: angular.IAugmentedJQuery) => {
$element.wrap('<li></li>'); }
Pretty simple in the end, but makes the html much more symantic so i am happy. Shout out to Sander for the help

Static list item inside ngRepeat

So,
I am rendering a basic list using ngRepeat, but need the first <li> to be static content, rather than being generated from the data array:
e.g.
<ul ng-repeat="item in items">
<li>This is text from the template and will always be the same.</li>
<li>{{item}}</li>
</ul>
I've also looked at using ng-repeat-start, but just can't quite get to a solution.
It is a common misconception that people want to use ng-repeat on the parent element, when in fact you use it on the actual elements that do the repeating.
You just need to change it to:
<ul>
<li>This is text from the template and will always be the same.</li>
<li ng-repeat="item in items">{{item}}</li>
</ul>

correct strategy to generate html dynamically with angular

I have a huge JSON object tree with two levels. First level has around 500 elements, and each element contains an average of 100 child elements.
I want to display the first level of the tree and I am doing it with a simple ng-repeat. When the user clicks on the element I want to display the child elements of that element. If I use a span ng-switch or a ng-show to show/hide child elements when the page first renders it freezes for around 10 seconds while generating all the HTML.
It doesn't sound like the right solution. There must be a different way of doing it, but I can't figure out. Anyone knows?
I have explained most in my comment, and here is a working plunker:
http://plunker.co/edit/RSZwfLlsCJ68MUkACbdp?p=preview
the new ng-if directive will do what you want
<h1>ng-if</h1> <h5>Click on the level to expand</h5>
<div class="well">
<ul class="nav nav-list" ng-repeat="(attr,element) in tree">
<li ng-click="expand=!expand" ng-class="{'active':expand}"><a>{{element.name}}</a></li>
<ul ng-if="expand" class="nav nav-list">
<li ng-repeat="item in element.items">{{item.name}}</li>
</ul>
</ul>
</div>
you can also do this in the "old-way" with ng-show using new ternary operator or its alternative expr && if_true || if_false
<h1>old-way</h1> <h5>Click on the level to expand</h5>
<small>use ternary operator or <pre>expand && element.items || []</pre></small>
<div class="well">
<ul class="nav nav-list" ng-repeat="(attr,element) in tree">
<li ng-click="expand=!expand" ng-class="{'active':expand}"><a>{{element.name}}</a></li>
<ul ng-show="expand" class="nav nav-list">
<li ng-repeat="item in (expand ? element.items : [])">{{item.name}}</li>
<!--<li ng-repeat="item in (expand && element.items || [])">{{item.name}}</li>-->
</ul>
</ul>
</div>
See this answer on ng-repeat performance. Essentially, it just takes a long time since Angular's ng-repeat, and basically all other directives, are set up to always look for updates in the whole JSON structure. So if you have lots of data and don't need live updates in the HTML view when changing the JSON, I wouldn't recommend using AngularJS. Generally, AngularJS performance also depends a lot on the browser and its JavaScript engine.
Alternatively, you could divide your JSON into subparts and then use pagination to display it.
I would recommend to fetch the data gradually from the server. Use server-side pagination and retrieve only the fields that you are going to display. Then, when a user clicks on one of the first level items, you can do another XHR to the server with the new data. I had similar requirements for a project and that solved the latency issue.
Regards,
Agustin.

Resources