I have an issue where my $scope.searchVal variable updates, but if I assign it inside an object, it doesn't seem to update at all.
See below:
<div ng-controller="MyCtrl">
<div>{{searchVal}}</div>
<div>{{searchFilter}}</div>
<div>
<input ng-model="searchVal"/>
</div>
</div>
See the JSFiddle
If I type in the input, the first div's value updates properly for searchVal, but the searchFilter object that contains $scope.searchVal as a value is set to the original value of '' and does not update accordingly.
I've considered (and tried) updating the whole object everytime I need it by calling a function, but this seems really hacky to me. Is there a simple way to get this done? Am I missing something?
What you have to is $watch the value from the scope something like:
Online Demo
$scope.$watch('searchVal',function(newVal, oldVal){
$scope.searchFilter.search = newVal;
});
It isn't as straighforward as it seems since angular has no way of knowing whether you want the dynamic binding for the property or not.
You can achieve what you are trying to do using $watch service. Put a watch on the searchVal & use it to detect changes & update the object property accordingly.
Please put $watch . It will always watch the value change of variable
$scope.$watch('searchVal',function(newVal, oldVal){
$scope.searchFilter.search = newVal;
});
updated fiddle http://jsfiddle.net/be6s1eLo/3/
Related
This does seem to me a pretty straight forward question but for some reason I am unable to find a solution after much searching. Pardon me if its a duplicate.
The question is that I would like to pass a 'model' to a function and change its value there.
So in the HTML I have this:
<input type='button' value='click here'
ng-click="CreatePlayer.toggleSidenav(CreatePlayer.someVal)" />
And the function inside the controller is:
CreatePlayer.toggleSidenav = function(target) { }
Now, if I console.log(target)
I do get the current value of this model. But if I set target = 'blah'; It doesn't update the value in the HTML.
Can someone please help me understand what am I doing wrong here?
Thank you.
Update
Thank you all for the time you gave to read the question.
Here is a JS fiddle that I just wrote to explain my problem: https://jsfiddle.net/ibnyusrat/564f7x9p/
The issue is, inside of the controller there is a function and inside of that function I am able to read the value passed as a parameter but I am not able to set it to another value. I don't want to create a new function for every situation. Preferably I would like to use the same function and only pass a different model as a parameter to apply whatever it is that I want to do with its value. Can anyone please help me understand what am I doing wrong?
Thank you all in advance.
In the template pass the name of the scope variable as a string argument:
<div ng-controller="CreatePlayerController as CreatePlayer">
{{CreatePlayer.someVar}}
<input type='button' value='click here'
ng-click='CreatePlayer.updateValueOf("someVar")' />
</div>
In the controller, use property accessor bracket notation to change the value:
this.updateValueOf = function updateValueOf(target){
this[target] = 'Some other value.';
};
The DEMO on jsFiddle
You no need to pass value to function , because your model value change automatically $scope variable updated.
<input type='button' value='click here'ng-click="CreatePlayer.toggleSidenav()" />
In controller set your value;
$scope.CreatePlayer.CreatePlayer ="blash";
I can't understand why angular ng-change function are called only in first click. In this example
http://jsfiddle.net/ZPcSe/5/
function is called every time when i change current radio selection, but in my code something is wrong
http://jsfiddle.net/4jL3u8ko/1/
Can someone change my code to work like first example, and explain why it isn't working?
Great question.
This is not an answer but one may call this explanations to answers and any further problems that may occur. And this happens quite often, so its important to understand. The answer given by #Victor is ok and the comment of #Atias is also helpful.
Explanation -
Simple explanation : ng-repeat forms child scope.
Little detailed :
So your application has 3 scopes (or may increase depending on the number of values in $scope.radioButtons). Let me name them - parentScope(main scope), childScope1(scope of first element of ng-repeat) and childScope2(scope of second element of ng-repeat).
Parent scope variables : calledFunctions(array), radioButtons(object) and newValue(function)
ChildScope1 variables : name(assigned a literal at the start), val(assigned an object at start) and value(it has no value, or undefined)
ChildScope2 variables : name(assigned a literal at the start), val(assigned an object at start) and value(it has no value, or undefined)
What happens when you click on the radio button of childScope1(for first time):
ChildScope1.value = ChildScope1.name or ChildScope1.value= "Radio1"; the ng-change directive checks if this scope's model has been changed? Yes, as it was undefined and now it has a literal("Radio1")!! So call ChildScope1.newValue(), which obviously is not present. Therefore now look for parent- call parentScope.newValue(). This is present, so execute it.
keep in mind that ChildScope1.value= "Radio1";
After clicking on other radio buttons......
Now lets click 2nd time on ChildScope1's radio button. After clicking it- ChildScope1.value = ChildScope1.name or ChildScope1.value= "Radio1"; the ng-change directive checks if this scope's model has been changed??? No!! Because it still contains the same literal vaule as before, so do not even look for ChildScope1.newValue() or ParentScope.newValue() function.
Now same thing happens with ChildScope2.
Hope this explains why your fiddle works like a one time binding, or how #Victor or #Atias are correct.
Simply the solution is - The ng-model of ChildScope1 or ChildScope2 or any other child scopes, should point to a variable of parent scope. So you can use $parent or make an object in parent scope(main scope) with a property( eg.- ParentVal={ChildVal = ""};). And in ng-model inside ng-repeat- write ng-model=ParentVal.ChildVal.
Sorry for my poor english and please pardon my spelling mistakes if there is any.
Thanks
ng-model is tricky. You should always, always have a dot in your models, or you get this kind of problem where you are creating several models and you think you only have one.
As a quick fix, use $parent.value instead of just value in your ng-model and ng-change.
As a good fix, have a proper model object in the scope, instead of storing values directly in the scope:
$scope.model = {};
ng-model="model.radiosValue"
As an even better fix, use the controllerAs pattern and use that object as the view model.
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!
If you take the following Plunkr you will see a simple service - increments a count and it gets reported to the user.
What I am trying to understand is why this works (the increment is reported to the user on a click) - binding to the function in the view:
From the HTML
<p> This is my countService variable : {{countService()}}</p>
From the controller
$scope.countService = testService.getCount
And why this Doesn't work - binding the function to the scope:
From the HTML
<p> This is my countService variable : {{countService}}</p>
From the controller
$scope.countService = testService.getCount()
Ultimately we're binding to a function in the service, though the second one doesn't bind the new value.
A clear, easy to understand, explanation would be great :)
In the first version you bind directly to the function so angular checks if the functions output has changed. In the second version you only call the function once when the scope is created and set countService to that value. Since countService now is a variable that has nothing to do with the counting function its value wont reflec the value returned from that function.
I am trying to give one $scope variable the value of another $scope variable. Can someone tell me what is going wrong? Take a look at the simple plunker for details:
http://plnkr.co/edit/TlKnd2fM5ajrlkKKhKZ1?p=preview
I know I am missing a fundamental concept in the way $scope works, but I can't pinpoint what it is. Somebody slap me and give me that 'Ahaaaa' moment if you don't mind.
The problem is that what you assign to the greeting property does not get reevaluated when username changes. greeting gets stuck with the initial (blank) value of username. You could set up a watch on username and modify greeting when username changes, or you could change the view:
<div ng-controller="Parent">
{{username}} {{greeting}}
<!-- ... -->
</div>
And don't use username when assigning to greeting:
$scope.greeting = 'is so confused.';
Here's an updated example.
There is nothing wrong with what you are doing, but you can't expect greeting to get updated every time username changes since the binding (that creates a $watch) is done in greeting, which is a string. So everytime a $digest loop occurs it will just re evaluate your variable greeting to the same result ("is so confused.").
You need to bind the property you want to maintain updated like this:
plunker