Selectize & AngularJS not playing along with Select box (Multiple options) - angularjs

I've been trying to implement Selectize with AngularJS (1.2.4). I'm using this directive to interface with the plugin and everything is working smoothly until now. When using the ngModel from a normal select box It works fine, and returns the expected object but when I try to use it with the multiple attribute, it won't set the model.
I've inspected the DOM and appears the script removes unselected options from the hidden select and that might be messing with the angular binding.
I've created a Plunkr to demonstrate the behaviour.
http://plnkr.co/It6C2EPFHTMWOifoYEYA
Thanks

As mentioned in the comments above, your directive must listen to changes in the selectize plugin and then inform angular of what happened via ng-model.
First, your directive needs to ask for an optional reference to the ngModel controller with the following:
require: '?ngModel'.
It is injected into your link function as an argument in the 4th position:
function(scope,element,attrs,ngModel){}
Then, you must listen for changes in selectize with
$(element).selectize().on('change',callback)
and inform ngModel with ngModel.$setViewValue(value)
Here is a modified version of your directive. It should get you started.
angular.module('angular-selectize').directive('selectize', function($timeout) {
return {
// Restrict it to be an attribute in this case
restrict: 'A',
// optionally hook-in to ngModel's API
require: '?ngModel',
// responsible for registering DOM listeners as well as updating the DOM
link: function(scope, element, attrs, ngModel) {
var $element;
$timeout(function() {
$element = $(element).selectize(scope.$eval(attrs.selectize));
if(!ngModel){ return; }//below this we interact with ngModel's controller
//update ngModel when selectize changes
$(element).selectize().on('change',function(){
scope.$apply(function(){
var newValue = $(element).selectize().val();
console.log('change:',newValue);
ngModel.$setViewValue(newValue);
});
});
});
}
};
});
Also:
plunker
angular docs for ngModelController

Related

Custom directive doesn't get updated inside cellTemplate of ui-grid

I am using a custom directive named flag inside the cellTemplate of ui-grid. This directive just accepts a country name (USA or CANADA), and displays its corresponding flag. The directive works fine when the page loads for the first time. However, later even if I change the value of flag to anything, the custom directive doesn't update and display the new flag. Not sure what am I missing.
Plnkr
The directive gets compiled once! You need to watch the attribute or set a two way binding!
app.directive('flag', function() {
var flags = {
USA: 'https://github.com/hjnilsson/country-flags/raw/master/png100px/us.png',
CANADA: 'https://github.com/hjnilsson/country-flags/raw/master/png100px/ca.png'
};
return {
restrict: "A",
scope: {
flag: "#"
},
link: function(scope, elem, attrs) {
scope.$watch('flag', function(newValue, oldValue){
angular.element(elem).attr('src', flags[newValue]);
});
}
};
});
working plunkr ==> http://plnkr.co/edit/dcHDMEY8MpQnCnm99aDd?p=preview

angular: update directive when injected factory change data

I have a directive that gets its data from a factory. It works fine, when loads first time. But later, when the factory data changed, directive do not react on this changes. How can i fix it?
appWD.directive('widgetName', ['WidgetData', function(WidgetData) {
return {
restrict: 'E',
templateUrl: '_widget.html',
link: function(scope, elem, attrs) {
scope.data = WidgetData.GetWidgetData('widgetName');
//both do not work
//scope.$watch(scope.data);
//scope.$watch(WidgetData.GetWidgetData('widgetName'));
}
};
}]);
GetWidgetData('widgetName') returns object.
codepen example: http://codepen.io/anon/pen/xwLLxM?editors=101
Try using
scope.$watchCollection(function(){return WidgetData.getWidgetData('widget');}, function() {
alert('scope.$watchCollection');
});
$watchCollection "shallow watches the properties of an object and fires whenever any of the properties change" AngularJs Docs
I created a small app to replicate your problem. Here is the Demo
setTimeout(function(){
$scope.$apply()
},0);
put this after data updation, this vl update UI
May work

ngModel and How it is Used

I am just getting started with angular and ran into the directive below. I read a few tutorials already and am reading some now, but I really don't understand what "require: ngModel" does, mainly because I have no idea what ngModel does overall. Now, if I am not insane, it's the same directive that provides two way binding (the whole $scope.blah = "blah blah" inside ctrl, and then {{blah}} to show 'blah blah' inside an html element controlled by directive.
That doesn't help me here. Furthermore, I don't understand what "model: '#ngModel' does. #ngModel implies a variable on the parents scope, but ngModel isn't a variable there.
tl;dr:
What does "require: ngModel" do?
What does "model : '#ngModel'" do?
*auth is a service that passes profile's dateFormat property (irrelevant to q)
Thanks in advance for any help.
angular.module('app').directive('directiveDate', function($filter, auth) {
return {
require: 'ngModel',
scope: {
model : '#ngModel',
search: '=?search'
},
restrict: 'E',
replace: true,
template: '<span>{{ search }}</span>',
link: function($scope) {
$scope.set = function () {
$scope.text = $filter('date')($scope.model, auth.profile.dateFormat );
$scope.search = $scope.text;
};
$scope.$watch( function(){ return $scope.model; }, function () {
$scope.set();
}, true );
//update if locale changes
$scope.$on('$localeChangeSuccess', function () {
$scope.set();
});
}
};
});
ngModel is an Angular directive responsible for data-binding. Through its controller, ngModelController, it's possible to create directives that render and/or update the model.
Take a look at the following code. It's a very simple numeric up and down control. Its job is to render the model and update it when the user clicks on the + and - buttons.
app.directive('numberInput', function() {
return {
require: 'ngModel',
restrict: 'E',
template: '<span></span><button>+</button><button>-</button>',
link: function(scope, element, attrs, ngModelCtrl) {
var span = element.find('span'),
plusButton = element.find('button').eq(0),
minusButton = element.find('button').eq(1);
ngModelCtrl.$render = function(value) {
updateValue();
};
plusButton.on('click', function() {
ngModelCtrl.$setViewValue(ngModelCtrl.$modelValue + 1);
updateValue();
});
minusButton.on('click', function() {
ngModelCtrl.$setViewValue(ngModelCtrl.$modelValue - 1);
updateValue();
});
function updateValue(value) {
span.html(ngModelCtrl.$modelValue);
}
}
};
});
Working Plunker
Since it interacts with the model, we can use ngModelController. To do that, we use the require option to tell Angular we want it to inject that controller into the link function as its fourth argument. Now, ngModelController has a vast API and I won't get into much detail here. All we need for this example are two methods, $render and $setViewValue, and one property, $modelValue.
$render and $setViewValue are two ways of the same road. $render is called by Angular every time the model changes elsewhere so the directive can (re)render it, and $setViewValue should be called by the directive every time the user does something that should change the model's value. And $modelValue is the current value of the model. The rest of the code is pretty much self-explanatory.
Finally, ngModelController has an arguably shortcoming: it doesn't work well with "reference" types (arrays, objects, etc). So if you have a directive that binds to, say, an array, and that array later changes (for instance, an item is added), Angular won't call $render and the directive won't know it should update the model representation. The same is true if your directive adds/removes an item to/from the array and call $setViewValue: Angular won't update the model because it'll think nothing has changed (although the array's content has changed, its reference remains the same).
This should get you started. I suggest that you read the ngModelController documentation and the official guide on directives so you can understand better how this all works.
P.S: The directive you have posted above isn't using ngModelController at all, so the require: 'ngModel' line is useless. It's simply accessing the ng-model attribute to get its value.

AngularJS - Add attribute at runtime

I'm working on an AngularJS app. I'm trying to add the autofocus attribute to an element at runtime. The reason I want to do this is so I can set this attribute on one of several elements. How do you add an HTML attribute to an element via an AngularJS directive?
Thank you!
You can use the $set method on the $compile.directive.Attributes object. See the documentation here. This will create a new attribute which AngularJS will recognize. Remember to use the normalized (camelCase) version of the attribute. You can do it in the link function of your directive.
That's directive:
(function(){
angular.module('ng').directive('dynamicAutoFocus', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
scope.$watch(attrs.dynamicAutoFocus, function(id){
if(angular.isDefined(id)){
var target = document.getElementById(id);
if(target){
target.focus();
}
}
});
}
};
});
})();
Plunker

AngularJS: accessing the ngTouch service from a directive?

I really love how the new ng-click directive in Angular now automatically includes functionality for touch events. However, I am wondering if it is possible to access that touch-event service from my custom directive? I have lots of directives that require that I bind a click event to the given element, but I'm simply doing that using the typical jquery syntax (ex: element.on('click', function(){ ... })). Is there a way that I can bind an ng-click event to an element within a directive? Without having to manually put a ng-click tag on my element in the HTML of my view...?
I want to be able to harness the power of both click and touch events. I could obviously import a library (such as HammerJS or QuoJS) but I would prefer not to have to do that, especially since Angular is already doing it.
I can access the $swipe service and bind different elements to that, but is there a similar service for ngTouch?
For reference, this is an example of when I would want to do this:
mod.directive('datepicker', ['$timeout', function($timeout){
return {
link: function(scope, elem, attrs){
var picker = new DatePicker();
elem.on('click', function(e){
picker.show();
});
// I would rather do something like:
// elem.on('ngTouch', function(){ ... });
//
// or even:
// $ngTouch.bind(elem, {'click': ..., 'touch': ...});
}
}
}]);
UPDATE: As noted by below, the source code for the ng-click directive is here. Can anyone see a way to harness that code and turn it into a "bindable" service?
I don't think that's quite the right approach. I'd approach this by using a template within your directive and then using ngTouch within that.
mod.directive('datepicker', ['$timeout', function ($timeout) {
return {
template: '<div ng-touch="doSomethingUseful()"></div>',
link: function (scope, elem, attrs) {
var picker = new DatePicker();
scope.doSomethingUseful = function () {
// Your code.
}
}
}
}]);
UPDATE
Full example with additional attributes on the directive element:
http://codepen.io/ed_conolly/pen/qJDcr

Resources