I am trying to pass a dynamic value to a custom angular directive. I am generating the value from a function and I want it to be resolved before the directive resolves; a first cursory pass at this looked like this on a DOM element:
my-directive="{{ myfunction() }}"
This resulted in the DOM looking the way it should:
my-directive="MYRESULTS"
However I find that the directive isn't seeing these results when it is run; it appears to be triggered before the value is set. Is there some way I can seed a custom value for my directive? Any assistance is appreciated.
Related
There is a way to update a value in object and my view (HTML) display the new value without use two way bind with Angular 1.5.3?
If I understand you correctly you need a one-way bind from your controller to your view. If so, you can just use ng-bind
E.g.
{{ctrl.myName}} or <span ng-bind="ctrl.myName"></span>
If you want a one-time bind, this was introduced in angular 1.3:
E.g.:
{{::ctrl.myName}} binds myName once, future changes to it won't be picked up
Below I will put link to my example in Plunker to show you my problem.
In ng-repeat I have used array(filtrowane) to store data from filter result, also I have added ng-change on input for showing the value of filtrowane.length in console. Variable filtrowane is defined in controller, and after typing something It`s showing different values in console and in the view, can you tell me why?
PS. run your web browser console and you will see the difference.
example Plunker
When you change value, first ng-change is called, then $digest starts which will update filtrowane.
If you use such notations:
item in filtrowane = (tablica | filter:search)
do not use filtrowane in controller. Html should be view, so you should not declare any variables there, which you use in model (js).
Look here: http://plnkr.co/edit/QAHlbuZqWX4szglvMP87?p=preview
This code does same as yours, but filtering is done in javascript. A bit more complicated, but things are more clear. (it is also better for big arrays)
Use ng-blur instead of ng-change
<input type="text" ng-model="search" ng-blur="sprawdzFiltr()">
This is due to the ng-change is probably executed before the filter is applied, so the old value will be taken.
I have made a new directive that is basically a new carousel that extends the angular UI carousel bootstrap. This new carousel will display multiple divs in one frame. My new directive accepts any data in an array and a custom html template for each data.
However, if I use my carousel with a directive, there is a weird behaviour that I am seeing with watch inside the directive. Everything works fine, but the watch inside my directive is always getting the same value for newVal and oldVal. What I mean is this, here's my carousel code:
<slide ng-repeat="frame in ctrl.frames">
<div ng-repeat="divs in frame.divs">
<custom-directive data="divs"></custom-directive>
</div>
</slide>
and inside my customDirective controller, I watch the change of data like this:
$scope.$watch("ctrl.data", function(newVal, oldVal){
if (newVal !== oldVal) {
// data is updated, redraw the directive in my case
// however newVal is always the same as oldVal
}
})
newVal and oldVal is always the same.. I expected the initial state to be oldVal = undefined and newVal will be my new data. However, this is never the case. Data is passed as a two-way binding to carousel and to custom directive (using '=' operator inside the scope of each directive).
Why is this happening? I have investigated this for long and here's my findings:
If I don't use ng-repeat inside my carousel, this will work. oldVal will be undefined and newVal will be my data during the initial state. But why is ng-repeat causing this? I have read lots of article regarding golden rule of prototypical inheritance, that says ng-repeat will create new childScope that hides/shadows the parent, but that only happens to primitive object and I am passing an array to my data.
I need to use ng-repeat in my carousel directive.. so I need to know why ng-repeat is causing this.. any suggestions?
UPDATE:
Reproduced the problem in Plunkr here. As you can see, oldValue is always the same as newValue (I expected the oldValue to be undefined in the beginning)
When you register the $watch in your link function, Angular has already processed the bindings during the preLink phase, hence you will never see undefined the first time your watcher is executed (that initialiation call is the only moment on which oldVal and newVal are potentially the same. If the watcher was registered before the bindings resolution, the oldValue would be undefined)
If you really want to see it, you could override the compile phase and add a custom preLink method (the default link being the postLink).
But I really doubt that you want to do that. Why is it a problem to not have undefined the first time? You should try to explain the real problem you are facing.
Also, note that if divs that you pass to your directive is an array, you should use scope.$watchColleciton instead of scope.$watch in order to detect changes in the array elements instead of change of the whole array pointer.
I think the problem you are having is just a misunderstanding of how $watch works.
$watch is expected to initialize with equal values. See the documentation here. Specifically:
After a watcher is registered with the scope, the listener fn is
called asynchronously (via $evalAsync) to initialize the watcher. In
rare cases, this is undesirable because the listener is called when
the result of watchExpression didn't change. To detect this scenario
within the listener fn, you can compare the newVal and oldVal. If
these two values are identical (===) then the listener was called due
to initialization
So in other words, your check for if they are equal is so you don't detect the initial call
In your provided Plunker, if you need to do some initialization code, you can do two things:
You can check if they are equal in the $watch function, and if they are then that is the initial call with their initial values
Or, outside of that function in the link function, the values are their initial values there (since the link function is equivalent to post-link, which means scope values have already been linked) so you can put your code there
Forked your Plunker here. Notice I moved the alert outside of the $watch, and the value is still valid
EDIT:
The reason you see a difference when it is not in the ng-repeat and it is set up like your commented out code in the Plunkr is due to you adding data in a $timeout. When the page initially loads, below are what the two types render as:
<a1 prop="data[0]"></a1>
HTML looks as it was written. data=[]. directive element exists, calling link with data[0]=undefined. $watch called with prop=undefined
<!-- ngRepeat: element in data track by $index -->
HTML is simply a comment. waiting for data to be populated. No directive element exists, which means link is not called
When you add items to data after the timeout, they look like this:
<a1 prop="data[0]"></a1>
Same as above. data[0] is now defined so prop is defined
<div ng-repeat="element in data track by $index" class="ng-scope">
<a1 prop="element" class="ng-isolate-scope"></a1>
</div> (x3)
Page now has directive elements. calling link function on each with data now filled. $watch called with prop values linked
I want to create a directive with isolated scope, but I'm not able to get it working.
jsFiddle
I want to isolate age model in a directive scope. I want to perform some business logic on that model and then set that model to parent binding. I hope the fiddle is explanatory.
I am also adding a button to the template which when clicked should invoke a submit function:
<button ng-click="submit()">click me</button>
It seems the button is working fine, but why is $scope.$watch() is not begin triggered? In a normal situation, if I change the view value it will automatically update the model value. But now it isn't.
$watch requires a dollar sign, and you pass either a function or a string that is evaluated on your scope, i.e.:
$scope.$watch('age', function(value) {
There are many more errors in your code, for instance you don't have a declared variable called 'age' so this line will reference window.age and give you an error because it is undefined, you need to say $scope.age I think:
age = age+10;
It just looks like your updated fiddle is a playground, hope these point you in the right direction. I'd recommend going through the egghead.io angular videos.
I am trying to set the default button and get the current selected value.
The example without the repeat loop works.
Here is my plnkr:
http://plnkr.co/edit/t9CefA5bhLZs3RASmEUG?p=preview
It is based on this example:
http://plnkr.co/edit/LFj4inY9TLYZs9z7yHCr?p=preview
Thank you!
So, there were 2 things going on in your plunker.
Firstly the btn-radio attribute doesn't need interpolation ({{}}) you can (and should) provide an expression directly. So just write btn-radio="company.id" instead of btn-radio="{{company.id}}".
Secondly, you must know that the ng-repeat directive creates a new scope. This is a very, very common conceptual issue people have with AngularJS so I would encourage you to read https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
Coming back to you particular problem you can either change you ng-model expression to point to a parent scope (ng-model="$parent.radioModel") or bind to an object property (ng-model="radioModel.id").
Here is a working plunk with the second approach:
http://plnkr.co/edit/uGAxaRlPFK6sD4tRjGXX?p=preview