I am using bootstrap select widget in my angularjs app. I have a repeated pattern where I need to have the same select element in multiple forms. So I thought why not create that as a directive.
The code for the directive is below.
(function () {
var controller = function ($scope) { }
controller.$inject = ['$scope'];
var yesNoDecline = function ($timeout) {
return {
restrict: 'A',
scope: {},
template: '',
controller: controller,
link: function ($scope, element, attrs) {
//Only a select
if ($(element).is('select')) {
//Add options
$(element).append($('<option />', {
value: 'No',
text: 'No'
}));
$(element).append($('<option />', {
value: 'Yes',
text: 'Yes'
}));
$(element).append($('<option />', {
value: 'Decline to answer',
text: 'Decline To Answer'
}));
$timeout(function () {
$(element).selectpicker('refresh');
}, 0);
//Add some attributes
$(element).attr('ui-jq', 'selectpicker');
$(element).attr('ui-options', '{ iconBase: "fa", tickIcon: "fa fa-check" }');
}
}
}
};
yesNoDecline.$inject = ['$timeout'];
appModule.directive('yesNoDecline', yesNoDecline);
})();
Problems with the above code:
Additional empty item is added at the top although the code above didn't add it.
When I select "No" as an option, "Yes" is shown on the select. The same is true to other options, I select an option, another option appears selected.
In the controller, I am setting the selected value to No on the $scope. However, nothing is selected on the select using this directive.
Here is another version of the code using pure Angular:
I've converted this directive to a more angular-like. I get an error though multiple directives requesting same ngModel, which I don't get it frankly.
(function () {
var yesNoDecline = function ($timeout) {
return {
restrict: 'E',
replace: 'true',
require: 'ngModel',
scope: {
id: '#',
name: '#',
ngModel: '=ngModel'
},
template: '<select class="form-control bs-select" id="{{ ::id }}" name="{{ ::name }}" ng-model="ngModel.$viewValue" ' +
' ui-jq="selectpicker" ui-options=\'{ iconBase: "fa", tickIcon: "fa fa-check" }\'>' +
' <option ng-repeat="obj in data" value="{{ ::obj.value }}">{{ ::obj.text }}</option>' +
'</select>',
link: function (scope, element, attrs, ngModelCtrl) {
//Populate the scope
scope.data =
[{
value: 'No', text: 'No'
}, {
value: 'Yes', text: 'Yes'
}, {
value: 'Decline to answer', text: 'Decline To Answer'
}];
scope.$watch(function () {
return ngModelCtrl.$viewValue;
}, function (newValue) {
ngModelCtrl.$setViewValue(newValue);
ngModelCtrl.$render();
});
$timeout(function () {
$(element).selectpicker('refresh');
}, 0);
}
}
};
yesNoDecline.$inject = ['$timeout'];
appModule.directive('yesNoDecline', yesNoDecline);
})();
That's how I use it:
<yes-no-decline id="x" name="x" ng-model="vm.y"></yes-no-decline>
I appreciate your assistance.
Thanks
Related
How do I make a directive run a function like the other built in directives in angular?
In example:
<div ng-repeat="someId in myList" my-directive="runFunctionInScope(someId, divHtmlElement)" />
myApp.directive('myDirective', function ()
{
return function (scope, element, attrs)
{
//??
}
}
You can try something like the below code snippet. Also please check this plunker for working example of your given scenario.
Template:
<div ng-repeat="someId in myList" my-method='theMethodToBeCalled' my-id='someId.id' />
Controller:
app.controller('MainCtrl', function($scope) {
$scope.myList=[{
id: 1,
value: 'One'
}, {
id: 2,
value: 'Two'
}];
$scope.theMethodToBeCalled = function(id) {
alert(id);
};
});
Directive:
app.directive("myMethod",function($parse) {
var directiveDefinitionObject = {
restrict: 'A',
scope: {
method:'&myMethod',
id: '=myId'
},
link: function(scope,element,attrs) {
var expressionHandler = scope.method();
expressionHandler(scope.id);
}
};
return directiveDefinitionObject;
});
I'm writing a directive that will lookup a value from a list of data and always display that value. Why when I click the change data button in this plunkr example does it not change the result? I've tried with a $watch but it only fires once.
Plunk: http://plnkr.co/edit/9A3z0KMm8EaBNgYv1NmH?p=preview
For example, here is the usage:
<lookup-binder lookup="lookupList" find="data.id" result="lookupList.description"></lookup-binder>
Here is what the directive looks like:
app.directive('lookupBinder', function() {
return {
restrict: 'E',
scope: {
lookup: '=',
find: '=',
result: '='
},
controller: function($scope){
console.log('controller fired');
},
link: function(scope, element, attrs, tabsCtrl) {
console.log('link fired')
scope.$watch(scope.find, function(newValue, oldValue) {
console.log('watch fired');
var el = _.find(scope.lookup, { id: scope.find });
if(el != null){
scope.result = el.description;
}
});
},
template: 'lookup Binder: <br/> lookupList: {{lookup}} <br/> find: {{find}} <br/> result: {{result}} <br/>'
};
});
Controller:
var appName = angular.module('app', []);
angular.module('app').controller('testController', TestController);
TestController.$inject = ['$scope'];
function TestController($scope) {
$scope.gradeLevels = [{
id: '1',
description: 'kindergarden'
}, {
id: '2',
description: 'first grade'
}];
$scope.gradeLevel = {
gradeLevelID: '1',
record: 'test1'
};
console.info('gradeLevels[0].id = ' + $scope.gradeLevels[0].id);
console.info('gradeLevel.gradeLevelID = ' + $scope.gradeLevel.gradeLevelID);
}
scope's $watch function takes either a string or a function, so change it to either one of them:
scope.$watch('find', function(newValue, oldValue) {
...
}
Also, the result of _.random is a number, and your ids are strings, so change the random to a string:
$scope.data.id = _.random(1,4).toString();
Check this plunker
I'm quite new to AngularJS and I'm trying to define 4 seperate select elements usign element directives.
app.directive('playerselect', function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'templates/directives/player-select.html',
}
});
<select ng-options="player.full_name for player in players">
<option value="" selected disabled>Kies een speler</option>
</select>
When a user selects an option in one of the select elements I want the other select elements to be updated, so that the selected option gets disabled in all other select elements. Basically, I want to make sure that each option can get chosen only once.
Any ideas on how to do this?
I would do it like this:
directive
app.directive('playerSelect', function() {
return {
templateUrl: ...
scope: {
players: '=',
selectedPlayer: '=',
selectedPlayers: '=',
},
controller: function($scope) {
$scope.isDisabled = function(player) {
return $scope.selectedPlayers.indexOf(player) > -1 &&
player !== $scope.selectedPlayer)
};
},
}
});
directive template
<select ng-model="selectedPlayer">
<option ng-repeat="player in players" ng-disabled="isDisabled(player)">{{player}}</option>
</select>
controller
app.controller('PlayerController', function($scope) {
$scope.players = ...
$scope.selectedPlayers = [
$scope.player1,
$scope.player2,
$scope.player3,
$scope.player4,
];
});
controller template
<div ng-controller="PlayerController">
<player-select players="players" selected-players="selectedPlayers" selected-player="player1"></player-select>
<player-select players="players" selected-players="selectedPlayers" selected-player="player2"></player-select>
<player-select players="players" selected-players="selectedPlayers" selected-player="player3"></player-select>
<player-select players="players" selected-players="selectedPlayers" selected-player="player4"></player-select>
</div>
Check this jsFiddle for see it in action.
I would recommend you an use of directive two way data binding and ngModelController.
So in your html :
<player-select players="players" ng-model="select1"></player-select>
<player-select players="players" ng-model="select2"></player-select>
<player-select players="players" ng-model="select3"></player-select>
In your controller :
$scope.players = [{
name: 'foo'
}, {
name: 'bar'
}, {
name: 'baz'
}];
And the directive, using scope.$watch for update each list :
angular.module('fiddleApp').directive('playerSelect', function ($rootScope) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
players: '='
},
template: '<select ng-model="selected" ng-options="player.name for player in playersCopy"></select>',
link: function (scope, el, attrs, ngModel) {
function update() {
scope.playersCopy = angular.copy(scope.players);
if (scope.selected) {
scope.playersCopy.push(scope.selected);
}
}
scope.$watch('selected', function (newValue, oldValue) {
if (newValue) {
scope.players.splice(scope.players.map(function (a) {
return a.name
}).indexOf(newValue.name), 1);
if (oldValue) { scope.players.push(oldValue); }
ngModel.$setViewValue(scope.selected);
$rootScope.$broadcast('playersUpdate');
}
});
scope.$on('playersUpdate', function () {
update();
});
update();
}
};
});
I'm writting a custom directive to bind a html select element to an unordered list.
It works just fine with static select elements.
However, I couldn't make it work with dynamic selects. Is there a way to my directive retrieve the options generated by ng-options or ng-repeat directives?
I have an example here: http://jsfiddle.net/fjbyq/3/
angular.module('myApp', [])
.directive('taskFilterChoices', function() {
return {
transclude: true,
replace: true,
template: '<div class="listToggleBox listType" data-ng-transclude><ul class="choicefilterList"><li data-ng-repeat="option in options"> {{ option.text }} </li> </ul> </div>',
scope: {},
link: function(scope, element, attrs) {
var _select = element.find('select'),
_selectOptions = [];
_select.hide();
_select.find('option').each(function(i, option) {
var _optionObj = {
value: option.value,
text: option.innerText
};
_selectOptions.push(_optionObj);
});
scope.options = _selectOptions;
}
};
})
.directive('taskFilterLink', function() {
return {
restrict: 'C',
link: function(scope, element) {
$(element).on('click', function(e){
e.preventDefault();
var _select = $(this).closest('.listToggleBox').find('select');
_select.val($(this).data('value')).triggerHandler('change');
});
}
};
});
Thanks
See fiddle http://jsfiddle.net/5FdgC/2
var myModule = angular.module('myModule', [])
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.model = { name: ' hans', game: 'wow' };
$scope.items = [{ name: ' jens', game: 'wow' }, { name: ' hans', game: 'wow' }];
$scope.equal = angular.equals($scope.items[1], $scope.model);
}]);
myModule.directive('selectBlock', function () {
return {
restrict: 'A',
replace: true,
template: '<select ng-model="model" ng-options="item.name for item in items"></select>',
scope: {
model: '=',
items: '='
},
link: function (scope, element, attributes) {
}
}
});
Problem:
I pass a model and some items. model angular.equals to true with an object in items. However, setting ng-model="model" in the select does not display model in the dropdown. It remains blank with the items listed beneath.
Say
items = [ob1, ob2]
angular.equals(ob2, model) = true
ng-model="model"
select renders as
--blank-- //selected value
ob1
ob2
instead of
ob1
ob2 // selected value
Cheers
Ok, I have found the problem.
http://jsfiddle.net/5FdgC/3/
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.items = [{ name: ' jens', game: 'wow' }, { name: ' hans', game: 'wow' }];
// here, you have to set the default by reference, not by value.
$scope.model = $scope.items[1];
[...]