Strange behaviour with ng-model binding and checkbox - angularjs

I'm trying to intercept a checkbox change so I can put a confirmation stage in the middle and I am experiencing strange behaviour.
When I click the checkbox preventDefault is stopping the UI from changing the checkbox, except the bound model will change once and then no longer be changeable.
Any ideas on how I can fix this? Am I approaching this wrong?
$scope.change = function(selected, $event){
$event.preventDefault();
};
https://jsfiddle.net/tcVhN/197/
edit: Answers to JB's questions below:
I am trying to intercept the checkbox change so I can put a confirmation step in the middle IE "Are you sure you want to change
this text box?"
Just updated to 1.47 (and updated jsfiddle link).
I'm using ng-click because ng-change doesn't pass the event
through which means I can't cancel the ui change via
$event.preventDefault.
See above.

I have modified your Fiddle to make it work:
https://jsfiddle.net/masa671/8qrct4y2/
Notice the change in HTML: ng-model="x.checked" to ng-checked="x.checked".
JavaScript:
$scope.change = function(selected, $event){
$event.preventDefault();
$timeout(function () {
if (window.confirm('Are you sure?')) {
selected.checked = !selected.checked;
}
});
};
The key problem for me was to find out, how to prevent the checkbox status from changing until the user has confirmed the change. I don't know the best/right solution, but I resolved this so that the event handler just prevents the default behaviour, and the actual change is handled outside the event handler with the help of $timeout.
At least the Fiddle seems to work in a sane manner... :-)

Related

How to capture the closing event of an aside when clicked outside or ESC in AngularJS?

I have a case where in which 2 asides are loaded and I have to clear a variable when the aside is closed pressing ESC or by clicking outside the aside.
I searched online and found that the following code can be used in the case of a modal. What is the code for aside?
scope.$on('modal.hide', function() {
console.log('modal... -hide');
});
How to watch for an Aside closing in angular-strap
The above code is not working for aside.
Is this approach correct? Otherwise, please suggest a method.
In the above case the modal name is not specified. So when we have more than one modal or aside how do we differentiate the closing of the modal or aside? In the current case, I don't have to differentiate the modals. But how to catch the event differently?
Update
The following code woks for aside.
scope.$on('aside.hide', function() {
console.log('aside... -hide');
});
The second question of how to identify each aside closing differently needs to be found out now.
With v2.1.1 of angular-strap the hide event for $aside was changed to aside-hide.
The AngularJS framework invokes handlers of $scope/$rootScope events with arguments:
scope.$on('aside.hide', function(event, data) {
console.log('aside... -hide');
console.log(event);
console.log(data);
});
Use those arguments to get information about the event.

Angular-Material "md-datepicker" directive - "md-open-on-focus" issue

As exampled in this fiddle, there's seems to be an inconsistency issue with opening the md-datepicker multiple times when using md-open-on-focus.
The problem occurs after opening and closing it for the first time (which works ok) - after that, it'll randomly open on click and become unstable.
<md-datepicker ng-model="timeModel" md-hide-icons="all" md-open-on-focus></md-datepicker>
Has anyone experienced the same behavior and found a solution? Thanks.
Currently, the problem with md-hide-icons="all" and md-open-on-focus used together is, when you click outside, the focus remains on the input. But, since there are no icons to click for and focus is already on the input, there is no way we can open the datepicker.
If you click outside, and click outside again, focus from the input is gone, and it will work normally from there on which should arguably be the expected behavior.
But if you don't want such behavior, we can do something to change it!
Now, having a look at the datePicker code, in the closeCalendarPane function, they have
self.calendarPaneOpenedFrom.focus();
which is responsible for keeping focus on input. If we remove it, it would lose focus while clicking outside (or selecting a date from the picker) which is exactly what we want. They have some code handling input when openOnFocus is true but not sure how that helps!
Forked jsfiddle (changed line is at #31449)
Also, changing the library code isn't what we would normally want to do. So, for now, you could have a workaround like having a callback on md-is-open and removing the focus from the input element inside callback using your favourite way (jQuery/angular.element or pure JS) [As mentioned by #quirimmo]
Hope that helps!
Unfortunately this functionality it's not implemented yet in angular material.
If you take a look to the official examples, you will see the same behavior in the one using md-open-on-focus.
https://material.angularjs.org/1.1.0/demo/datepicker
You need a little workaround in order to make it works. Just changed your example code:
HTML:
<md-datepicker ng-model="timeModel" md-hide-icons="all" md-is-open="isOpen" md-open-on-focus></md-datepicker>
JS:
angular.module('sandbox', ['ngMaterial'])
.controller('Ctrl', function($scope, $timeout) {
$scope.timeModel = new Date();
$scope.$watch('isOpen', function(newValue, oldValue) {
if (!newValue && oldValue) {
document.querySelector('.md-datepicker-input').blur();
}
});
});
https://jsfiddle.net/ecs9ao89/
You can achieve also the same behavior defining a new directive and requiring the base datepicker of angular material, and open it on click.
But this solution is smaller effort.

Suppress ng-click action upon ng-swipe-left

Button works perfectly on touchscreen when clicking or left-swiping:
<button ng-click="click(it)" ng-swipe-left="leftSwipe(it)" />
However, on the desktop where left-swipe is accomplished by combining a mouse-click with a left-drag, the click event is also fired.
Is there any way I could suppress this?
Well I didn't find anything simple, so here is one workaround and one semi-suppression, both rely on $timeout, but given you rely on human interaction I think we're ok.
Semi-Suppression: we'll want to ignore clicks this digest cycle and return to listen to the event next digest cycle.
$scope.leftSwipe = function(event){
angular.element(event.target).unbind('click');
$timeout(function(){angular.element(event.target)
.bind('click', function(it) {$scope.click(it);})},0);
};
Here we pass the event to the 'left-swipe' function in order to get the target element, if you do not want to pass the event as parameter (depending on your code) you can grab an element hardcoded id either with query ($('#yourButtonId')) or without (document.querySelector('#yourButtonId')) and use it instead. Take note that re-handling click event here will require passing the params (in your case 'it'?) again, that is why it is wrapped in another function and not called directly.
Workaround: I'd consider this much simpler but it's up to you and the code.
var hasSwiped = false;
$scope.click = function(event){
if (!hasSwiped){
...
}
};
$scope.leftSwipe = function(event){
hasSwiped = true;
$timeout(function(){ hasSwiped = false; },1000);
};
Here we simply create a variable 'hasSwiped' and set it to true once swiping and reseting it to false only after the 'click' event has fired (it depends on the app, but one second sounds reasonable to me between a swipe and click). In the click event just check this flag if raised or not.
Hope this helps,
Good Luck!

$watch fires on focus lost

I have a textarea tag with jquery.nicescroll pluggin and ng-model attached to it.
<textarea id="paper" ng-model="paper"></textarea>
In my code I apply a $watch on this ng-model variable.
$scope.$watch("paper", onTextChange);
Everything is good except that onTextChange is fired not only when I type something, but when I click away from textareaб and also when I switch to another tab.
How can I prevent it so that onTextChange is fired only when the text is changed, meaning when I type in something or delete chars?
Demo with instructions: plunker
here's a fix:
http://plnkr.co/edit/kycmUrthYU38Ukdz0jJG?p=preview
setTimeout(
function() {
$scope.$watch("paper", function(newtext, oldtext) {
if (newtext !== oldtext) {
onTextChange();
}
});
}, 100)
So the issue is that watch fires the function whenever angularjs tells the app to digest. What you did was tell it to call the 'change' function EVERY time, when you should have passed in a checker function to check the change happened. It's about 'watching', not about 'watching for changes' - the function argument is supposed to see if you need to do something.
Extra note:
AngularJS sets up watchers for all kinds of things on various elements - here's a bit more info. I believe the blur corresponds to ng-touched which triggers the digest What gets added to $scope.$$watchers by default in Angular? And what triggers $digests?
It is a bad idea to use setTimeout. Instead, you could use ng-model-options="{ getterSetter: true }" and write a method to get/set the value (Modified get/set Plunk) and handle the text change condition within this method.

Post-load event Angularstrap modal

I'm using angular chosen with angularstrap and i'm having problems with the initial value of the selector to be selected. The way i got it to work is i set a Timeout on the model attached to the selector to wait for the dom and then set the model value. So my guess is that chosen needs to wait for the dom to be created before it can initialize the selected option.
$scope.showModal = function() {
myModal.$promise.then(myModal.show);
// hack to make chosen load
$timeout(function () {
myModal.$scope.SelectedColor = "green";
}, 500 );
};
in my opinion this timeout solution is not a good one and i would like to find a better way to set the model after the dom has been created.
This is because chosen directive is calling trigger("chosen:updated") before the DOM is actually loaded. A fix would be adding $timeout() to the $watchCollection trigger.
This has been discussed and looks like the solution is here in the answer from kirliam.
Someone should issue a pull request for this issue.
edit: I issued a pull request for a fix regarding this issue. Hope it gets merged in.

Resources