I am trying to create an attribute directive (because I need to use this logic at multiple places in my project) for Select dropdown which automatically selects the only item if the items array has only one element.
I have the following HTML with an attribute directive named smart-select-dropdown:
<select class="form-control" ng-model="selectedItem" smart-select-dropdown
ng-options="item as item.name for item in items | filter: {someFilter}">
<option value="" ng-bind="Select Item"></option>
</select>
Here is my directive code:
angular.module('app').directive('smartSelectDropDown', function(){
return{
restrict: 'A',
replace: true,
link: function(scope, element){
angular.element(document).ready(function () {
if(element[0].options.length === 2){
element.find('select option:nth-child(2)').prop('selected', true);
}
});
}
}
});
Here is the controller code:
angular.module('app').controller('MyCtrl', function($scope){
$scope.selectedItem = null;
$scope.items = [
{
id : 1,
name : 'Item 1'
},
{
id : 2,
name : 'Item 2'
},
{
id : 3,
name : 'Item 3'
}
];
});
The problem I am facing is that element[0].options.length is not giving me the correct length. It is always giving me 1 which is actually the default 'Select Item' option. It does not contain the length of ng-options which is actually required. Can anyone let me know what am I missing here?
PS: I am using AngularJS version 1.2.17
This can be achieved without a directive.
Controller Code changes:
angular.module('app').controller('MyCtrl', function($scope){
$scope.items = [
{
id : 1,
name : 'Item 1'
},
{
id : 2,
name : 'Item 2'
}
]
});
$scope.selectedItem = items[1]; // or items[0] as per your requirement
HTML Code Changes:
<select class="form-control" ng-model="selectedItem"
ng-options="item as item.name for item in items track by item.id">
<!-- You may choose to remove this first option tag with in select -->
<option value="" ng-bind="Select Item"></option>
</select>
There is even no need of a directive in your case, you can just check the $scope.items length and assign the first element to ng-model if its length is 1
Controller:
angular.module('app').controller('MyCtrl', function($scope){
$scope.selectedItem = null;
$scope.items = [
{
id : 1,
name : 'Item 1'
},
{
id : 2,
name : 'Item 2'
}
]
if($scope.items.length == 1){
$scope.selectedItem = $scope.items[0];
}
});
HTML:
<select class="form-control" ng-model="selectedItem" ng-options="item as item.name for item in items">
<option value="" ng-bind="Select Item"></option>
</select>
You can get the selectedItem and items from the controller to your directive scope, and check the condition and set selectedItem accordingly.
angular.module('app').directive('smartSelectDropDown', function(){
return{
restrict: 'A',
replace: true,
scope: {
items: '=',
selectedItem: '=selectedItem'
}
link: function(scope){
if(scope.items.count == 1){
scope.selectedItem = scope.items[0];
}
}
});
}
}
});
Though, you might have to modify the code for some corrections as I have not tested it.
Note: if you are using directive just for this purpose, then follow #Rangamannar answer.
Related
I have a drop down component in angularjs which when used single time works fine and the values are selected but if I use the same component multiple times in ng-repeat the drop downs are rendered but the values are not being selected. I am using the following code:
JS file:
angular.module('mainApp').component('inputSelectComponent', {
templateUrl : 'sys/templates/input-select.template.html',
bindings : {
ngModel : '='
},
controller: ['$scope', '$element', '$http', 'translate', 'apiServer', 'session', 'EVENTS', function compCategoriesDropdownController($scope, $element, $http, translate, apiServer, session, EVENTS) {
var _select = $element.find('select');
$scope.options = JSON.parse($element.attr('options'));
setTimeout(function(){
$element.find('select').selectize({});
}, 0);
$scope.$watch('$ctrl.ngModel', function(e){
var selectize = _select[0].selectize;
if (typeof(selectize) !== 'undefined') {
setTimeout(function(){
selectize.setValue(e);
}, 0);
}
});
$scope.$watch('$element.attr', function(e){
$scope.label = $element.attr('label');
if ($element.attr('disabled') === 'disabled'){
_select.attr('disabled', 'disabled');
}
});
$element.find('select').on('change', function(e){
$scope.$ctrl.ngModel = e.target.value;
});
}]
});
Template file:
<label class="col-form-label">{{label}}</label>
<select class="cat-drop{{(error!=null)? ' not-validated' :''}}">
<option ng-repeat="(key, value) in options" value="{{key}}">
{{value}}
</option>
</select>
This is how I am calling it:
<div ng-repeat="relatedproduct in relatedproductsData">
<span class="serial-number">{{ $index + 1 }}.</span>
<div class="form-group row">
<input-select-component ng-model="relatedproduct.item_type" class="input-component col-sm-2" label="{{t('related_products_item_type')}}" options='{"2": "2 - For Product", "3": "3 - For Category"}'></input-select-component>
</div>
</div>
Any help is appreciated!
I used a directive instead of an component.
The documentations says:
Components only control their own View and Data: Components should never modify any data or DOM that is out of their own scope. ..
Using a directive allows you to usa ng-model="ngModel" on the select component in the directive and change the value directly.
angular.module('mainApp').directive('inputSelectComponent', function () {
return {
template: `<label class="col-form-label">{{label}}</label>
<select class="cat-drop{{(error!=null)? ' not-validated' :''}}" ng-model="ngModel">
<option ng-repeat="(key, value) in options" value="{{key}}">
{{value}}
</option>
</select>`,
scope: {
ngModel: '=',
options: '=',
}
}
});
it does work in the caller:
<div ng-repeat="relatedproduct in [{ item_type : '' }, {item_type : ''}, { item_type: '' }]">
<span class="serial-number">{{ relatedproduct }}.</span>
<div class="form-group row">
<input-select-component ng-model="relatedproduct.item_type"
class="input-component col-sm-2"
label="{{t('related_products_item_type')}}"
options='{"2": "2 - For Product", "3": "3 - For Category"}'></input-select-component>
</div>
</div>
Good luck!
Try using $timeout instead of setTimeout. Maybe because of angularJS digest...
I want to add a chip in the above box when i select it from the drop down so i have created a filter for that and i have passed an array to it bt now the function which was adding the chip to the above box is now not working i don't know why.
The controller code:
var app = angular.module("myApp", []);
app.controller("appController", function ($scope) {
$scope.selected = [{
'id': 1,
'state': 'UP'
}, {
'id': 2,
'state': 'Delhi'
}];
$scope.options = [{
'id': 1,
'state': 'UP'
}, {
'id': 2,
'state': 'Delhi'
}, {
'id': 3,
'state': 'Haryana'
}, {
'id': 4,
'state': 'WB'
}]
$scope.add = function (item) {
console.log(item);
let insert = true;
item = JSON.parse(item);
for (let i = 0; i < $scope.selected.length; i++) {
if (angular.equals(item, $scope.selected[i])) {
insert = false;
break;
}
}
if (insert == true) {
$scope.selected.push(item);
$scope.selected = angular.copy($scope.selected);
}
};
$scope.remove = function (item) {
let a = item;
let b = $scope.selected.indexOf(a);
$scope.selected.splice(b, 1);
};
});
app.directive("filter", function () {
return {
restrict: 'E',
scope: {
param: '=',
array: '=',
fun: '&'
},
template: "<div ><select ng-change='fun(items)' ng-model='items'><option value='' selected disabled hidden>Choose Here</option> <option ng-repeat='item in array' value={{item}}> {{item.state}}</option> </select>"
};
});
And the view:
<body ng-app="myApp" ng-controller="appController">
<div>
<div class="chip" ng-repeat="chips in selected">
{{ chips.state }}
<span class="closebtn" ng-click="remove(chips)">×</span>
</div>
</div>
<filter array='options' fun='add(param)'></filter>
</body>
The complete code can be found in this fiddle.
I've had a look at this code. Apart from a few errors, the only problem I see is passing data from the directive scope back to the parent. My suggestion would be to refactor the directive as:
app.directive("filter", function() {
return {
restrict: 'E',
scope: {
// selection: '=',
array: '=',
fun: '&'
},
template: `<div>
<select ng-change='fun({ selection: selection })' ng-model='selection'>
<option value='' selected disabled hidden>Choose Here</option>
<option ng-repeat='item in array' value={{item}}> {{item.state}}</option>
</select>
</div>`
};
});
as you can see, you don't really need selection in the parent, as it belongs to the 'filter' component. However, you share this data by passing it to the fun attribute (which is the function you call on selection change).
A simplified version of the controller would then be:
$scope.add = function(selection) {
var item = JSON.parse(selection);
for (let i = 0; i < $scope.selected.length; ++i) {
if ($scope.selected[i].id === item.id) {
return;
}
}
$scope.selected.push(item);
};
$scope.remove = function(item) {
let index = $scope.selected.indexOf(item);
$scope.selected.splice(index, 1);
};
A working version of the initial fiddle can be found here.
change below line in your directory
template: "<div ><select ng-change='fun(items)' ng-model='items'><option value='' selected disabled hidden>Choose Here</option> <option ng-repeat='item in array' value={{item}}> {{item.state}}</option> </select>"
to
template: "<div ><select ng-change='fun({param: items})' ng-model='items'><option value='' selected disabled hidden>Choose Here</option> <option ng-repeat='item in array' value={{item}}> {{item.state}}</option> </select></div>"
I am trying to create a few different directives that will work as search / filter tools for different parts of my application.
For this purpose i have created the following directive code:
app.directive("lbFilterDivision", ['divisionService', function (divisionService) {
return {
restrict: "E",
templateUrl: 'tpl/directives/lb-filters/lbFilterDivision.html',
scope: {
model: '='
},
link: function (scope, element, attr) {
scope.divisions = [];
divisionService.getList().then(function (result) {
scope.divisions = result;
})
}
};
}]);
The template attached to this is:
<select class="form-control"
ng-model="model"
ng-options="item.id as item.name for item in divisions"
fix-select-null="">
<option value="" translate="FORMS.DIVISION_PLACEHOLDER"></option>
Okay first of all let me explain the main idea.
The idea is that you have a search variable that will be passed to the directive. Then the two way binding should notify up through the system.
So say for instance i have the following HTML:
<lb-filter-division model="search.division.id"></lb-filter-division>
<li ng-repeat="user in users | filter:search"> </li>
As you can see i set the model = to search.division.id which means every time i change selected variable it should update the search.division.id variable and filter the list.
Sadly this is not the case.
Can anyone see what ive done wrong?
Edit - I found the answer. Apprently there was a syntax error in my code. Im so sorry! i will leave this code here if someone gets the same idea as my self.
Here is a fiddle:
fiddle
Solved the problem.
If you wish to copy or are looking to solve the same issue i can refer to this fiddle i made:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.users = [
{id: 1, name: "div1", division:{id: 1, name: 'hello'}},
{id: 2, name: "div2", division:{id: 2, name: 'hello2'}},
{id: 3, name: "div3", division:{id: 3, name: 'hello3'}}
]
}
myApp.directive("lbFilterDivision", function () {
return {
restrict: "E",
scope: {
model: '='
},
template: '<select ng-model="model" ng-options="item.id as item.name for item in divisions"></select>',
link: function (scope, element, attr) {
scope.divisions = [{id: 1, name:'hello'},{id: 2, name:'hello2'},{id: 3, name:'hello2'}];
}
};
});
fiddle
Good luck!
I've created a directive for a select input to select a userId. The model binds from the directive to the rest of the view. However, when I set the id from the controller it doesn't seem to bind to select input in the directive.
Controller:
app.controller('MainCtrl', function($scope) {
// set userId to willem's Id
$scope.userId = 3;
});
Directive:
app.directive('selectUser', function() {
return {
restrict: 'E',
scope: {
ngModel: '='
},
controller: function($scope) {
$scope.users = [{
"id": 1,
"name": 'Tupac'
}, {
"id": 2,
"name": 'Biggie'
}, {
"id": 3,
"name": 'Willem'
}];
},
templateUrl: 'directive.html'
};
});
index.html
<body ng-controller="MainCtrl">
users:
<select-user ng-model="userId"></select-user>
userId = {{userId}}
</body>
directive.html
<select class='form-control focus' ng-model="ngModel">
<option value="">all users</option>
// doesn't bind from the controller. Supposed to show willem, instead of all users to start with.
<option ng-Repeat="user in users" value="{{user.id}}">{{user.name}}</option>
</select>
Working example on: http://plnkr.co/edit/c7eyoB
You should use ngOptions :
<select class='form-control focus' ng-model="ngModel" ng-options="user.id as user.name for user in users">
<option value="">all users</option>
</select>
Then the binding will work. See updated plnkr
Edit, concerning the following question in comment :
could you please explain, why it is not working with ng-repeat?
You can achieve the same visual result with :
<select class='form-control focus' ng-model="ngModel">
<option value="">all users</option>
<option ng-repeat="user in users" value="{{user.id}}" ng-selected="user.id === ngModel">{{user.name}}</option>
</select>
I.e. we added ng-selected="user.id === ngModel".
But this has some drawbacks. First, you are creating unneeded isolated scopes. And also, the values bound to the options are strings, i.e. you will actually select '1', '2' or '3' instead of 1, 2 or 3.
I am new to angularjs and working to directive for a dropdown but having problem to select initial value. I have to pass the Id of the option to select as an attribute to the directive. I am setting the attribute value in a scope variable which I then use in ng-Init.
Here is my directive code:
export class ChoiceGroup
{
constructor ()
{
var directive: ng.IDirective = {};
directive.restrict = "E";
directive.replace = true;
directive.template = '<div><select name="cmbCG" ng-model="dataSel" ng-options="c.DisplayName for c in data"></select></div>';
directive.link = function ($scope: any, element: JQuery, attributes: any) {
var injector = angular.element(document).injector();
var key = attributes.key;
var cgCacheService: ChoiceGroupsCacheService;
seCGCacheService = injector.get('seChoiceGroupsCacheService');
var values: Array<ChoiceValue>;
values = seCGCacheService.GetChoiceValues(key);
$scope.data = values;
if (attributes.selectedvalue) {
$scope.selectedvalue = values[attributes.selectedvalue];
}
}
return directive;
}
The code is in Typescript. Here is HTML:
<choicegroupcombo key="RecurrenceFrequency" selectedvalue="3"></choicegroupcombo>
If I hardcode the value of dataSel ie ng-init="dataSel=3" then it works fine but when I set to scope variable then it does not work. So how can I fix the issue.
EDIT
Solved. I have updated the code accordingly.
I think this might be a classic case of "over-engineering".
Your code doesn't seem to be trying to do anything other than what a standard select box does.
Try this instead:
app = angular.module('app', [])
app.controller 'MainCtrl', ($scope) ->
# $scope.values = ChoiceGroupsCacheService(key)
$scope.values = [
{ id: 0, label: 'Zero' }
{ id: 1, label: 'One' }
{ id: 2, label: 'Two' }
{ id: 3, label: 'Three' }
]
$scope.number = 2
and the view:
<select ng-model="number" ng-options="item.id as item.label for item in values"></select>
Code is in coffeescript:
Here's a plunkr: http://plnkr.co/edit/C6qmUoJ6HjsAT69oavRg