How to cancel debounce in ng-model-options in AngularJS - angularjs

<form name="editForm">
Title<input ng-model="task.title" type="text" name=taskTitle ng-model-options="{ updateOn: 'default' ,debounce{'default':2000000} }">
<a ng-click="UpdateTask(task.title)">SAVE</a>
<a ng-click="editForm.$rollbackViewValue();">DISCARD</a>
</form>
Since the debounce value is very long, when I click "DISCARD", the ng-model will not reflect its changes.
However, what I want is that when I click "SAVE", either I can change the debounce value or make it expired so the changes will be reflected immediately.
I can't find cancelDebounce() in AngularJs document, anyone would like to provide a solution? Thanks

I'd suggest you to have one scope variable which will have value of debounce like $scope.myDebounce = 2000000, then do create one discard function inside controller that will first reset the be-bounce value to 0, then do rollback the form changes. Reset the myDebounce variable to its actual value in next digest cycle.
Markup
<form name="editForm">
Title
<input ng-model="task.title"
type="text"
name=taskTitle
ng-model-options="{ updateOn: 'default', debounce: {'default': myDebounce } }">
<a ng-click="UpdateTask(task.title)">SAVE</a>
<a ng-click="editForm.$rollbackViewValue();">DISCARD</a>
</form>
Code
$scope.myDebounce = 2000000; //somewhere in controller
$scope.discard = function (form) {
$scope.myDebounce = 0; //resetting debounce to get quick `ng-model` update
form.$rollbackViewValue();
$timeout(function(){
//setting the actual debounce value to old one in next digest
$scope.myDebounce = 2000000;
});
}

I think what you really need is NgModelController.$commitViewValue();
From the angular doc
$commitViewValue()
Commit a pending update to the $modelValue.
Updates may be pending by a debounced event or because the input is waiting for a some future event defined in ng-model-options. this method is rarely needed as NgModelController usually handles calling this in response to input events.

Related

angular $scope digest update from a dom event

I have an angular $scope variable that gets instantiated through a window event. The $scope variable is displayed correctly to the user, but updates are not being set through ng-model. What's odd is that ng-change has the correct value that I can use to manually set the scope variable.
I really don't understand why this is happening.
Event Handler:
document.addEventListener('updateSearchBar', handleUpdate);
function handleUpdate(eventData) {
$scope.benefitsFromDate = eventData.detail.fromDate;
$scope.$apply();
}
Front end:
<input
type="date"
name="fromDate"
ng-change="updateBenefitsFromDate(benefitsFromDate)"
ng-model="benefitsFromDate"
/>
<input
type="button"
value="Search"
class="btn btn-default"
ng-click="searchDocuments()"
ng-disabled="isSearchingDocuments"
/>
Angular snippet:
$scope.updateBenefitsFromDate = function(change) {
console.log($scope.benefitsFromDate); //still has old value
console.log(change); //has updated value when param is the $scope variable
//$scope.benefitsFromDate = change; //only way to update
};
$scope.searchDocuments = function() {
//ng-model never updates the variable when the value is changed and uses the instantiated value
console.log($scope.benefitsFromDate);
};
Why doesn't ng-model reflect the change, but passing $scope.benefitsFromDate in the ng-change function have the updated value?
I use the $timeout module when processing these kind of updates and avoid the call to $scope.$apply().
$timeout(function() {
$scope.benefitsFromDate = eventData.detail.fromDate;
});
I learned that angular is finicky about "dot rules".
All I had to do was prefix my data values with something else.
$scope.data = {};
$scope.data.benefitsFromDate = ...;
Or you can declare your controller to use as
ng-controller="appController as vm"

Cancelling old requests in ng-change with debounce

I have a requirement to fetch data through a web service for the search parameter which I type in a textbox. Currently it is working fine with debounce in ng-model-options and it fires a request after the time I specify.
I couldn't find that does angular cancel the previous incomplete request automatically once a new one is fired.
If not, how should I handle this as each key press will fire a request after the time period I specified.
The HTML is
<input type="text" class="form-control" name="inpTeachers" ng-model="inpTeachers" id="inpTeachers" autocomplete="off" required ng-change="GetTeachers(frmAddSubjectMain)" ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }" placeholder="Ex. Nancy Chang" />
And the method GetTeachers is just a simple method changing some scope variables after a http request.
Should I be cancelling the old http request if another ng-change is fired.
If you might want to close a fired request, you can use the timeout property of the http's config object, giving it a number (milliseconds) or a Promise.
function GetTeachers(frmAddSubjectMain){
var canceler = $q.defer();
yourService.retrieveTeachers(canceler).then(function() {
/* here is your result */
});
}
And in your service, you might need something like:
$http.get('/teachers', {timeout: canceler.promise});
Whenever you would like to cancel the ongoing request (if it isn't finished yet), just call:
canceler.resolve();

Debounce an ng-change event in angular

What's a good way to debounce an ng-change event in angular? I know about the debounce available in ng-model options, but I want my model to update right away as its changed because the user has the option to submit this data, and I don't want any information to be missing from the post because the model was waiting for the debounce. I do want to add a debouncer on my ng-change event or the function it calls. The html for my element
<div ng-change="saveProg()" text-angular ng-model="message" required></div>
I'd like to debounce the saveProg function so it doesn't run once for every character typed, but once every second or so. My function looks like this
$scope.saveProg = function() {
ProgressService.saveProg({
topic: $scope.topic,
message: $scope.message
})
}

AngularJs CheckBox conundrum

My view has
<input type="checkbox" class="check_box" ng-model="campaign.paused"
ng-click="CampaignPauseClicked(campaign, $event)" />
<p>campaign.paused == {{campaign.paused}}</p>
with the <p> being for debugging. It shows false, as it shoudl, given the data, but in the controller
$scope.CampaignPauseClicked = function(campaign, $event)
{
campaign.paused = ! campaign.paused;
when I breakpoint on the first code line, the value of campaign.paused is true (!).
I have searched the code and campaign.paused is not being written elsewhere.
Any idea what could be happening here?
[Update] I am using an ng-click fucntion, which I have not shown in its entirity, because I need it to "swallow" the $event and prevent it from propogating to the parent.
That's because ng-model is updating the value when you click the checkbox. You're undoing what Angular is doing for you.
If you want to do it by yourself in your $scope.CampaignPauseClicked function, remove the ng-model part from the html.
Otherwise, you can let Angular do its thing, leave the ng-model="campaign.paused" clause, and remove the first line from your ng-click callback.
Also you can replace the ng-click with the ng-change directive since you are using a checkbox.
ng-change will run everytime the checkbox state is changed (checked/unchecked)
<input type="checkbox" class="check_box" ng-model="campaign.paused"
ng-change="CampaignPauseChanged ($event)" />
<p>campaign.paused == {{campaign.paused}}</p>
And in your controller:
$scope.CampaignPauseChanged = function(event)
{
console.log(campaign.paused)
console.log(event)
}
Another option would be the ng-checked directive here is an example in this plunker. As you can clearly see the checkbox model returns true only if it is checked.

AngularJS: revert form on modal cancel?

I'm trying to use AngularJS 1.3.0's new $rollbackViewValue() method in ngModelController to cancel changes to a form in a modal popup or persist them when I close the modal. I'm using BootstrapUI for the $modal service.
I think I'm on the right track, but there is something that isn't quite working properly:
In my controller, I've got:
$scope.updateCharge = function (charge) {
var scope = $scope.$new();
scope.charge = charge;
var modalInstance = $modal.open({
templateUrl: 'client/app/views/providers/charges/updateCharge.html',
scope: scope
});
modalInstance.result.then(function () {
scope.charge.$save({providerId: providerId, chargeId: charge.id});
});
};
In my template, I have the following:
<form name="form" novalidate ng-model-options="{updateOn: 'save', debounce: {'save': 0 }}" class="form-horizontal" role="form">
<div class="modal-body">
<div class="form-group">
<label class="col-sm-3 control-label" for="chargeName">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" required ng-model="charge.code" id="chargeName"/>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="form.$invalid" ng-click="form.$broadcast('save'); $close()">Update</button>
<button class="btn btn-warning" ng-click="form.$rollbackViewValue(); $dismiss('cancel')">Cancel</button>
</div>
</form>
Generally speaking, this seems to work. When I click on cancel, my changes are reverted. When I click on Update, the modal closes but I do not see my updates in the scope.charge object.
I would have expected that my scope.charge object would be updated prior to the modal closing.
Am I using the ng-model-options incorrectly?
If I add a separate 'Apply' button that only does a form.$broadcast('save'), I see my scope object properly updated. So I am presuming that my $close() is being called prior to the event being processed by the ng-model-options. How can I avoid this race condition?
You can to use the $rollbackViewValue() method to revert changes but I think that is not the intention.
$rollbackViewValue(); Cancel an update and reset the input element's
value to prevent an update to the $modelValue, which may be caused by
a pending debounced event or because the input is waiting for a some
future event.
If you have an input that uses ng-model-options to set up debounced
events or events such as blur you can have a situation where there is
a period when the $viewValue is out of synch with the ngModel's
$modelValue.
In this case, you can run into difficulties if you try to update the
ngModel's $modelValue programmatically before these debounced/future
events have resolved/occurred, because Angular's dirty checking
mechanism is not able to tell whether the model has actually changed
or not.
The $rollbackViewValue() method should be called before
programmatically changing the model of an input which may have such
events pending. This is important in order to make sure that the input
field will be updated with the new model value and any pending
operations are cancelled.
The normal use case is to copy the model, optionally to persist the model and, if all is ok, refresh the model.
_this = this;
this.edit = function() {
this.modelToEdit = angular.copy(this.originalModel);
}
this.save = function () {
service.save(modelToEdit).then(function(savedModel) {
_this.originalModel = savedModel;
});
}
Or you can backup the model and restore when cancel
_this = this;
this.edit = function() {
this.backupModel = angular.copy(originalModel);
}
this.cancel = function() {
this.originalModel = this.backupModel;
}
this.save = function() {
service.save(this.originalModel).then(function(data) {}, function(error) {
_this.originalModel = _this.backupModel;})
}
I can see a few problems with your code:
ngModelOptions.updatedOn is meant to be a DOM event, i.e. click, blur, mouseenter, mouseover, etc,
The form controller does NOT have a $broadcast method, so it's never emitting an event.
I think the fact that it sort of works is because there is not type="button" on the <button> so they are considered as "submit" inside the form. And the model is updated because of that.
I suggest you use a simplified version, and
remove the ng-model-options from the form.
add a type="submit" to the Update button, and remove the form.$broadcast
add a type="button" to the Cancel button.
Am not sure how it would work with the modal, but here's a plunkr with ng-if: http://plnkr.co/edit/m37Fd0NybpnfslNkvJnO
I did not end up using any of the options suggested as answers, because the reality is, angular 1x has no "proper" way of doing what I want. Angular uses 2way binding, and yes I can write fancy directives to make life easier but infact it just makes the html looks even more complicated.
I settled with the suggested way as per many threads on the forum and that is to use angular.copy and then use the cloned model in your html. When you submit changes, set the cloned model to the original model.
There was heaps of examples on here on how to use angular.copy. And it works well.

Resources