Apply kendo dropdownlist style only on angular select - angularjs

I have a select which is being populated using angular binding.
<select class='clsBucket' id='optBuckets' ng-options='opt as opt.name for opt in buckets' ng-model='bucketSelected' ng-change='changeBucket()'>
Now i want to apply the Kendo dropdownlist style on this select , but i don't want to populate the options using kendo datasource etc and continue to do that using angular.
If i use $('#optBuckets').kendoDropDownList() then i get the requiired style applied but the binding data is lost.
Any help in order to resolve that is highly appreciated.

The above code lists "buckets" as the data source. With that in mind, the promise which assigns 'buckets' to the scope should have it's promise exposed on the scope. From there a directive can access it (here called 'bucketsPromise')
The code in the controller may look like such:
$scope.bucketsPromise = bucketsService.get().then(function(data) {
$scope.buckets = data;
}).promise;
The directive will the appear as such:
.directive('angularToKendoDropdown', function() {
return {
scope: {
'bindToCtrl': '&dataSourcePromise'
},
link: function(scope, element, attr) {
scope.bindToCtrl.then(function() {
$(element).kendoDropDownList();
})
}
};
});
The given select would appear as such:
<select class='clsBucket angular-to-kendo-dropdown' id='optBuckets'
ng-options='opt as opt.name for opt in buckets'
ng-model='bucketSelected' ng-change='changeBucket()'
data-source-promise='bucketsPromise'>
</select>

Related

Providing id values for each option in ng-options

I have a select box that looks like this:
<select ng-model="ctrl.arrayVal" ng-options="option for option in ctrl.myarray"></select>
where ctrl.myarray looks something like:
ctrl.myarray = [100, 200, 700, 900];
This all works great, but for automated testing purposes I need to provide an id to each of the dynamically generated options. So for example if I rewrote this using ng-repeat then I would need something like this:
<select ng-model="ctrl.arrayVal">
<option id="array-{{option}}" ng-repeat="option in ctrl.myarray" value="{{option}}">{{option}}</option>
</select>
Notice the array-{{option}} id that is possible in this second snippet using ng-repeat. I need to provide a similar id but using the first ng-options method.
Is there a way to do this?
Currently ng-options only lets you specify the value property of the <option> element using the select as or track by syntax. It is also possible to add the disabled property if your data source is an object (not an array). It is not possible to further customize the fields of the <option> element including id.
Docs on ngOptions
Maybe you can achieve your goal with a custom directive. For example:
angular.module('myApp', [])
.directive('idAdder', function() {
return {
link: function(scope, element, attrs) {
scope.$watch(attrs.ngModel, function() {
scope.$evalAsync(function() {
angular.forEach(element.find('option'), function(o, k) {
o.id = o.value;
});
});
});
}
}
});
Then, use it as a custom attribute:
<select ng-model="ctrl.arrayVal"
ng-options="option for option in ctrl.myarray"
id-adder></select>
The $watch and $evalAsyncare used to execute the function after the options have been populated/created.

Custom Directive : Dropdown selected value not binding

I'm trying to create a custom directive for a drop down control in AngularJS 1.4.4. I can handle the selected event, but i can not get the binding for what is selected in the drop down list.
I want to call this from Html markup the following way.
<my-dropdown-list source="myList" destination="mySelection" />
The angular js custom directive is here.
(function() {
var directive = function($compile) {
return {
restrict: 'E',
scope: {
model: '=source',
selectedValues: '=destination'
},
controller: function($scope) {
$scope.onSelChange = function() {
alert('called');
console.log($scope.selectedItem.Code, $scope.selectedItem.Name);
};
// $scope.selectedItem is always undefined here.
},
link: function ($scope, $elem) {
var rowHtml =
'<select ng-options="item as item.Name for item in model" ng-model="selectedItem" ng-change="onSelChange()"></select>';
$elem.html(rowHtml);
$compile($elem.contents())($scope.$new());
}
};
};
my.directive('myDropdownList', directive);
})();
I'm new to Angular, so this may be something small that i missed here, but i can't seem to get a value for 'selectedItem'
I find out this in AgularJS document
Note that the value of a select directive used without ngOptions is
always a string. When the model needs to be bound to a non-string
value, you must either explictly convert it using a directive (see
example below) or use ngOptions to specify the set of options. This is
because an option element can only be bound to string values at
present.
Link: https://docs.angularjs.org/api/ng/directive/select
You should use ngRepeat to generate the list like this post:
Angularjs: select not updating when ng-model is updated

How do I reference the scope/model property of an element from within an AngularJS directive?

I am using a custom directive to attach the jQuery Chosen plugin to a multi-select element. (I realize there are similar native AngularJS plugins out there, but I want to learn how to integrate a jQuery plugin the right way because I'm sure I will eventually come across a requirement for which there exists only a jQuery plugin.)
The element is already bound to a scope model property, and in the directive, I attach a watch handler on this model property to ensure that the plugin's update/refresh function is called whenever it changes. However, I'm currently doing this by hard-coding the name of the model property into the directive, which is obviously not ideal. I want the directive to be able to figure that out on its own, so that it can be used in a variety of situations. I suppose I could pass it into the directive via an attribute value, but I would prefer if the directive was smart enough to figure it out on its own.
Is there a reference path available from the element object to its bound scope model property?
HTML:
<div ng-app="testApp" ng-controller="testController">
<select multiple ng-model="selection" jquery-ng-chosen>
<option value="1">First option</option>
<option value="2">Second option</option>
</select><br/>
<button type="button" ng-click="selection = []">Clear</button>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chosen/1.1.0/chosen.jquery.min.js"></script>
JS:
var testApp = angular.module('testApp', []);
testApp.controller('testController', function($scope) {
});
testApp.directive('jqueryNgChosen', function () {
return {
link: function (scope, element, attrs) {
element.chosen();
scope.$watch('selection', function () {
element.trigger("chosen:updated");
});
}
};
});
Fiddle. Notice that the clear button works properly because the directive has used a watch to update the jQuery chosen plugin. (Note: the jQuery JS and Chosen CSS links are omitted because they are defined from within the jsFiddle.)
Whenever you create a reusable directive like this you want to be sure to give it isolate scope (https://egghead.io/lessons/angularjs-understanding-isolate-scope)
In your isolate scope object you can bind the value of the ng-model attribute to your directive's scope with something like this: model: "=ngModel" Then you just have to watch the model property you put on your directives scope. You can choose to deep watch that property by passing true as the 3rd parameter to watch, although in this example you wouldn't need to
testApp.directive('jqueryNgChosen', function () {
return {
scope: {
'model': '=ngModel',
},
link: function (scope, element, attrs) {
element.chosen();
scope.$watch('model', function (newValue, old) {
element.trigger("chosen:updated");
}, true);
}
};
});
http://jsfiddle.net/z4vqxdtv/
To get the value in the ng-model attributes, you could use attrs.ngModel like this:
link: function (scope, element, attrs) {
element.chosen();
scope.$watch(attrs.ngModel, function () {
element.trigger("chosen:updated");
});
}
This way, you don't have to be worried if the attribute is actually be ng-model, data-ng-model, ng:model or other variant. It will be normalized by angular.
Hope this helps.

How to update attribute on element via directive using angularjs

I have a pretty simple case in AngularJS where:
<select ng-repeat="el in elms" disabled="disabled" remove-disable>
<option>make a selection</option>
</select>
Initially my select is empty and so I added the disable attr to avoid having people click on it.
When the ajax call is completed and the select renders the list of options I want to remove the disable attribute.
It looks straight forward, right? but all I have seen is approaches using $watch and not for exactly this case.
I'm approaching it from a jQuery point of view where an looking at the DOM after the ajax call, finding the element and removing the attr. like this:
$('select').removeAttr('disabled');
Unfortunately I don't want to do jQuery, I want to do it with a directive, since that is what is for. the angular folks say that all DOM manipulations should be done via directives so I will like to know just how.
enrollmentModule.directive('removeDisable', function () {
return {
restrict: 'A',
scope: {
ngModel : '='
},
link: function (scope, element, attrs) {
console.log('no people yet');
if (element[0].complete) {
console.log('element finish rendering');
};
scope.$watch(attrs.ngModel, function () {
console.log('agents arrived');
});
}
};
});
AngularJS has a ngDisabled directive that you can use to make the link between the state of the list and an expression :
<select ng-repeat="el in elms" ng-disabled="elms.length == 0">
<option>make a selection</option>
</select>

How to create this custom control with AngularJS directive?

I'm a bit new to AngularJS and am trying to write a custom select control based on Zurb Foundation's custom select(see here: http://foundation.zurb.com/docs/components/custom-forms.html)
I know I need to use a directive for this but am not sure how to accomplish this.
It's going to have to be reusable and allow for the iterating of whatever array is passed in to it. A callback when the user selects the item from the dropdown list is probably needed.
Here is the markup for the custom Foundation dropdown list:
<select name="selectedUIC" style="display:none;"></select>
<div class="custom dropdown medium" style="background-color:red;">
Please select item
<ul ng-repeat="uic in uics">
<li class="custom-select" ng-click="selectUIC(uic.Name)">{{uic.Name}}</li>
</ul>
</div>
This works for now. I am able to populate the control from this page's Ctrl. However, as you can see, I'd have to do this every time I wanted to use a custom dropdown control.
Any ideas as to how I can turn this baby into a reusable directive?
Thanks for any help!
Chris
If you want to make your directives reusable not just on the same page, but across multiple AngularJS apps, then it's pretty handy to set them up in their own module and import that module as a dependency in your app.
I took Cuong Vo's plnkr above (so initial credit goes to him) and separated it out with this approach. Now this means that if you want to create a new directive, simply add it to reusableDirectives.js and all apps that already have ['reusableDirectives'] as a dependency, will be able to use that new directive without needing to add any extra js to that particular app.
I also moved the markup for the directive into it's own html template, as it's much easy to read, edit and maintain than having it directly inside the directive as a string.
Plnkr Demo
html
<zurb-select data-label="{{'Select an option'}}" data-options="names"
data-change-callback="callback(value)"></zurb-select>
app.js
// Add reusableDirectives as a dependency in your app
angular.module('angularjs-starter', ['reusableDirectives'])
.controller('MainCtrl', ['$scope', function($scope) {
$scope.names = [{name: 'Gavin'}, {name: 'Joseph'}, {name: 'Ken'}];
$scope.callback = function(name) {
alert(name);
};
}]);
reusableDirectives.js
angular.module('reusableDirectives', [])
.directive('zurbSelect', [function(){
return {
scope: {
label: '#', // optional
changeCallback: '&',
options: '='
},
restrict: 'E',
replace: true, // optional
templateUrl: 'zurb-select.html',
link: function(scope, element, attr) { }
};
}]);
zurb-select.html
<div class="row">
<div class="large-12 columns">
<label>{{label || 'Please select'}}</label>
<select data-ng-model="zurbOptions.name" data-ng-change="changeCallback({value: zurbOptions.name})"
data-ng-options="o.name as o.name for o in options">
</select>
</div>
</div>
Is something like this what you're looking for?
http://plnkr.co/edit/wUHmLP
In the above example you can pass in two attribute parameters to your custom zurbSelect directive. Options is a list of select option objects with a name attribute and clickCallback is the function available on the controller's scope that you want the directive to invoke when a user clicks on a section.
Notice there's no code in the link function (this is where the logic for your directive would generally go). All we're doing is wrapping a template so that it's reusable and accepts some parameters.
We created an isolated scope so the directive doesn't need to depend on parent scopes. We binded the isolated scope to the attribute parameters passed in. The '&' means bind to the expression on the parent scope calling this (in our case the callback function available in our controller) and the '=' means create a two way binding between the options attribute so when it changes in the outter scope, the change is reflected here and vice versa.
We're also restricting the usage of this directive to only elements (). You can set this to class, attributes, etc..
For more details the AngularJs directives guide is really good:
http://docs.angularjs.org/guide/directive
Hope this helps.

Resources