ng-book examples not working as expected - angularjs

I'm currently reading through ng-book for angular js and have come accross two example pieces of code.
The first deals with the $parse service and can be found here: http://jsbin.com/UWuLALOf/1/edit
The second deals with $interpolate and can be found here: http://jsbin.com/oDeFuCAW/1/edit
Neither of these are working as expected (I think). They should be updating the view live. I've tried creating the first example locally but I get the same result. I thought maybe it was due to the version of angular but since it's using a specific version from the google api's this shouldn't be an issue. I'd like to figure out exactly what the parse and interpolate services are doing here so any ideas greatly appreciated.
Thanks
C

Instead of passing newVal param to $parse function you should pass an expression 'expr'
$scope.$watch('expr', function(newVal, oldVal, scope) {
if (newVal !== oldVal) {
// Let's set up our parseFun with the expression
var parseFun = $parse('expr');
// Get the value of the parsed expression, set it on the scope for output
scope.parsedExpr = parseFun(scope);
}
});

that's beacues the version of angular which you attached to site is not stable version , use this
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
but nevertheless code are not working as expected

The code is working as expected but most likely you are not expecting what you should :)
Let's take one at a time:
$scope.$watch('expr', function(newVal, oldVal, scope) {
if (newVal !== oldVal) {
// Let's set up our parseFun with the expression
var parseFun = $parse(newVal);
// Get the value of the parsed expression, set it on the scope for output
scope.parsedExpr = parseFun(scope);
}
});
The $scope.$watch is setup and is monitoring changes tn the expr variable, which is tied with the input value via ng-model
If you change the expr value in the input, it is send to $parse for evaluation (1+1 should present 2, name should present {"name":"Ari Lerner"} in this case).
$parse will convert a string expression to a function, so having this in mind you should know what to expect.
In the second example, when the body part is changed via the textarea, the template is rendered via $interpolate which will evaluate anything inside {{ }} and then render the html.

Related

Angular JS Directive - complete background

I am a rookie in angularJS learning about directives (and struggling a lot :)).
I am trying to understand a piece of angularJS code in the plunker
by user tasseKATT for the stack overflow question regarding angular-ui-bootstrap.
I was hoping if anyone can explain this code fragment in more detail.
Specifically
How parsing and compilation happens in directives
How angular knows when to recompile directives ($watch - perhaps, if so how).
I checked the documentation for $parse but dont see any explanation on the service taking a function. What piece of information am I missing.
Also what is the
(value || '').toString();
used for.
What are the properties compileHTML. Where can I see the documentation for the compile function explained in more detail than the one provided by AJS.
What is $$addBindingClass(tElement) and $$addBindingInfo.
Explain the function ngBindHtmlWatchAction
what is $sce.
The fragment from the directive is below
app.directive('compileHtml', ['$sce', '$parse', '$compile',
function($sce, $parse, $compile) {
return {
restrict: 'A',
compile: function ngBindHtmlCompile(tElement, tAttrs) {
var ngBindHtmlGetter = $parse(tAttrs.compileHtml);
var ngBindHtmlWatch = $parse(tAttrs.compileHtml, function getStringValue(value) {
return (value || '').toString();
});
$compile.$$addBindingClass(tElement);
return function ngBindHtmlLink(scope, element, attr) {
$compile.$$addBindingInfo(element, attr.compileHtml);
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
element.html($sce.trustAsHtml(ngBindHtmlGetter(scope)) || '');
$compile(element.contents())(scope);
});
};
}
};
}
]);
Disclaimer : First thing buddy, you are a rookie in angular and you are trying to understand directives using a very complicated directive. Maybe its better if you start with something a little less complicated :)
I will try to answer your many questions:
How parsing and compilation happens in directives:
Angular knows which all directives are present, so when you write a name which matches one of the directive it compiles the html and link the directive.(I am trying to find a good blog for you to read on these cycles of directive, maybe this post will help)
How angular knows when to recompile directives ($watch - perhaps, if so how): angular doesnot recompile directives, a directive is only compiled once when angular encounters it for the first time.
$watch is basically put on a variable, so whenever angularjs is applying the changes of the scope, it looks for that variable if it is changed.
$parse
It is a way of adding angular to html. So if you directly append html to an element, it does not have angular like features. So we get the html, compile/parse it and then append to the element. Parsing is basically getting all the variables in the html and applying two way binding on those, plus replacing them with their values in controller.
Read this post.
Remember that compile/parse are always done against a scope variable, because variables defined inside the html are properties of the scope variables.
What is this (value || '').toString();
This is a common coding practce of js developers.
This basically amounts to :
(value ? value : '').toString();
Why dont we use value directly like this
value.toString();
Because if the value is undefined or null, it does not have a toString function and it will throw an error.
So the coder is trying to check if the value is there, then convert the value to string otherwise, just put empty string(converting to string results empty string).
what is $sce?
sce is a service, that comes in file angular-sanitize.js which is a part of angular bundle. When somebody tries to modify html to insert his link(malicious activity we call it), angular does not allow and treats his html as simple text.
But what if you want to add html, you pass your html to $sce service(injected in controller/directive etc) and the output is the html which you can insert into the view(this will come as html).
$$addBindingClass
This is just to add "ng-binding" class to the element, so that you can see which element has a model created by angularjs.
$$addBindingInfo
This is to add information to the element for debugging purpose. You can select the element in the inspector and run the following statement in console to get the scope information.
angular.element($0).scope(); //$0 means selected element
Here is a link that explains this thing better.

Angular $watch function not working as intended

So I have the following watcher on a scope in the link function of a directive. I have used this pattern for another variable and it works however this one does not. Does anyone have any suggestions. Code is as follows
$scope.$watch('QOLFactory.surveysInView', function(newValue, oldValue, scope){
console.log(QOLFactory.surveysInView);
console.log(newValue, oldValue);
scope.surveys = newValue;
scope.surveyData = generateSurveyTotals(newValue);
});
The first console log prints the corrent value of QOLFactory.surveysInView, but the second console.log prints undefined, undefined. Is there something that I am doing wrong?
Thanks in advance.
In order for the watcher on directive to have access to the Service that was being injected in the controller, the Service has to be assigned to $scope variable. This makes the watcher behave as expected, and returns the correct values.

Transform text between element AngularJS

This has been bothering me for about an hour now due to my lack of AngularJS proficiency. I am working through trying to understand directives. This example regarding capitalizing text from a directive seems clear, but how to you capitalize text in the following example through a directive without referring to "myMessage.description" within the directive?
<td my-directive>{{myMessage.description}}</td>
I know its staring me right in the face, but I cannot seem to find a direct answer to this probably because I am not phrasing my question correctly in my mind. My thought is that I would have to refer to something hanging off of attrs.value or attrs.text within the link function. If so, how do I update the view with the new value?
The typical way to transform text in a view is to just use a filter in the interpolation, there's a built in uppercase filter so you can just add |uppercase to the interpolated expression.
http://plnkr.co/edit/FbqLdnu3AAW83uy2w31u?p=preview
<p>Hello {{foo.bar|uppercase}}!</p>
Here's a way you could do it with a directive:
http://plnkr.co/edit/xADMMdyuklMgJ9IdmocT?p=preview
.directive('upperCase', function($interpolate){
return {
// Compile function runs before children are compiled/processed
// so we get the initial contents in this function
compile:function(iElem, iAttrs){
var curContents = iElem.html();
//After we grab the contents we empty out the HTML so
//it won't be processed again.
iElem.html('');
// The compile function can return a link function
// this gets run to get the scope for the instance
// and uses the $interpolate service to do the same
// thing angular would do when it sees the {{}}
// with the added step of running toUpperCase
return function(scope, iElem, iAttrs){
iElem.html($interpolate(curContents)(scope).toUpperCase())
}
}
};
});

Injector returns undefined value?

I'm trying to get the service from my legacy code and running into a weird error with injector() returning undefined:
Check this plnkr
Also, I'm trying to set back the new property value back to the service, will that be reflected to the scope without the use of watch?
Thank you very much, any pointer or suggestion is much appreciated.
You're trying to get the element before the DOM has been constructed. It's basically the same issue as running javascript outside of a $(document ).ready(). So this line has no element to get:
var elem = angular.element($('#myCtr'));
Also, by the way, instead of using jQuery, another Angular option for doing the above is:
var elem = angular.element(document.querySelector('#myCtr'))
Angular provides an equivalent to $(document ).ready() called angular.element(document).ready() which we can use.
But you'll also need to grab scope and execute your change within a scope.$apply() so that Angular is aware that you've changed something that it should be aware of.
Combining the two we get:
angular.element(document).ready(function () {
var elem = angular.element($('#myCtr'));
//get the injector.
var injector = elem.injector();
scope= elem.scope();
scope.$apply(function() {
injector.get('cartFactory').cart.quantity = 1;
});
});
updated plnkr

Can $scope.$watch determine equivalency of two objects?

I've built a directive that gets its data by $parse'ing from an Angular expression in one of my $attrs. The expression is typically a simple filter applied to a list model, the evaluation of which will change when the parent $scope is modified.
To monitor when it should update the data it's using, my directive is using a $scope.$watch call, with a custom function that re-$parse's the expression. The problem I'm running into is that $parse will generate a new object instance from the expression, so $watch sees the value as changed even when the data in each object is completely equivalent. This results in my code hitting the $digest iteration cap very quickly due to actions taken in the $watch callback.
To get around this I am doing the following, currently:
var getter = $parse($attrs.myExpression);
$scope.$watch(function () {
var newVal = getter($scope);
if (JSON.stringify($scope.currentData) !== JSON.stringify(newVal)) {
return newVal;
} else {
return $scope.currentData;
}
}, function (newVal) {
$scope.currentData = newVal;
// other stuff
});
However, I don't like relying on JSON as an intermediary here, nor using my $watch'ed function itself to evaluate equivalency of old and new values. Is there a flag the $watch can take to determine if two objects are equivalent, or is there a better approach for handling this kind of situation?
Hi you should use this,
scope.$watch('data', function (newVal) { /*...*/ }, true);
This has been answerd here on stackoverflow

Resources