How to get object value in Angular when brackets conflict? - javascript

I have a weird situation: due to our Django backend, we changed our frontend Angular braces to brackets, but now I can't access an object value dynamically, for example:
<div>[[ house.basement ]]</div>
<div>[[ house.attic ]]</div>
The above works, but below would not:
<div>[[ house[floor] ]]</div>
Is there a straight-forward solution here? I can use a function to look for the floor, but it'd be much less efficient.
Here is my simplified controller:
var MyCtrl = function($scope) {
$scope.floor = 'basement';
$scope.house = {
'basement': 'boo';
'attic': 'yay';
};
};
There's a radio button that controls floor model, but I don't think that's too relevant here.
EDIT
So based on comments, I was not being clear, I want to access the value of $scope.house based on how $scope.floor changes. This works perfectly fine in normal angular:
<div>{{ house[floor] }}</div>
And it would display 'boo'. The problem is because we changed braces to brackets, the interpreter breaks down and we're not sure how to escape properly.

Use ng-bind.
<div ng-bind='house[floor]'></div>
http://plnkr.co/edit/diL34NCmoUPcd3AZZ3jb?p=info

Related

What makes an Angular.JS directive require curly braces around it?

I've been doing a codeschool tutorial on Angular.JS and one section in particular confused me. Here's the code snippet I'm referring to:
<section class="tab" ng-controller="TabController as tabs">
<ul class="nav nav-pills">
<li ng-class="{active:tabs.isSet(1)}">
<a href ng-click="tabs.setTab(1)">Description</a></li>
<li ng-class="{active:tabs.isSet(2)}">
<a href ng-click="tabs.setTab(2)">Specs</a></li>
<li ng-class="{active:tabs.isSet(3)}">
<a href ng-click="tabs.setTab(3)">Reviews</a></li>
</ul>
</section>
This line in particular needs curly braces in the directive to be correct by CodeSchool standards:
<li ng-class="{active:tabs.isSet(1)}">
My question is, how come this other line's directive comes through correctly without the need of curly braces:
<a href ng-click="tabs.setTab(1)">Description</a></li>
To me, it would seem that since I'm accessing the function tabs.setTab() I would need to wrap it in the same way as before. Can anyone explain why this is the case?
Here's the TabController JS code by the way for reference:
app.controller('TabController', function(){
this.tab = 1;
this.setTab = function(newValue){
this.tab = newValue;
};
this.isSet = function(tabName){
return this.tab === tabName;
};
});
First of all, you should understand that these conventions are completely arbitrary. They were decided by the angular authors. They could have easily used some other character, like an exclamation point for example...
ng-class="!active: true, otherClass: false!"
These values are not code. They are just strings (plain text). There is some javascript in angular that parses these strings and makes sense of them. It loops over every character and looks out for certain key characters, like curly braces.
That being said, there is a reason they chose the characters they did. They are trying to mimic javascript in html. The values passed to ng-click represent a function call in javascript. Not coincidentally, this is how you call a function in javascript...
tabs.setTab(1)
The value passed to ng-class, however, is not mimicking a function call. It is mimicking an object. This is how you declare an object in javascript...
{ active: tabs.isSet(1) }
This represents an object with a key of active and a value of tabs.isSet(1), which evaluates to a boolean telling angular whether that class should be applied
It depends on the directive you're using. If you're just trying to set a boolean, a function call or a single boolean controller parameter might suffice. Other directives might be more complicated and have different parameter requirements. You'll find out when you start making your own directives.
In the case of ngClass, it is because you can set multiple classes at once, conditionally. There are several ways of doing this, but the syntax is fairly clear (documentation).
This is an example from the docs:
<p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
Here's the related plunkr: https://plnkr.co/edit/?p=preview

How to use Angular structural directive with multiple inputs

I want to implement something similar with angular-permisssion. And with requirement to control the element's existance, I need to use angular structural directive.
At the beginning, i think such syntax would work:
<h2 *permissionIf [permissionIfExcept]="'Read'">Except</h2>
However, it doesn't work that way.
Moreover, the offical guide only teach you how to write custom structural directive with single input.
With multi-inputs, some third-party tutorials involve a bit. But that's using the angular template micro-syntax to achieve data binding. Then one problem occurs:
template syntax doesn't support pure key-value inputs:
<h2 *permissionIf="except: map.except;only: 'test'">Except</h2>
It expands into this(which is illegal):
<h2 template="permissionIf except: map.except;only: 'test'">Except</h2>
A stupid temporary solution is add a useless variable declaration.
<h2 *permissionIf="let i;except: map.except;only: 'test'">Except</h2>
Another inconvenient way is to use template element to wrap the code.
<template permissionIf [permissionIfExcept]="'Read'">
<h2>Except</h2>
</template>
The above all are not accepetable enough. But I can't find a bette way to resolve it.
Hope some guys can give some suggestion:).
The input names need all to be prefixed with the selector of the directive, followed by the input name capitalized (i.e. permissionIfExcept). Example:
#Directive({
selector: '[permissionIf]'
})
export class PermissionIfDirective implements AfterContentInit {
private _permissionIf:string[];
#Input()
set permissionIf(value: string[]) {
this._permissionIf=value;
console.log('permissionIf: ', value);
}
private _except:string;
#Input()
set permissionIfExcept(value: string) {
this._except = value;
console.log('except: ', value);
}
}
To use them with the '*' syntax:
<div *permissionIf="permissions;except:'Read'"></div>
Note here you're using the name following the prefix uncapitalized (i.e. except). Also note the : in the assignment.
The explicit syntax (using template) would look like this:
<template [permissionIf]="permissions" [permissionIfExcept]="'Read'">
</div></div>
</template>
but with <ng-container> it could look like
<ng-container *permissionIf="permissions;except:'Read'">
<div></div>
</ng-container>
Plunker example
See also the source of NgFor as an example.
#Günter Zöchbauer answer is almost correct.
Actually right now to make his answer working you need to explicitly rename the secondary #Input name.
So it should be:
#Input("permissionIfExcept")
set permissionIfExcept(value: string) {
this._except = value;
console.log('except: ', value);
}
Then one problem occurs: template syntax doesn't support pure key-value inputs:
True
A stupid temporary solution is add a useless variable declaration.
I think you are using this in a way it was not meant to be.
From the docs:
The microsyntax parser title-cases all directives and prefixes them with the directive's attribute name, such as ngFor. For example, the ngFor input properties, of and trackBy, become ngForOf and ngForTrackBy, respectively. That's how the directive learns that the list is heroes and the track-by function is trackById.
https://angular.io/guide/structural-directives#microsyntax-examples
Bottom line is in the context of your question, the microsyntax accepts "expression", followed by optional "keyed expression"s and I'm afraid those are your only options.
One could of course pass an object as the first expression—similar to ngIf—, the difference being you can teach your directive how to evaluate the expression:
*permissionIf="{ only: 'whatever', except: ['things', 'stuff'] }"

AngularJS Get current factory/service name

Ola!
I was just playing around with angular's factories and services and I just noticed
that I can't get the current name of the factory/service.
At least I couldn't get any resources about that.
For example so its a bit more clear I have a factory, like this;
.factory('GuessImAFactory', [function() {
var factoryName = this.yadayada.name; //<- This actually doesn't work,
//if you haven't guessed
//<--Some other code goes here-->
return something;
}])
So the question does anyone know the trick how to get the name of it?
I don't think this is possible. In callback function you can't get actual instance of factory since you are defining it right in that place. In other works, it's not yet created.
However, simple workaround for this would be to hard code it.. Since you probably won't have dynamic factories, you could just simply say
var factoryName = 'GuessImAFactory';
and use it if needed.
Try this one: factoryObject.__proto__.constructor.name

`ng-model` of AngularStrap `bs-typeahead` not available in scope?

I am running into an issue with the value of ng-model, applied to an element using AngularStrap's bs-typeahead, is not accessible within scope. It is however viable from a {{ var }} within the HTML.
I have the following HTML:
<input type="text" placeholder="add a destination" ng-options="item as item for item in modelTypeahead" ng-model="selectedDestination" bs-typeahead data-template="templates/SrcDstTypeaheadTemplate.html">
I initialize the variable in my controller:
$scope.selectedDestination = "";
Placing a {{ selectedDestination }} elsewhere within the HTML works as expected.
However, when I do a console.log($scope.selectedDestination); within my controller it comes out as an empty string.
If I update my initialization to be something, for example:
$scope.selectedDestination = "abc123";
... both the <input> and the {{ selectedDestination }} update appropriately. My console.log will also spit out the set value. However, if I update the typeahead the {{ selectDestination }} will update but my console.log will spit out 'abc123' still.
Is there a scope issue that I am missing? I don't understand how {{ selectedDestination }} is putting out the correct string but the console.log is putting out something different. It would almost seem my binding is one-way, but AngularStrap's bs-typeahead should be two-way (per all the examples).
Where are you doing console.log? You would have to make sure that the value has changed before you do that for the value to show up, you could do:
$scope.watch('selectedDestination', function() {
console.log($scope.selectedDestination);
});
I'm not sure if you're still running into any issues with this, but I found a solution to the exact same problem when I ran into it just recently. I'm almost positive that its a scope issue or apply failure within the AngularStrap stuff, but I wouldn't know where to start looking.
Really, I'm not educated enough to give you the exact reasons that this works, but this is what you do:
(1) You change the variable to an object.
when you put the model and watch on an object instead of a top layer variable, it works better through layers of directives. Don't ask me why....
(2) Use a deep watch on the object you just created.
When you change it to an object, you need to use a deep watch on the variable or the $apply and $digest won't pick up any changes. This is because by default the value will be checked for "reference" equality instead of "value" equality. This breaks because the object's "reference" doesn't change, only its values. But be careful using this deep comparison because the extra effort can cause a lot of overhead.
Here's an example in use with AngularStrap's typeahead:
$scope.selectedDestination = {};
~~~
<input type="text" placeholder="add a destination" ng-options="item as item for item in modelTypeahead" ng-model="selectedDestination.destination" bs-typeahead data-template="templates/SrcDstTypeaheadTemplate.html">
~~~
$scope.$watch('selectedDestination', function(value) {
console.log('selectedDestination', $scope.subComponent);
}, true); //here we need to tell the watch to do a deep watch
EDIT I've traced my issues back to a few things, but part of it was the $render function. I'll keep looking into it. Good luck!

How do I get the current child-scope of a ngRepeated element in AngularJS?

This seems like a simple question, but I've been googling around for a while and can't seem to find it.
In my JS I have something called parseTags(book) that takes a JSON comma-separated list of tags (book.tags) and parses it into an array:
$scope.parseTags = function(book){
book.tags = book.tags.split(',');
};
In my HTML I have something like this:
<div ng-repeat="book in books" ng-init="parseTags(book)">{{book.title}}</div>
Is there a way just to get the child scope from within the $scope.parseTags function? Instead of passing in book each time?
Something like:
$scope.parseTags = function($childScope){
$childScope.tags = $childScope.tags.split(',');
}
<div ng-repeat="book in books" ng-init="parseTags()">{{book.title}}</div>
parseTags function is executed in context of the current child scope. So parseTags can also be written as:
$scope.parseTags = function() {
this.book.tags = this.book.tags.split(',');
};
Demo: http://plnkr.co/edit/DUkDVOMjjj0khh5KCYo7?p=preview
you should only use ng-init for special cases when you need to use a property of the ng-repeat, I think you will better doing this kind of functionality in the controller. Unless there is a really specific reason you can't do that. I haven't seen your use of tags in the html, but looks like the kind of functionality a filter would do.
From angularjs docs:
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.
source: angularjs docs

Categories

Resources