OnChange callback for md-slider in Angular Material Design - angularjs

How can I know in controller that the value of < md-slider > from Angular Material Design has changed?

you can add ng-change on the directive
<md-slider min="0" max="50" ng-model="text" ng-change="myMethod()" md-discrete></md-slider>

I didn't find how to do it in a proper way, but you can do it by creating a directive and an event, like:
.directive('testDragEnd', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('$md.dragend', function() {
console.info('Drag Ended');
})
}
}
})
And the, you have to add the directive in the <md-slider>. (<md-slider test-drag-end></md-slider>).
I hope it helps.
In Angular Material's git hub there are few other events where you can do the same:
https://github.com/angular/material/blob/952ee3489e84226c73f83db15f8586db93cdca19/src/components/slider/slider.js
element
.on('keydown', keydownListener)
.on('$md.pressdown', onPressDown)
.on('$md.pressup', onPressUp)
.on('$md.dragstart', onDragStart)
.on('$md.drag', onDrag)
.on('$md.dragend', onDragEnd);

I used a $watch on the variable bound to the slider:
$scope.$watch(
function() {
return $scope.tex;
},
function(newValue, oldValue) {
$mdToast.show($mdToast.simple().content("Slider=" + newValue).position("top right").hideDelay(1500));
});
In my case my HTML looked like this:
<md-slider min="0" max="50" ng-model="tex" md-discrete></md-slider>
Good Luck.

I put a callback function in ng-blur, that worked for me. (Tried ng-change at first, but that became very slow).
Example:
<md-slider ng-model="gigabyte" ng-blur="myCallbackFunction()" id="myslider"> </md-slider>
<input flex type="number" ng-model="gigabyte" ng-blur="myCallbackFunction()" aria-controls="myslider">

ng-mouseup should do the trick. If you want to add logic (as I did) to ensure your value has changed before make your db call to update, you can add that inside your saveChanges() function inside your controller.
<md-slider ng-mouseup="vm.saveChanges(vm.myModel)"></md-slider>

Related

In an AngularJS custom directive, can I add a built-in directive to the existing element?

In AngularJS, if a custom directive is to be used by adding an attribute:
<input type="text" my-directive>
then can I add an ng-keypress="handleKeypress()" to the element? (without modifying the original HTML, that is) I tried doing that in the link function:
link: function(scope, elem, attrs) {
attrs["ngKeypress"] = "handleKeypress()";
}
but it doesn't seem to work. If attrs["ng-keypress"] is used instead of attrs["ngKeypress"], also nothing happens. I can also use jQuery or jqLite's
elem.on("keypress", function(ev) { ... });
but I am also thinking of just using Angular's way of adding an ngKeypress if possible.
Try this,
link: function(scope, element) {
element.attr('ng-keypress', 'handleKeypress()');
element.removeAttr("my-directive"); //remove your directive attribute to restrict indefinite loops
$compile(element)(scope);
}
DEMO
<input type="text" ng-keypress="yourFunction()" my-directive> will work.
Here is jsbin for that

AngularJS prevent ngModel sync

I have a simple directive called po-datepicker, it displays a datepicker on the screen, but allows the user to type a date manually:
<input type="text" ng-model="model" po-datepicker required />
and this is the directive:
myApp.directive('poDatepicker', function () {
return {
require: ['?^ngModel'],
restrict: 'A',
link: function ($scope, elem, attrs, ctrl) {
var ngModel = ctrl[0];
var picker = elem.datepicker();
picker.on('changeDate', function(e) {
ngModel.$setViewValue(e.date);
...
});
elem.parent().find('button').on('click', function() {
picker.datepicker('show');
});
var changeFn = function(e) {
// Here I have some logic that calls $setViewValue();
};
picker.on('hide', changeFn);
elem.on('keyup blur', changeFn);
}
};
});
this works as expected, but when I try to type a value in the input, it updates the ngModel, changing the variable in the scope, how can I prevent ngModel from being changed in the input?
Here is a plunkr, try manually writing a value and you'll understand what I'm talking.
Actually, after some research, I found a solution for this problem.
What I found on forums and questions is that I needed to unbind the element's events, like this:
elem.unbind('input').unbind('keydown').unbind('change');
But that solution didn't work as expected.
The problem is that I'm currently using Angular 1.2.x, I found out that you need also to set some priority to the directive, such as:
return {
require: ['?^ngModel'],
priority: 1,
...
}
The priority: 1 is needed in this case, because of the priority of some internal Angular.js directives.
Here is an updated plunker with the right priority set up.
Just add 'disabled' to the input http://plnkr.co/edit/xFeAmSCtKdNSQR1zbAsd?p=preview
<input type="text" class="form-control" ng-model="test" po-datepicker required feedback disabled/>

Why is this code broken in AngularJS 1.2?

Goodevening,
The following code seems to work in older versions of Angular, but not in the 1.2 version. I am not sure why that is. What happens in Angular 1.2, is that the $watch directive does not get hit. It does not execute. It does execute in Angular 1.0.1.
Any idea why?
HTML
<fieldset validatedmarker>
<legend>User</legend>
<input type="text" name="name" class="form-control" ng-model="name" required>
</fieldset>
And the JS.
var APP = angular.module('myApp', []);
APP.directive('validatedmarker', function() {
return {
restrict: 'AE',
scope: { },
link: function(scope,e,a) {
scope.$watch(function() {
return scope.name;
}, function(newValue, oldValue) {
console.log("change detected: " + newValue)
});
}
};
});
Working example (Angular 1.0.1)
http://jsfiddle.net/2w8xW/
Non working example (Angular 1.2.0)
http://jsfiddle.net/24P86/
Thanks!
The most probable reason for this is the treatment of isolated scopes, that has changed in AngularJS 1.2.0
In earlier version everything inside the directive element got the isolated scope, but now the elements inside the directive get the original parent scope. So when you are setting the value of scope.name you are setting on parent scope. The watch is on child scope.
See this SO answer AngularJS Scope differences between 1.0.x and 1.2.x and the changelog.
To make your example work you would have to create a template for your directive and set transclude: true in directive definition.
the main reason why this is hapening is probably due to the fact that you are creating an isolated scope over an already liked html since you are not providing a template but rather using the existing html wich is most likely to be linked to the parent scope so you should take your directives innerhtml and use it as a template for your directive
like
APP.directive('validatedmarker', [function() {
return {
restrict: 'AE',
scope: { },
template:'<fieldset><legend>User</legend><input type="text" name="name" class="form-control" ng-model="name" required="true"/>',
replace:true,
link: function(scope,e,a) {
scope.$watch(function() {
return scope.name;
}, function(newValue, oldValue) {
console.log("change detected: " + newValue)
});
}
};
}]);
<div validatedmarker></div>
you can see it here
http://jsfiddle.net/24P86/1/
You are specifying an isolated scope, but with no attributes inside. You can either add attributes inside the scope property of your directive, or remove it altogether if you don't need to isolate your directive's scope.
More info on the attributes can be found here (search on 'isolate' scope):
https://docs.angularjs.org/api/ng/service/$compile

Angular.js child input element not getting parents scope

This should be pretty simple, but for some reason when I have
<div class="file-navigator" ng-controller="FileSystemCtrl">
<input type="file" id="openFile" ng-model="path" ng-change="openFolder()" nwdirectory />
The ng-change doesn't get triggered.
If I use
onchange="angular.element(this).parent().scope().openFolder()"
the onchange event gets triggered, but obviously, that's ugly.
The FileSystemCtrl is defined as a module which I'm importing into my app, it's structured like this.
angular.module('myApp.FileSystemModule',[])
.factory('FileSystemModel',function($rootscope){...})
.controller('FileSystemCtrl',function(){...});
Any ideas why the child doesn't know about it's parent controller? Particularly as the child doesn't have a controller of it's own?
AngularJs doesn't support input with type file. See this issue. And this. Your onchange event is the best option for now.
Another way would be to use a directive taking advantage of $compile to interact with a ng-model:
.directive('path', function($compile) {
return {
restrict: 'E',//<path></path> in your markup
replace: true,
template: '<input type="file" />',
link: function(scope, elem, attrs) {
var textField = $(elem).attr('ng-model', 'something');
$compile(textField)(scope);
$(elem).change(function() {
//do stuff
});
}
};
});
I didn't test it, but it provides you with a get-go.

AngularJS - setting focus to element using NON-ISOLATE directive

I know this question has been asked about 100 times (trust me, I've read them all), but I'm having trouble getting focus to go to an input box when the directive does NOT use isolate scope. The scope.$watch doesn't fire when the backing data changes.
Why not just use the one with isolate scope, you ask? Well, my understanding is that you should ONLY use isolate scope if your directive has a template.
The only differences in the directives is:
// works
app.directive('doesFocus', function ($timeout) {
return {
scope: { trigger: '#doesFocus' },
link: function (scope, element) {
scope.$watch('trigger', function (value) {
// sets focus
}
...
// does not work, and in fact when I inspect attrs.doesNotFocus it is undefined
app.directive('doesNotFocus', function ($timeout) {
return {
scope: false,
link: function (scope, element, attrs) {
scope.$watch(attrs.doesNotFocus, function (value) {
// sets focus
}
...
I'm on week 3 of using Angular, so I must be missing some silly semantic issue.
Here is a fiddle illustrating my issue.
http://jsfiddle.net/tpeiffer/eAFmJ/
EDIT
My actual problem was that my real code was like this (hazard of mocking the problem, you sometimes mask the real problem):
<input should-focus="{{isDrawerOpen()}" ... ></input>
but because I was using a function, not a property, I was missing the required ticks
<input should-focus="{{'isDrawerOpen()'}}" ... ></input>
Making this change fixed the problem and my directive can still be like this:
scope.$watch(attrs.shouldFocus, focusCallback(newValue));
END EDIT
Thanks for helping me in my quest for angular excellence!
Thad
Remove {{}} from your HTML. So instead of:
<input class="filter-item" placeholder="Enter filter"
does-not-focus="{{bottomDrawerOpen}}" type="text">
use
<input class="filter-item" placeholder="Enter filter"
does-not-focus="bottomDrawerOpen" type="text">
Then it works with watching attrs.doesNotFocus:
scope.$watch(attrs.doesNotFocus, function (value) {...} );
Fiddle
Your bottom drawer was watching a function isDrawerOpen(), not a property.
Change
scope.$watch('isDrawerOpen()',...);
to
scope.$watch('toggleBottomDrawer',...);

Resources