i'm using ng-repeat to display some array values (messages). How do i get the corresponding DOM element after pushing it to the messages array?
I want to add and remove a class(es) from the added dom element.
Thank you,
Bernhard
The Angular way is not to directly manipulate the DOM but to use the directives provided to you by the framework. To change the CSS classes applied to an element, use ngClass, ngClassEven, and ngClassOdd.
<div ng-repeat="item in items">
<span ng-class="{active: isActive(item)}">{{item}}</span>
</div>
The parameter to ngClass is a JavaScript object which keys are the names of the CSS classes, and values are expressions. For each key-value pair, if the expression results in true, the class is added, otherwise it is removed.
ngClassEven and ngClassOdd can be used to apply classes only to even or odd elements of the repetition.
Related
I'm trying to go with the best approach and avoid unnecessary rendering/processing time in my AngularJS app when choosing between 2 directives to be displayed in the page inside an ngRepeat loop, want to know which is the best way:
If by setting the ng-if directly in the directive html element, like:
<div ng-repeat="element in list">
<my-directive-a ng-if="someFunction(element)"></my-directive-a>
<my-directive-b ng-if="!someFunction(element)"></my-directive-b>
</div>
Or by moving out the first <div> from the directive's template and use it as a wrapper for each directive. For instance:
<div ng-repeat="element in list">
<div ng-if="someFunction(element)">
<my-directive-a></my-directive-a>
</div>
<div ng-if="!someFunction(element)">
<my-directive-b></my-directive-b>
</div>
</div>
NOTE: The starting <div> element on each directive could be modified behave the same so I will basically take that out of the directive's html and moving it outside the directive declaration in order to place the ng-if there
What would be the best approach for this case? Are there any performance implications from doing it one way or another? Or is it just the same thing? Consider that the number of elements in the list could get really big.
They are quite the same, but you can improve performance with one-time binding, but only when element does not change at runtime (for example, let's say that it has property name, and your someFunction is like return element.name === 'John'). Angular just stop observing this function when it returns value, and watches will be deleted. There are 2 prerequisites to use this solution:
Elements properties in list does not change (if you rely on them in someFunction), for example if you rely on name property name must not change, because watcher on someFunction is note available.
When list changes or its elements properties change, you reload all list (for example, you fetch it from server again if you know that change occurred)
What you get with this? There is no watches after my-directives are drawn on ng-ifs, and when something changes, new reference is bound to list (for example, it comes from server) and everything will be redrawn, ng-ifs will run again and when will become stable (function returns value) then will be unbound. How it looks like? Like this:
<div ng-repeat="element in list">
<div ng-if="::(someFunction(element))">
<my-directive-a></my-directive-a>
</div>
<div ng-if="::(!someFunction(element))">
<my-directive-b></my-directive-b>
</div>
</div>
Two colons before expression. But be aware, that with one-time binding it's easy to mess up - you need to be sure that you test your code enough to be sure it works.
I would like to achieve the following (which does not work as written):
<div ng-repeat="product in products">{{product.headline}}</div>
<div>{{product.text}}</div>
I only first see all headlines, then all texts below each other. How can refer to product again on an additional div element?
There is a nifty feature in Angular whereby some directives, like ng-repeat, support spanning across multiple elements:
<div ng-repeat-start="product in products">{{product.headline}}</div>
<div ng-repeat-end>{{product.text}}</div>
You can also create custom directives that support this functionality. Here's a snippet from Angular docs:
multiElement
When this property is set to true, the HTML compiler will collect DOM nodes between nodes with the attributes directive-name-start and directive-name-end, and group them together as the directive elements. It is recommended that this feature be used on directives which are not strictly behavioural (such as ngClick), and which do not manipulate or replace child nodes (such as ngInclude).
I am trying to create an angular-js directive that encapsules editing a list of items: It should iterate over a list, repeating the wrapped element of the directive for every item and add some controls to each line (deleting elements, moving elements around, ...). Ideally I would like to use the directive like this:
<edit-list list="mylist">
<input ng-model="item.name">
<input ng-model="item.location">
</edit-list>
where the directives template should be like this (as demonstration I added a button for deleting items):
<div ng-repeat="item in list track by $index">
<span ng-transclude></span><button ng-click="delete_item($index)">
</div>
However, I can't get this to work. I tried to adapt this answer which manages to repeat the wrapped element over a list, however the plunker does not work anymore with modern angularjs versions (see my forked plunker which changes only the version of angularjs).
Nevertheless, here is a plunker tries to implement what I want to accomplish, but failes: It seems like the transcluded element can't see the item anymore. This makes sense given the documentation for ng-transclude, but I could not figure out how to make the wrapped element see the scope of the directive again.
I've updated your plnkr here http://plnkr.co/edit/GZsfp0HaeAVg5NjMBtGC?p=preview and made some changes:
Your goal is to ng-repeat over a list. There is no need for ng-transclude since you already binding your list to your directive.
I removed the reference to ng-transclude in your directive
I amended your delete method to pluck the proper item from the list.
I am creating a directive in which template I need to use the a scope's variable value as the name of the directive (or alternatively controller) to load.
Say I have a directive widget that has a template called widget.html which looks like:
<div class="widget widget.type" {{widget.type}} ng-controller="widget.type">
<div class="navBar">
<div ng-include="widget.type + '-t.html'"></div>
<i class="fa fa-close"></i>
<hr>
</div>
<div ng-include="widget.type + '-f.html'"></div>
</div>
Now widget.type is not getting evaluated in the first line. It works fine for ng-include. Say widget.type's value is weather. The first line should then be interpolated first to look like (doesn't matter if class attribute, widget.type-attr or ng-controller is interpolated)
<div class="widget" weather>
and then compiled to include the weather directive.
How can I get widget.type interpolated in the template?
Not an option is to use ng-include to load the directive. I need to use one common template for the widget and want to add/override/extend the base directive with additonal functionality/Variables.
If this is not the way to achieve that, is there a way to extend a directive the OOP-way?
See the plunkr
You can only place interpolation expressions in text nodes and attribute values. AngularJS evaluates your template by first turning it into DOM and then invoking directive compilation, etc. If you try to place {{...}} instead of attribute name, you'll just end up with messed-up DOM.
If you really need to replace a whole directive based on $scope variable value, you'll need to create a directive for application of other directives and do some heavy lifting with $compile (you'll have to completely re-compile the template each time the value changes). I'd recommend trying to find other designs solving your situation before attempting this.
For adjusting your template based on element attributes, see this answer.
I would like to tile elements that get in to ng-repeat (set up their css based on the filtered list)
Ideally I would like to use html markup like this:
<ul>
<li ng-repeat="row in rows">
<h6>{{row.name}}</h6>
<ul>
<li ng-repeat="item in items|filter:row" tile>{{ item.name }}</li>
</ul>
</li>
</ul>
Is it possible for me to access the list that is being passed to ng-repeat? (any other way than AngularJS - how to get an ngRepeat filtered result reference)
Can I somehow transclude the items, so the tile directive would actually apply css (I want to abstract ng-style="{top:getTop(item),left:getLeft(item)}" etc.)
Is it possible for me to access the list that is being passed to ng-repeat? (any other way than AngularJS - how to get an ngRepeat filtered result reference)
I think you'll need to do what you saw in the link you referenced:
ng-repeat="item in (filteredItems = (items|filter:row))" tile>
Assuming your tile directive does not create a new scope, it will share the scope that ng-repeat creates (one scope per item). So the filteredItems property will be available to your directive.
Can I somehow transclude the items, so the tile directive would actually apply CSS (I want to abstract ng-style="{top:getTop(item),left:getLeft(item)}" etc.)
Your tile directive has access to the li element on which it is defined. So you can simply call, e.g., element.addClass(...).