How do I create a conditional directive in Angular? - angularjs

I'm trying to define a directive in Angular which takes a DOM like this:
<example-directive href="{{ foo }}"><img src="{{ bar }}"></example-directive>
... and transforms it to ...
<img src="BAR_VALUE">
... but only if the href is defined. If {{ foo }} is empty, it should transform to
<img src="BAR_VALUE">
It also needs to respond appropriately when the value of {{ foo }} and {{ bar }} changes. I can't figure out the documentation sufficiently to manage it. How do you do it?

Since the conditional show/hide doesn't work for you, what you need is variable templates within the same directive: https://coderwall.com/p/mgtrkg
This way you will have one directive and you will select between two templates depending on the existence or not of the href.

I dont see a need to use a directive here. Just use ngIf:
<a ng-if="foo != ''" href="{{foo}}"><img src="{{ bar }}"></a>
<img ng-if="foo == ''" src="{{ bar }}">
ngIf is a newer directive. Check for the supporting versions.

UPDATE: Ok I misunderstood the problem. See if i got it right:
I would have two css classes:
.vis { display: normal; }
.hid { display : none; }
Then your directive could have a html like this
<span ng-show="foo != ''"><img src="{{ bar }}" /></span>
<span ng-show="foo == ''"><img src="{{ bar }}" /></span>
It's a bit of a hacky workaround because of the nature of your html (deleting only a wrapping element makes it tricky), but it should work.

Related

Why does ng-click not fire functions when inside ng-repeat, in AngularJS?

I am working on a small AngularJS application. In one of the views, I have replaced some hard-coded html with data coming from a JSON file that I iterate through:
<class="actions-list">
<div ng-repeat="item in $ctrl.myCustomService.config.items"
ng-class="{'disabled': !item.isEanabled}"
class="actions-item"
ng-click="$ctrl.selectAction('{{item.action}}')">
{{item.name | translate }}
</div>
</div>
The problem is that, since this replacement, the function fired by ng-click, that used to be (hard-coded) ng-click="$ctrl.selectAction('register'); and so on, does not work anymore.
Why does that happen? How can I fix the problem?
You don't need quotes or {{ }} inside ng-click:
<class="actions-list">
<div ng-repeat="item in $ctrl.myCustomService.config.items"
ng-class="{'disabled': !item.isEanabled}"
class="actions-item"
ng-click="$ctrl.selectAction(item.action)">
{{item.name | translate }}
</div>

ng-click - hide all other div that isn't this $index within ng-repeat

What i'm trying to do is similiar to an accordion.
Simple put
i have an ng-repeat with an <a> tag that once clicked show another div called "Printpanel" nested inside it within ng-show.
If the user cick to another <a> tag, i want to hide all other div showed before, and open only to that one related.
I am using $index to trigger the specific div.
Here what i have done:
<div ng-repeat="product in $ctrl.Products">
<a href="#" ng-click="showDetails = $index;>CONFIGURE</a>
<div class="Printpanel ng-hide" ng-show="showDetails == $index" ng-hide="showDetails != $index">
</div>
it seems that ng-hide is not recognized... Anybody have an idea?
You don't need to use ngShow + ngHide: one is enough.
<div class="Printpanel ng-hide" ng-show="showDetails == $index">
You can use ng-if also:
<div class="Printpanel" ng-if="showDetails == $index">
EDIT:
due to scope inheritance problem, you are not able to set showDetails variable. use $parent for that.
working example:
<div ng-repeat="product in $ctrl.Products">
CONFIGURE
<div class="Printpanel" ng-if="$parent.showDetails == $index"> </div>
</div>
seems you have missed closing double quotes here ng-click="showDetails = $index;
Also either of one you need to use ng-show/ng-hide/ng-if = expression

Recursive template with ng-include and onload not rendering anything

I'm trying to create a recursive template in AngularJS. I've read quite some blog posts and SO posts about it, but cannot get my code to work.
I'm trying to make a recursive template to render simple nested expressions with variables and operators. In this case, the nodes in the data tree can be one of multiple types and each type is rendered differently. I try to address this with one Angular template, ng-switch and ng-include.
The template:
<script type="text/ng-template" id="expression.tpl">
<ng-switch on="expression.type">
<span ng-switch-when="two-sided-operator">
<ng-include src="'expression.tpl'" ng-init="expression = expression.left"></ng-include>
{{ expression.operator }}
<ng-include src="'expression.tpl'" ng-init="expression = expression.right"></ng-include>
</span>
<span ng-switch-when="variable"> {{ expression.name }} </span>
<span ng-switch-when="literal"> "{{ expression.value }}"</span>
</ng-switch>
</script>
<ng-include src="'expression.tpl'" onload="expression=testExpression"></ng-include>
The data:
$scope.testExpression = {
type: "two-sided-operator",
left: {
type: "variable",
name: "foo"
},
operator: "==",
right: {
type: "literal",
value: "bar"
}
};
This however does not render anything. If I remove the internal ng-includes, it does render at least expression.operator.
Can anyone tell me what I am doing wrong? Could there be an issue with the assignment to expression in the ng-init statement?
Thanks for reading this far!
Finally got this to work by using a hack of ng-repeat. The working template:
<script type="text/ng-template" id="expression.tpl">
<ng-switch on="expression.type">
<span ng-switch-when="two-sided-operator">
<span ng-repeat="expression in [expression.left]" ng-include="'expression.tpl'"></span>
{{ expression.operator }}
<span ng-repeat="expression in [expression.right]" ng-include="'expression.tpl'"></span>
</span>
<span ng-switch-when="variable"> {{ expression.name }} </span>
<span ng-switch-when="literal"> "{{ expression.value }}"</span>
</ng-switch>
</script>
I don't understand ng-include deep enough to explain why it did not work though :)

Do I need to put AngularJS in the <head> if I want to hide {{ xxx }} from showing on my page?

I have HTML like this:
<div id="top"
ng-hide="app.stateService.displayModal">
<div>{{ app.userService.data.name }}</div>
</div>
// Body HTML here. No images are loaded. Just Divs
<script src="Scripts/jquery-2.1.1.min.js"></script>
<script src="/Scripts/angular.js"></script>
Now the page briefly shows {{ app.userService.data.name }}.
If I want this to not show then do I have to have AngularJS in the head of my document? The reason I placed AngularJS at the end was because I wanted to have the page appear as quickly as possible.
Can someone advise me about this and also tell me how I can make the {{ xxx }} hidden when the page first loads up.
You could use ng-cloak to hide any elements until it get compiled.
<div id="top" ng-cloak
ng-hide="app.stateService.displayModal">
<div>{{ app.userService.data.name }}</div>
</div>
The CSS rules to hide the elements with ng-cloak will be added automatically by angular.js.
If that isn't fast enough you could add the css rules yourself at the head:
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
Or using the angular-csp.css file.
Also see: ngCloak documentation
you can do it in two ways -
ng-bind
OR
ng-cloak
{{ app.userService.data.name }}
Either way, you {{}} won't show up. no need to put angular.js inside head.
This is the role of the ng-cloak directive used as a css class.
Check doc: https://docs.angularjs.org/api/ng/directive/ngCloak
You better use ng-bind
<span ng-bind="app.userService.data.name"></span>
instead of {{app.userService.data.name}}.
This will avoid that flickering.

How to conditionally parse expressions with angular?

I have a ng-repeat, which shows conditional sections of markup. However all the expression in the markup are still being parsed and therefore throwing exceptions when they are undefined.
E.G: if item.discriminator is VoteOnNodeRcNotification, the other ng-show will still parse, and for example {{ item.nodeCommentText }} won't exist
i.e: (inside the ng-repeat
<div ng-show="item.discriminator == 'VoteOnNodeRcNotification'" class="type-specific">
<a class="outer-link self-clear" ng-href="{{ nodeLink(item.nodeId, item.nodeText) }}">
<small class="rep-change-mag" ng-class="getAdjustedCss(item.amountAdjusted)">
{{ friendlyAmountAdjusted(item, item.amountAdjusted) }}
</small>
<p class="rep-change-assoc">
Edit accepted: {{ item.nodeText }}
</p>
</a>
</div>
<div ng-show="item.discriminator == 'VoteOnNodeCommentRcNotification'" class="type-specific">
<a class="outer-link self-clear" ng-href="{{ nodeLink(item.nodeCommentId, item.nodeCommentText) }}">
<small class="rep-change-mag" ng-class="getAdjustedCss(item.amountAdjusted)">
{{ friendlyAmountAdjusted(item, item.amountAdjusted) }}
</small>
<p class="rep-change-assoc">
Vote up: {{ item.nodeCommentText }}
</p>
</a>
</div>
You can use ''ng-if'' for that. Just like ng-show, but the content is not generated when the condition is falsy.
ng-show just shows/hides the element, so it still exists in the DOM and therefore will always be evaluated. You can either set a default value for item.discriminator in your controller. For example..
$scope.item = {};
$scope.item.discriminator = null;
null is still falsy and therefore will be hidden by ng-show, but won't throw an error because it is actually defined. Another options is to use ng-if instead of ng-show, which actually removes the element from the DOM when the expression is falsy

Resources