passing paths in an angularjs directive - javascript

I have a directive which i'd like to pass a path
<mydirective api="/my/tomcat/server?asd=123&efg=456">
However I get "Lexer Error: Unexpected next character". I assume some encoding needs to take place. Can someone advice?

I'm not sure why you are getting the lexer error without seeing your code, if you could update the post, we may be able to tell why that happens. In the meantime, there are a few ways to retrieve an attribute value in your directive. Not all will work with a string literal.
1) The # isolate scope binding: This parses the value and will work as is with your HTML. The parse actually happens later so this value will not be immediately available in the directive's internal methods (i.e. link, etc.) and is best used in the directive's template method. Ex.:
scope: {
api: '#'
}
2) The = isolate scope binding: This will work if you wrap your expression in single quotes because it evaluates the attribute as an expression. Ex.:
scope: {
api: '='
}
And in your HTML (notice the single quotes):
<mydirective api="'/my/tomcat/server?asd=123&efg=456'">
3) Attribute Evaluation: This allows you to evaluate the attribute string value directly from the directive's internal methods and will work as is with your HTML. Ex:
link: function(scope,element,attrs){
console.log(attrs.api);
}
You can read more about directives in the AngularJS directive doc here.

Angular try to evaluate the expression in the api attribute, you have to surround it with quotes :
<mydirective api="'/my/tomcat/server?asd=123&efg=456'">

Related

AngularJS custom attribute does not evaluate first time directive runs

HTML
<a custom-attr='{{ controller.object.value }}' data-ng-model='controller.object.value'>
Angular directive
.directive('customAttr', function () {
return {
require: 'ngModel',
controller: 'ControllerName',
controllerAs: 'cName',
link: function (scope, el, attr, ctrl) {
el.on('click', function ($event) {
if (ctrl.$viewValue && attr.customAttr) { // breakpoint
}
})
}
}
})
Goal:
to see the correct value in attr.customAttr the first time the directive runs.
Description
Stopping at a breakpoint on the if statement inside of the directive's link function, I expect to see a boolean value. I have verified the boolean value is correct in the model using $log.log(). Unfortunately, the first time the directive runs, attr.customAttr evaluates to a string of the reference to the model value ('controller.object.value' in the debugger), and then on subsequent iterations of the directive it evaluates correctly to the boolean. I tried removing the curly braces from the attribute, and I just get an unchanging empty string.
What can I do that will cause the model value to evaluate correctly the first time?
Note: I have done a similar version of this before with a numeric value without problem. The key difference appears to be that the working version is on an input element, and has both ngModel and ngValue attributes.
Consider using
attrs.$observe('customAttr', function() {
scope.customAttr= scope.$eval(attrs.customAttr);
});
to properly double bind .attrs (and thus, probably resolve your issue ) to your directive.
More info in this answer and in the docs.
As it turns out the controller and controllerAs properties were having an effect on this. I removed it, but then decided I would rather have the controller so instead I used isolate scope to evaluate the object instead of reading it from the attr property.

Difference between expressions and interpolation markup with embedded expressions in angular

"Angular directive attributes take either expressions or interpolation markup with embedded expressions. It is considered bad practice to embed interpolation markup inside an expression".
I couldn't understand the difference between expressions and interpolation markup with embedded expressions. Please can someone explain ? I am new to angular.I checked the documentation but couldn't find the difference.
An expression would be the "myscope" in ng-model="myscope"
And you can also do custom attributes like my-att="{{myscope}}"
edit: these expressions in curly braces are interpolated, meaning not the expression itself but its value gets passed into the directive. Your directive will thereby not have direct access to the scope property you used for the interpolation.
But you should not mix the two like ng-model="my{{scope}}"
I think that is what it refers to. This will often not work, since the scope is not yet initialized when the directive is parsed
Expression refers to Angular expression that may or may not contain double curly braces whereas interpolation markup means code inside this curly braces.
Expression {{exp}}: Something that turns into some value.
Characteristics:
It is processed by angular.
Angular expression are tied to the scope they are in. It has access to the properties of $scope.
Doesn't throws error if it has type error.
Control flow function(e.g., 'If', 'else' statement etc) not allowed.
Accept filter or a filter chain to format the output.
Interpolation (process): Process of replacing placeholders in a string with values.
Characteristics:
In Angular, these placeholders are usually expressions.
Result is automatically updated when placeholder changes. If $scope.message changes so will interpolation result will change.
Ex:
My name is {{ message }}
It is interpolated to: My name is Harry Potter

AngularJS - ngModel cannot be assigned to function

My question is very common one. I saw many different cases of people with similar question to mine, but I still does not have my answer.
[My goals]
I want to implement a standalone directive to encapsulate div consisting of label(span) and value(input). There are some specifics that worth mentioning here:
My directive aims to preserve the value of the "input" into my page controller score which is an ancestor of the scope my directive works with. Hence with the proper amount of ".$parent" I can access the desired scope for my ngModel assignment.
My directive does not know the name of the placeholder it shall use to preserve the user input as. This placeholder is coming as an HTML property to my directive.
[My approach]
I am using the directive compile phase to manipulate the directive's HTML (mainly adding properties to directive's template HTML). In the pre-link phase I want manipulate the scope that directive is using in order to add the directive's placeholder to my desired scope and assign it some default value if needed. (I guess I can achieve that with ng-init instead of messing with the scopes, but i believe that is irrelevant to my question).
[What I did]
As I explained above I have a function (determinePlaceholderName()) that returns a string which is the exact value I want to assign to ngModel. For example:
$parent.$parent.$parent.placeHolderName
I want to assign that function to input's ng-model attribute assuming that Angular will treath it as an expression, evaluate it and produce what I expected. Here is how I assign the ng-model property:
compile: function(cElem, cAttrs) {
//Some non relevant code here ....
$(cElem[0]).find("div input").attr("ng-model", "determinePlaceholderName()");
}
Basically what I expect is this to be treated like:
$(cElem[0]).find("div input").attr("ng-model", "$parent.$parent.$parent.placeHolderName");
[About determinePlaceholderName()]
This function I define in the link function of my directive:
link: function ($scope) {
$scope.constructValuePlaceholderName = function() {
//Some logic here.
return result;
}
[My Problem]
And finally what my problem is.
Angular result in an error explaining to me that
<input ng-model="myFunc()">
is non-assignable expression as documented.
[My Question]
How can I dinamically set ngModel if function assignment is forbidden by Angular??
Thank you for your time!
I hope this is what you're looking for:
http://plnkr.co/edit/TojgLNx3TUbmszhfs8Ui?p=preview
Using isolated scope, you can pass the name of the model, and the directive itself is not aware of the scope the model actually sits on or its name, but is able to use it as is and give it to ngModel.
The plunker also provides alternatives in case you don't want to use isolate scope or want to use a function.

Difficulty w/ Directives with optional scope values and setting with literals in html

New to angular. Trying to understand the proper way to do things
I am trying to create a directive that will run custom logic when a user clicks on a link before moving to the next page. However, before I can even get to the custom logic I'm having difficulty setting up the directive. I want to be able to:
A) Have certain attributes on the directive be optional (set to different defaults in certain cases)
B) Have a user be able to pass a string literal OR a variable from the controller's scope into the directive's attributes to be used on the directive's scope.
My Problems:
1.) If I use string literals in the directive's attributes, the corresponding $scope property is undefined. I have to access it through the $attrs. This isn't the worst thing but seems like a wrong practice to have to check the $scope.Prop and if it is undefined check $attrs.Prop.
2.) Also, this answer seems to say I need to use single quote when using string literals in the attributes but that behavior does not work in my example.
3.) When I have certain attributes on the directive not set, the default value I set on the $scope in the 'controller' function on the directive is not reflected when rendered. I cannot figure out why.
Code in Plnkr
Any help would be appreciated!
Here is a version where all three cases work:
The literals work without anything special
The scope values work using the usual {{ expression }} syntax
Defaults are provided in the directive's template using this syntax href='{{strHref || 'default href'}}' which I found in this thread.
Here is the full directive definition:
return {
restrict: 'E',
replace: true,
scope: {
strHref: '#link',
strText: '#displayText',
},
template: '<div><div>variables: {{strHref || \'default href\'}}, {{strText || \'default text\'}}</div> result: {{strText || \'default text\'}}<span ng-show="blnIsBackButton"> (Return)</span></div>',
};
Link : http://plnkr.co/edit/RB6shHI0xYa1FkP4eXDy
Scope & can be used for literals /expression. Scope = for setting up 2 way binding and scope = for method call.
You can set default value for property in controller if you want
I think directive name has to be lower case. Can someone confirm this? If i use pageLink it does not work but works for pagelink. (note 'L'). Same for property.
There are some nice videos about scope in http://www.egghead.io/.
I created this in Plunker which uses all 3 scope isolation types

AngularJS : Difference between the $observe and $watch methods

I know that both Watchers and Observers are computed as soon as something in $scope changes in AngularJS. But couldn't understand what exactly is the difference between the two.
My initial understanding is that Observers are computed for angular expressions which are conditions on the HTML side where as Watchers executed when $scope.$watch() function is executed. Am I thinking properly?
$observe() is a method on the Attributes object, and as such, it can only be used to observe/watch the value change of a DOM attribute. It is only used/called inside directives. Use $observe when you need to observe/watch a DOM attribute that contains interpolation (i.e., {{}}'s).
E.g., attr1="Name: {{name}}", then in a directive: attrs.$observe('attr1', ...).
(If you try scope.$watch(attrs.attr1, ...) it won't work because of the {{}}s -- you'll get undefined.) Use $watch for everything else.
$watch() is more complicated. It can observe/watch an "expression", where the expression can be either a function or a string. If the expression is a string, it is $parse'd (i.e., evaluated as an Angular expression) into a function. (It is this function that is called every digest cycle.) The string expression can not contain {{}}'s. $watch is a method on the Scope object, so it can be used/called wherever you have access to a scope object, hence in
a controller -- any controller -- one created via ng-view, ng-controller, or a directive controller
a linking function in a directive, since this has access to a scope as well
Because strings are evaluated as Angular expressions, $watch is often used when you want to observe/watch a model/scope property. E.g., attr1="myModel.some_prop", then in a controller or link function: scope.$watch('myModel.some_prop', ...) or scope.$watch(attrs.attr1, ...) (or scope.$watch(attrs['attr1'], ...)).
(If you try attrs.$observe('attr1') you'll get the string myModel.some_prop, which is probably not what you want.)
As discussed in comments on #PrimosK's answer, all $observes and $watches are checked every digest cycle.
Directives with isolate scopes are more complicated. If the '#' syntax is used, you can $observe or $watch a DOM attribute that contains interpolation (i.e., {{}}'s). (The reason it works with $watch is because the '#' syntax does the interpolation for us, hence $watch sees a string without {{}}'s.) To make it easier to remember which to use when, I suggest using $observe for this case also.
To help test all of this, I wrote a Plunker that defines two directives. One (d1) does not create a new scope, the other (d2) creates an isolate scope. Each directive has the same six attributes. Each attribute is both $observe'd and $watch'ed.
<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
attr5="a_string" attr6="{{1+aNumber}}"></div>
Look at the console log to see the differences between $observe and $watch in the linking function. Then click the link and see which $observes and $watches are triggered by the property changes made by the click handler.
Notice that when the link function runs, any attributes that contain {{}}'s are not evaluated yet (so if you try to examine the attributes, you'll get undefined). The only way to see the interpolated values is to use $observe (or $watch if using an isolate scope with '#'). Therefore, getting the values of these attributes is an asynchronous operation. (And this is why we need the $observe and $watch functions.)
Sometimes you don't need $observe or $watch. E.g., if your attribute contains a number or a boolean (not a string), just evaluate it once: attr1="22", then in, say, your linking function: var count = scope.$eval(attrs.attr1). If it is just a constant string – attr1="my string" – then just use attrs.attr1 in your directive (no need for $eval()).
See also Vojta's google group post about $watch expressions.
If I understand your question right you are asking what is difference if you register listener callback with $watch or if you do it with $observe.
Callback registerd with $watch is fired when $digest is executed.
Callback registered with $observe are called when value changes of attributes that contain interpolation (e.g. attr="{{notJetInterpolated}}").
Inside directive you can use both of them on very similar way:
attrs.$observe('attrYouWatch', function() {
// body
});
or
scope.$watch(attrs['attrYouWatch'], function() {
// body
});
I think this is pretty obvious :
$observe is used in linking function of directives.
$watch is used on scope to watch any changing in its values.
Keep in mind : both the function has two arguments,
$observe/$watch(value : string, callback : function);
value : is always a string reference to the watched element (the name of a scope's variable or the name of the directive's attribute to be watched)
callback : the function to be executed of the form function (oldValue, newValue)
I have made a plunker, so you can actually get a grasp on both their utilization. I have used the Chameleon analogy as to make it easier to picture.
Why is $observe different than $watch?
The watchExpression is evaluated and compared to the previous value each digest() cycle, if there's a change in the watchExpression value, the watch function is called.
$observe is specific to watching for interpolated values. If a directive's attribute value is interpolated, eg dir-attr="{{ scopeVar }}", the observe function will only be called when the interpolated value is set (and therefore when $digest has already determined updates need to be made). Basically there's already a watcher for the interpolation, and the $observe function piggybacks off that.
See $observe & $set in compile.js

Categories

Resources