Conditional Expressions in Angular's View: What if a model is missing? - angularjs

Supposed I've in controllers scope
$scope.user = {"username": "someUserName"};
and as you can see above there is no company information in it.
I want to show alternativ text in the view, if it is missing, like (Pseudo-Code):
{{ if "user.company" does not exist show "not set" may be with a certain style }}
Is there an Angular way to manage it elegantly?
Something like that would be great:
{{ user.company || 'Not set!'.class('danger') }}
In this case above I can find }} and replace it with || 'Not set!'.class('danger') }}
I am looking for a generic solution something like: "Show always a certain text with a certain style, if the corresponding model is missing" ?
Imagine you have to correct these values in 100 different views with different model values. What could you do?

You can use ng-if/ng-show/ng-hide
Like this
<div ng-if="!user.company">
Not set
</div>

I think it's more elegant do handle it with an expression.
Company: <div>{{user.company || 'Not Set'}}</div>

ng-if / ng-show / ng-hide should be used.
<div ng-if='!user.company'>Company not set</div>
<div ng-if='user.company'>{{user.company}}</div>
or
<div ng-hide='user.company'>Company not set</div>
<div ng-show='user.company'>{{user.company}}</div>
Note:
However if you pretend to change styles if a particular info exists/doesn't exist you can use ng-class. Example:
<div ng-class='{"no-company": !user.company}'>Company not set</div>
Than in css apply your styles:
.no-company {
background-color: red;
}
Bottom line:
If you want to show text, then use ng-if or ng-show/ng-hide.
If you want to change styles or apply effects using classes use ng-class.
Or you can mix the angular directives to achieve more complex results.

Related

What is the diff between using {{...}} and without {{...}} and angular directives?

Actually I'm confused between when to use {{ }} when using angular directives and when to not to use {{ }}
For example:
<div data-ng-init="isHidden=false">
<div data-ng-show="isHidden">
...
</div>
</div>
and
<div data-ng-init="isHidden=false">
<div data-ng-show="{{isHidden}}">
...
</div>
</div>
I'm confused between these syntax ? What are the differences between those? And when to use what? Thanks in advance :)
There is no difference except the "look" u need to use the {{value}} syntax in case you want to write data anywhere in your html body
<div>{{value}}</div>
It's all explained here: Difference between double and single curly brace in angular JS?
For quick answer:
{{}} are Angular expressions and come quite handy when you wish to
write stuff to HTML
Don't use these at a place that is already an expression!
For instance, the directive ngClick treats anything written in between
the quotes as an expression
<div data-ng-init="isHidden=false">
<div data-ng-show="isHidden">
...
</div>
</div>
In This Situation data-ng-show = false , Takes From data-ng-init As Statically,if You Have Given true Then It Returns True .
But Here
<div data-ng-init="isHidden=false">
<div data-ng-show="{{isHidden}}">
...
</div>
{{}} Called As Expressions In Angular One Of The Most Important Concept
It Directly Evaluate If isHidden = true Or False Based On Any Condition Written In Your App.js File .
Example:
<div data-ng-init="isHidden=YourVariable">
<div data-ng-show="{{isHidden}}">
...
</div>
if(YourVariable == true){
Do Somthing
}
else{
Do Something
}
If you are asking when to use {{}} while assigning value to a attribute and when not.
It depends on the binding types of directive. '#' or '='
So here, you have to use:
data-ng-show="{{isHidden}}" if the binding type of directive scope data-ng-show is '#', that mean the data-ng-show will be expecting a string value. So in this case if you keep data-ng-show="isHidden" it will take data-ng-show's value as 'isHidden', but data-ng-show="{{isHidden}}" will take the value of the $scope.isHidden and assign to data-ng-show.
Now if the binding type of directive scope data-ng-show is '=', that means the data-ng-show will be expecting a value from a scope. So data-ng-show="isHidden" itself will take the value of he $scope.isHidden and assign to data-ng-show.
Note: all the default HTML attributes expect a string so you have to use {{}} for default HTML attributes.
There is no as such major difference unless one uses them in the DOM for the value.
When one uses the following:
<div data-ng-show="isHidden">
then, expression is evaluated and on the basis of it respective value, the ng-show either hides or displays the div. But the value of the isHidden cannot be seen, when one inspects the HTML using the browser developer tool.
When one uses the following:
<div data-ng-show="{{isHidden}}">
In this case, the value of the isHidden can be seen from the developer tools, and the rest of the expression does evaluates the same as that of (1).

Set HTML as default value of undefined variable in Angular JS

I have a variable that could be potentially unset like this
{{salesperson || 'not set'}}
I want to add a CSS class to the not set area like this
{{salesperson || '<span class="error">- Not Set -</span>'}}
When I tried that it throws syntax errors.
HTML in expressions is escaped by angular, to avoid HTML/JS injections.
Use ng-class:
<span ng-class="{error: !salesperson}">{{salesperson || 'not set'}}</span>
Or simply ng-show/ng-hide:
<span ng-hide="salesperson" class="error">not set</span>
<span ng-show="salesperson">{{ salesperson }}</span>
So if you really, really want to inject HTML, look at this question: With ng-bind-html-unsafe removed, how do I inject HTML?
But if you just want to show an error if it's not defined:
{{salesperson}}<span ng-if="!salesperson" class="error ng-cloak">- Not Set -</span>'
It will only show the span if salesperson is undefined. Make sure to define your ng-cloak css as well to prevent any flickering.

Conditional rendering on angularjs

I used to work with backbone and handlebars and was able to do things like
{{ if condition }}
<div class="container">
{{ endif }}
more html
{{ if condition }}
</div>
{{ endif}}
I know I can use ng-if but I want to display conditionally an element opening and closing not the whole element.
You can write a directive and accomplish exactly what you want, but I think a better way to do things is to create a div(without conditions - since every open div element has to have a closing element) and to add dynamic content inside using ng-if:
<div>
<div ng-if="cond1"></div>
<div ng-if="cond2"></div>
<!-- ETC...... -->
</div>
The simplest and best way to do this by using "ng-if" attribute for any HTML element that you want to render on the basis of some condition that you can manupulate in your controller as per your need.
For example:
<div ng-if = "expression">
</div>
This expression should be evaluate to be boolean i.e. either true or false, here you can specify multiple condition also which should be evaluate to true or false.
For multiple condition example check out this fiddle:
http://jsfiddle.net/9Ymvt/820/
Check out this example in example section of "ngif doc":
https://docs.angularjs.org/api/ng/directive/ngIf

Is there a way to make AngularJS work with HTML-first?

Is there a way to have a HTML-view with pre-populated values from the server, and then get AngularJS to read those values into it's $scope?
I'm thinking of a scenario where the HTML is like this:
<div ng-controller="TestController">
<div ng-bind="title">Test Title</div>
<div ng-bind="itemCount">33</div>
<div ng-repeat="item in items">
<div ng-bind="item.title">Item 1 Title</div>
</div>
</div>
<button ng-click="update()">Update</button>
And the JavaScript is like this:
function TestController($scope) {
$scope.update = function() {
console.log($scope.title); // Should log "Test Title"
};
}
The thought behind this is to let the server render HTML that search engines can index, but have a JavaScript-model-representation of the content for manipulation through JS.
While ng-init is one solution, it requires you to explicitly set the value. So here is an alternative solution.
http://plnkr.co/edit/pq8yR9zVOHFI6IRU3Pvn?p=preview
Note : This solution wont work for ng-repeat. Control flow directives cant be used with this. But for simple extraction of information from ng-bind this works pretty well. All that you need to do is add the default directive ( code in plunk ) to wherever you are doing the bind and it will extract the text content and push it to the scope variable.
EDIT (solution with ng-repeat):
So, I was thinking of a way to make ng-repeat also work the same way. But getting ng-repeat to work like this isnt an easy job ( see the code for proof :P ). I have finally found a solution - here you go :
http://plnkr.co/edit/GEWhCNVMeNVaq9JA2Xm2?p=preview
There are a couple of things you need to know before you use this. This hasnt been thoroughly tested. It only works for repeating over arrays ( not objects ). There could be cases that have not been covered. I am overriding ngRepeat itself which could have other consequences. When you loop through the items ( in your server side code ) dont forget to add default="true" on the first element and default on the rest of the elements.
Hope this helps.
Add ng-init to your elements with the value so that it will work the way you want.
http://docs.angularjs.org/api/ng.directive:ngInit
I think what you really want is to make your application searchable by serving static files in parallell. Read more about it here http://www.yearofmoo.com/2012/11/angularjs-and-seo.html

What is the best way to conditionally apply attributes in AngularJS?

I need to be able to add for example "contenteditable" to elements, based on a boolean variable on scope.
Example use:
<h1 attrs="{'contenteditable=\"true\"': editMode}">{{content.title}}</h1>
Would result in contenteditable=true being added to the element if $scope.editMode was set to true.
Is there some easy way to implement this ng-class like attribute behavior? I'm considering writing a directive and sharing if not.
Edit:
I can see that there seems to be some similarities between my proposed attrs directive and ng-bind-attrs, but it was removed in 1.0.0.rc3, why so?
I am using the following to conditionally set the class attr when ng-class can't be used (for example when styling SVG):
ng-attr-class="{{someBoolean && 'class-when-true' || 'class-when-false' }}"
The same approach should work for other attribute types.
(I think you need to be on latest unstable Angular to use ng-attr-, I'm currently on 1.1.4)
You can prefix attributes with ng-attr to eval an Angular expression. When the result of the expressions undefined this removes the value from the attribute.
<a ng-attr-href="{{value || undefined}}">Hello World</a>
Will produce (when value is false)
<a ng-attr-href="{{value || undefined}}" href>Hello World</a>
So don't use false because that will produce the word "false" as the value.
<a ng-attr-href="{{value || false}}" href="false">Hello World</a>
When using this trick in a directive. The attributes for the directive will be false if they are missing a value.
For example, the above would be false.
function post($scope, $el, $attr) {
var url = $attr['href'] || false;
alert(url === false);
}
I got this working by hard setting the attribute. And controlling the attribute applicability using the boolean value for the attribute.
Here is the code snippet:
<div contenteditable="{{ condition ? 'true' : 'false'}}"></div>
In the latest version of Angular (1.1.5), they have included a conditional directive called ngIf. It is different from ngShow and ngHide in that the elements aren't hidden, but not included in the DOM at all. They are very useful for components which are costly to create but aren't used:
<h1 ng-if="editMode" contenteditable=true>{{content.title}}</h1>
To get an attribute to show a specific value based on a boolean check, or be omitted entirely if the boolean check failed, I used the following:
ng-attr-example="{{params.type == 'test' ? 'itWasTest' : undefined }}"
Example usage:
<div ng-attr-class="{{params.type == 'test' ? 'itWasTest' : undefined }}">
Would output <div class="itWasTest"> or <div> based on the value of params.type
<h1 ng-attr-contenteditable="{{isTrue || undefined }}">{{content.title}}</h1>
will produce when isTrue=true :
<h1 contenteditable="true">{{content.title}}</h1>
and when isTrue=false :
<h1>{{content.title}}</h1>
Regarding the accepted solution, the one posted by Ashley Davis, the method described still prints the attribute in the DOM, regardless of the fact that the value it has been assigned is undefined.
For example, on an input field setup with both an ng-model and a value attribute:
<input type="text" name="myInput" data-ng-attr-value="{{myValue}}" data-ng-model="myModel" />
Regardless of what's behind myValue, the value attribute still gets printed in the DOM, thus, interpreted. Ng-model then, becomes overridden.
A bit unpleasant, but using ng-if does the trick:
<input type="text" name="myInput" data-ng-if="value" data-ng-attr-value="{{myValue}}" data-ng-model="myModel" />
<input type="text" name="myInput" data-ng-if="!value" data-ng-model="myModel" />
I would recommend using a more detailed check inside the ng-if directives :)
Also you can use an expression like this:
<h1 ng-attr-contenteditable="{{ editMode ? true : false }}"></h1>
I actually wrote a patch to do this a few months ago (after someone asked about it in #angularjs on freenode).
It probably won't be merged, but it's very similar to ngClass: https://github.com/angular/angular.js/pull/4269
Whether it gets merged or not, the existing ng-attr-* stuff is probably suitable for your needs (as others have mentioned), although it might be a bit clunkier than the more ngClass-style functionality that you're suggesting.
For input field validation you can do:
<input ng-model="discount" type="number" ng-attr-max="{{discountType == '%' ? 100 : undefined}}">
This will apply the attribute max to 100 only if discountType is defined as %
Edit: This answer is related to Angular2+! Sorry, I missed the tag!
Original answer:
As for the very simple case when you only want to apply (or set) an attribute if a certain Input value was set, it's as easy as
<my-element [conditionalAttr]="optionalValue || false">
It's the same as:
<my-element [conditionalAttr]="optionalValue ? optionalValue : false">
(So optionalValue is applied if given otherwise the expression is false and attribute is not applied.)
Example: I had the case, where I let apply a color but also arbitrary styles, where the color attribute didn't work as it was already set (even if the #Input() color wasn't given):
#Component({
selector: "rb-icon",
styleUrls: ["icon.component.scss"],
template: "<span class="ic-{{icon}}" [style.color]="color==color" [ngStyle]="styleObj" ></span>",
})
export class IconComponent {
#Input() icon: string;
#Input() color: string;
#Input() styles: string;
private styleObj: object;
...
}
So, "style.color" was only set, when the color attribute was there, otherwise the color attribute in the "styles" string could be used.
Of course, this could also be achieved with
[style.color]="color"
and
#Input color: (string | boolean) = false;
Was able to get this working:
ng-attr-aria-current="{{::item.isSelected==true ? 'page' : undefined}}"
The nice thing here is that if item.isSelected is false then the attribute simply isn't rendered.
Just in case you need solution for Angular 2 then its simple, use property binding like below, e.g. you want to make input read only conditionally, then add in square braces the attrbute followed by = sign and expression.
<input [readonly]="mode=='VIEW'">

Resources