I am new to AngularJS and trying to set up a function that gets called upon whenever a variable is changed.
Currently I have a dropdown-menu, with ng-model bound to a $scope.userRating. And I am trying to get a function that gets called immediately when the user changes the value using the dropdown.
I have been looking at $watch, but not quite sure on how to get it to work. I also tried to make a ng-click function on the in the html, but ng-clicks don't seem to trigger.
You should use $watch .
Example-
$scope.$watch('modalVarible',function(newVal,oldVal){
//do your code
}
Here on the change of variable 'modalVarible' this watch will be called.
we can use $watchGroup in controller of our directive to bind $scope variable changes (bidirectional)
$scope.$watchGroup(['var1','var2'],function () {
});
you can also check this question.
Related
I'm trying to build a custom angular table directive, and came upon a curious problem. I have a parent directive parentDir, which has multiple child elements childDir inside it. When a childDir is clicked, it calls the parent's sortByKey function, which changes the sorting variable of the controller, and later on is supposed to sort the data depending on the clicked column.
The problem is, in childDirs, I'm $watching this the parent's sorting variable, but the callback function doesn't trigger when the variable value changes. Instead, it seems to queue the callback until something happens in either the parent directive controller's $scope or its parent $scope - so for example, changing an input with an ng-model triggers all of the queued $watch callbacks.
So far I've tried to call $scope.$apply() in the sortByKey function (which doesn't work) and in the watch callback (which obviously doesn't work since it's not getting called). I'm kinda stumped on why the callback isn't triggering since it's the first time I've ever encountered this behaviour.
Here's a link to the plunker demonstrating the behaviour. Open the console, and then click on the "Hello", "Hello 2" and "Hello 3" texts. You can see in the console that the sortByKey function is being called properly, but there's no logging from the $watch callback. Then change the input's value, and you can see the callback function console.logs.
To summarize:
$watch() callback function isn't being triggered in a child directive until parent scope is updated
adding $scope.$apply calls doesn't help
The wanted outcome is for the show text to appear after the clicked text as the text is clicked, and to disappear from all the other texts
I'm using an AngularStrap typeahead and need a callback, when an items gets selected by the user. According to the documentation, there is an onSelect option, which can be supplied with a function, and:
If provided, this function will be invoked when an item is selected.
… and …
Options can be passed via data-attributes on the directive or as an object hash to configure the service. For data attributes, append the option name to data-, as in data-animation="".
So I tried using it as follows:
<input type="text"
class="form-control"
ng-model="selection"
bs-options="item for item in items"
bs-typeahead
data-on-select="onSelect">
And provide the onSelect() method within my controller:
$scope.onSelect = function() {
console.log('this never gets called :(');
};
However, the callback is never invoked. Anything I'm missing here?
[edit] dfsq pointed out that it should be bs-on-select, regarding to the library’s sources. I just tried this variant, however the event only gets triggered once. I created this Plunker to illustrate my issue; the "Number of selection events" should naturally be incremented with each selection, however it remains 1.
Looks like this was answered in the comments by #Vanojx1 and that it should be:
bs-on-select="onSelect"
I've updated the Plunker with this change and it works (however, it doesn't call the method until the input loses focus).
The code at this plnkr has a modal which pops up when a user clicks on a "Click to take quiz" button which calls a controller method that in turn calls a modal service. To get the plnkr to work, click anywhere in the code and press the space bar to add white space in a way that does not effect syntax. This will trigger plnkr to re-initialize the app and make the modal pop up after you click the button.
The problem is that the text printed in the modal does not update dynamically when timeLeft variable counts down. And also, the user's button click does not update the quizAnswer variable. In short, the modal is not able to talk interactively with the calling controller and view.
What specific changes need to be made to the plnkr to get the modal text to show the dynamic countdown, and to get the modal buttons to change the value of the $scope.quizAnswer variable?
Also, I have been carefully reading the documentation at this link. I think that the answer may be related to:
1.) $uibModal's options parameter passed in open(options) contains the parameter scope that defines the parent scope to be used for the modal's content, and also property bindToController which, when set to true, binds the scope property to a specific controller defined by controllerAs.
2.) The open(options) method returns a modal instance, which includes close(result) and dismiss(reason).
I suspect that the solution lies in these methods and parameters, but I am looking for good examples and would appreciate some experienced eyes looking at this problem.
NOTE: The solution to this came in the comments below the accepted answer, especially the link to another posting that contains 2 lines of code for emitting the modal button click's results back to the parent controller.
You have a number of issues.
First, takeQuiz at navigation.js - line 16, should be attached to $scope, not this, since this will mutate depending on context.
Second, $scope.$apply and $scope.$digst(); at navigation.js - lines 29/30 are unnecessary since you will already be in a digest cycle. They should be removed else they'll trigger an error.
Finally (and this is the meat of your issue), you are misunderstanding how modal options are bound across when creating a modal instance. It is NOT two-way binding; it is a single extends from one object to another. As a result, trying to bind to the options (or creating a concatenated string with the timeRemaining) will not update once it's bound across.
Instead, one possibility is to create an event handler inside of the modal and broadcast on each tick, updating the modal. In addition, if you pass the body text as prepend and append text, it is easier to insert your timestamp value:
You will need to inject (and broadcast from) $rootScope in your navigationController, since the modalService is registered somewhere very high in the scope chain.
On each tick, broadcast the time remaining navigation.js:
$rootScope.$broadcast('timeRemainingTick', $scope.timeRemaining);
In your modalService.js, register to receive the event inside of the controller assignment:
var timeRemainingUnbind = $scope.$on('timeRemainingTick', function(event, newTick) {
$scope.modalOptions.timeRemaining = newTick;
});
Finally, make sure that you unbind the event by calling timeRemainingUnbind() in the close events of your modal to prevent memory leaks:
$scope.modalOptions.ok = function (result) {
timeRemainingUnbind();
$modalInstance.close(result);
};
$scope.modalOptions.close = function (result) {
timeRemainingUnbind();
$modalInstance.dismiss('cancel');
};
See my working forked plunker here
Here is the problem :
I try to do things like this :
Click on a directive triggers an event
Event sends data to a rootScope function which sets a scope variable value with new data
Another directive has a ng-model in its template which is supposed to reflect the previously changed var
Data should be updated in the view AND model. Model is ok (update done), but view update does not seem to trigger automatically, it only updates when I click on the "show button" (see fiddle)
Here is the corresponding jsfiddle I made, with comments :
http://jsfiddle.net/wkmm576m/7/
Here is the code which I think is making trouble... :
app.directive('addEditInput', function() {
return {
restrict : 'E',
template : '<div id="editForm"><input type="text" ng-model="currentVar.name" /><br/></div>'
}
});
In fact, I return a template, with ng-model. But the ng-model is not updated when its value changes from anywhere else...
Tbh, I'm a bit confused with directives and scopes communications - data....
Thanks for reading / help
Short answer - you need to call $scope.$apply in top controller:
http://plnkr.co/edit/QEjzlsJj4fh1eeqBXSvt?p=preview
Long answer: you bind usual js function to usual js event, so when user clicks on element - your function executes. Thats all. When you use ng-click - it will trigger digest cycle and your bindings will be updated. So you need to trigger it manually, so you call $scope.$apply.
Note: better do not use jquery where you do not need to. I.e. use $element.bind('click',function(){ instead of $().on...
I modified the AngularUI-Bootstrap direvtive for tooltips to help me display error messages, here is what I did so far: http://jsfiddle.net/distractedBySquirrels/TaAHZ/
But I have a problem with the dynamic messages. The first time you mouseover the first input field, the message is in the wrong place, this is because the model is not correctly updated yet. Of course, after the second mouseover the tooltip is in the right place. But if the message to display changes again the same failure occurs.
I am using $parse to update the template:
$parse('tt_content').assign( scope,
getErrorMessage( ctrl.$error ) || attrs.tooltip
);
Seems like not all the listeners have revceived and updated and "ttHeight" and "ttWidth" are still set to the "old" values. The correct update of the message/model only works with the $observe, which is illustrated by the third example.
I am stuck and don't know what I did wrong? :(
The problem is that when you modify your models with $apply, you need to manually activate the digest cycle in order to notify all watchers of such models. So basically, you need to add scope.$digest(); whenever you change the scope doing things like scope.$apply( show );
I modified your code with my suggestions here http://jsfiddle.net/TaAHZ/2/