In the following code sample why are the brackets necessary around position[0].position in the ng-click directive in the anchor element but not in the ng-show directive in the divs?
<div ng-controller="PlayersController as pl">
<section ng-init="tab = 'goalkeepers'">
<li ng-repeat="position in pl.players">
<a href ng-click="tab = {{position[0].position}}">{{position[0].position}}</a>
</li>
</section>
<div ng-repeat="position in pl.players">
<div ng-repeat='player in position' ng-show="tab === position[0].position">
<h2 ng-show='$first'>{{player.position}}</h2>
<h3>{{player.name}}</h3>
<h4>{{player.price | currency: '£': 0}} {{player.score}}</h4>
</div>
</div>
</div>
Does it have to do with setting equality versus checking for equality? Is it related to the nested ng-repeat?
When I add brackets around the equality check in ng-show in the div element I get a parse error, why?
In Angular expressions need to be within the curly-brace bindings, where as
Angular directives do not.
As we understand that ng-click is a directive you don't need to add curly-brace there.
You don't need the brackets in the ng-click attribute. Angular evals the value of the attribute so just ng-click="tab = position[0].position;"
Related
I'm new to AngularJS. I'm developing a page with a couple of similar blocks but I don't really want to use ng-repeat for those. The thing is, in their ng-click, ng-class and other directives I need to use some variable identifying the block. Is there a way to set it once in parent div and then use in children? Something like this:
<div ng-var="Potatoes">
<button ng-click="buy($parent.var)">Buy</button>
<span>This is a {{$parent.var}}</span>
<img ng-class="{{$parent.var}}">
</div>
<div ng-var="Tomatoes">
<button ng-click="buy($parent.var)">Buy</button>
<span>This is a {{$parent.var}}</span>
<img ng-class="{{$parent.var}}">
</div>
I would use ng-repeat over a specifically crafted object but in this case I have to manually position some other things that I omitted in the code above so ng-repeat is not an option, unfortunately.
You can still use 'ng-repeat' then use of '$index' can differentiate each loop.
I've created a simple directive in Angular which generates a scroller to display some products.
I'm having an issue with one part of the code.
<ul ng-style="{'margin-left':{{currentMargin}}+'px'}">
<li ng-repeat="tyre in tyres" ng-style="{'width':{{liWidth}}+'px'}">
<div class="imageContainer"><img src="../Images/eutl/{{tyre.image}}"/></div>
<div class="details">
<h3>{{tyre.name}}</h3>
About this tire
</div>
</li>
</ul>
and this is what it looks like in the browser once executed
<ul ng-style="{'margin-left':0+'px'}">
<!-- ngRepeat: tyre in tyres -->
<li ng-repeat="tyre in tyres" ng-style="{'width':265+'px'}" class="ng-scope" style="width: 265px;">
<div class="imageContainer"><img src="../Images/eutl/tire.jpg"></div>
<div class="details">
<h3 class="ng-binding">Fuel Efficient</h3>
About this tire
</div>
</li>
<!-- end ngRepeat: tyre in tyres --></ul>
after executing this on my page I get the scroller and the ng-style inside the "li" elements gets displayed correctly, while the ng-style for the "ul" doesn't.
I've tried multiple solutions, even trying to add the same exact ng-style from the "li" element and it just doesn't get processed and no style is added.
Can anyone help me by pointing out a mistake in my markup or a possible cause for one ng-style working on the Li elements and the other not working on the UL itself?
The other problem I'm having is that the value of the currentMargin is not updating in IE8/9 and so on.
Thanks
ng-style accepts an Angular expression that evaluates to an object. This means that if you want to use a variable inside that expression, you can use it directly (without the double-curlies):
ng-style="{width: liWidth + 'px'}"
Double-curlies are used when you want to insert a dynamic, interpolated value to an argument that accepts a string, like <img alt="{{product.name}} logo">. You then put an expression inside those brackets.
Try to do :
ng-style="{'width':liWidth+'px'}">
No curly bracket, a lot of ng directive don't like it
I am opening and closing some side (open and close under the title) using angular and I'm wondering if there is a way I can swap between classes When I'm doing this too so I can swap some css. Here's what I'm doing -
<form name="metaDeta" id="lessonDetails" class="lessonItem" ng-controller="detailsController">
<div class="small-12 columns">
<div class="lesonHead lessonHeadOpen saDetailsHead" ng-click="showDetails = ! showDetails" ng-class="myVar">
<h5>Lesson Details</h5>
</div>
<div class="lessonSASlider" ng-show="showDetails">
This works fine for opening and closing the form, however there is a class .lessonHeadOpen that I would like to try and toggle between .lessonHeadClosed. So basically I am looking for something like an addClass/removeClass even on click to toggle between the 2 classes on the element when the user opens and closes it. Is this possible with angular? Could I work off what I have or do I have to re-structure. Thanks!
You can use ng-class to dynamically add classes based on the result of expressions. Read ngClass
<div class="lessonHead" ng-click="showDetails = ! showDetails">
<h5>Lesson Details</h5>
</div>
<div ng-class="{className: showDetails}">
</div>
This directive will evaluate showDetails expression and if true, it will apply the class className
Update:
If I understand correctly, do the same but reverse the expression so if not showdetails add one class and then when show details is true it will be removed and the other class added. <div ng-class="{classOne: showDetails , classTwo: !showDetails}"> SEE FIDDLE
You can use ng-class for toggling.
Simply add a condition for a class to appear and it will based on the condition.
ng-class="{'className': shouldShowClass}"
You can use ng-class's ternary operator notation:
<div ng-class="showDetails? 'lessonHeadOpen': 'lessonHeadClosed'>...</div>
SAMPLE DEMO
plunker
i need to include a directive as a class:
and i need to use this pattern:
ng-class='{"some-directive":1'}
but unfortunately the directive can only be registered in the pattern below (try it in the plunker link above):
class='some-directive'
The main problem is I want to register(include) the directive only to the "rename" option of my context menu:
<li ng-class="{'renameDirective':(menu.name == 'Rename')}" ng-repeat="menu in contextmenu
How would I achieve that?.. what's tougher is that I want to add an argument in renameDirective.. maybe like this :
ng-class="{'renameDirective: directiveArg':(menu.name == 'Rename')}"
is something like this: <div renameDirective="directiveArg"></div>
UPDATE:
As a wordy workaround, the code below can temporarily solve the issue. It is open for more improvements (I guess taking advantage of the ng-class directive is best, shortest/ cleanest approach).
<ul>
<li ng-repeat="menu in contextmenu">
<div ng-switch on="menu">
<div ng-switch-when="Rename">
<button some-directive="Rename">button{{$index}}</button>
</div>
<div ng-switch-default>
<button>button{{$index}}</button>
</div>
</div>
</li>
</ul>
I had this semi-duplicate post as my reference here
<button class="{{menu.name=='Rename' && 'some-directive'}}">button2</button>
I have an angular template which looks like this...
<div ng-repeat="message in data.messages" ng-class="message.type">
<div class="info">
<div class="type"></div>
<div class="from">From Avatar</div>
<div class="createdBy">Created By Avatar</div>
<div class="arrowTo">
<div class="arrow"></div>
<div class="to">To Avatar</div>
</div>
<div class="date">
<div class="day">25</div>
<div class="month">Dec</div>
</div>
</div>
<div class="main">
<div class="content">
<div class="heading2">{{message.title}}</div>
<div ng-bind-html="message.content"></div>
</div>
</div>
<br />
<hr />
<br />
</div>
I have set up a JSfiddle to show the data being bound.
What I need to do is make the "from", "to" and "arrowTo" divs show conditionally, depending on the content of the data.
The log is is this...
If there is a "from" object in the data then show the "from" div and bind the data but don't show the "createdBy" div .
If there is no "from" object but there is a "createdBy" object then show the "createdBy" div and bind the data.
If there is a "to" object in the data then show the "arrowTo" div and bind it's data.
Or in plain English, if there is a from address, show it, otherwise show who created the record instead and if there is a to address then show that too.
I have looked into using ng-switch but I think I'd have to add extra markup which would leave an empty div if there was no data. Plus I'd need to nest switch directives and I'm not sure if that would work.
Any ideas?
UPDATE:
If I were to write my own directive (If I knew how!) then here is some pseudo code to show how I would want to use it...
<div ng-if="showFrom()">
From Template Goes Here
</div>
<div ng-if="showCreatedBy()">
CreatedBy Template Goes Here
</div>
<div ng-if="showTo()">
To Template Goes Here
</div>
Each of these would disappear if the function/expression evaluated to false.
Angular 1.1.5 introduced the ng-if directive. That's the best solution for this particular problem. If you are using an older version of Angular, consider using angular-ui's ui-if directive.
If you arrived here looking for answers to the general question of "conditional logic in templates" also consider:
1.1.5 also introduced a ternary operator
ng-switch can be used to conditionally add/remove elements from the DOM
see also How do I conditionally apply CSS styles in AngularJS?
Original answer:
Here is a not-so-great "ng-if" directive:
myApp.directive('ngIf', function() {
return {
link: function(scope, element, attrs) {
if(scope.$eval(attrs.ngIf)) {
// remove '<div ng-if...></div>'
element.replaceWith(element.children())
} else {
element.replaceWith(' ')
}
}
}
});
that allows for this HTML syntax:
<div ng-repeat="message in data.messages" ng-class="message.type">
<hr>
<div ng-if="showFrom(message)">
<div>From: {{message.from.name}}</div>
</div>
<div ng-if="showCreatedBy(message)">
<div>Created by: {{message.createdBy.name}}</div>
</div>
<div ng-if="showTo(message)">
<div>To: {{message.to.name}}</div>
</div>
</div>
Fiddle.
replaceWith() is used to remove unneeded content from the DOM.
Also, as I mentioned on Google+, ng-style can probably be used to conditionally load background images, should you want to use ng-show instead of a custom directive. (For the benefit of other readers, Jon stated on Google+: "both methods use ng-show which I'm trying to avoid because it uses display:none and leaves extra markup in the DOM. This is a particular problem in this scenario because the hidden element will have a background image which will still be loaded in most browsers."). See also How do I conditionally apply CSS styles in AngularJS?
The angular-ui ui-if directive watches for changes to the if condition/expression. Mine doesn't. So, while my simple implementation will update the view correctly if the model changes such that it only affects the template output, it won't update the view correctly if the condition/expression answer changes.
E.g., if the value of a from.name changes in the model, the view will update. But if you delete $scope.data.messages[0].from, the from name will be removed from the view, but the template will not be removed from the view because the if-condition/expression is not being watched.
You could use the ngSwitch directive:
<div ng-switch on="selection" >
<div ng-switch-when="settings">Settings Div</div>
<span ng-switch-when="home">Home Span</span>
<span ng-switch-default>default</span>
</div>
If you don't want the DOM to be loaded with empty divs, you need to create your custom directive using $http to load the (sub)templates and $compile to inject it in the DOM when a certain condition has reached.
This is just an (untested) example. It can and should be optimized:
HTML:
<conditional-template ng-model="element" template-url1="path/to/partial1" template-url2="path/to/partial2"></div>
Directive:
app.directive('conditionalTemplate', function($http, $compile) {
return {
restrict: 'E',
require: '^ngModel',
link: function(sope, element, attrs, ctrl) {
// get template with $http
// check model via ctrl.$viewValue
// compile with $compile
// replace element with element.replaceWith()
}
};
});
You can use ng-show on every div element in the loop. Is this what you've wanted: http://jsfiddle.net/pGwRu/2/ ?
<div class="from" ng-show="message.from">From: {{message.from.name}}</div>