ng-model doesn't bind properly in directive template's select - angularjs

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];
[...]

Related

attaching an angularjs directive to "bootstrap select"

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

Selected item in directive not working

I created a select directive and am using this directive twice. I need to see the selected items of both. What should I do?
HTML
<div select-list="items"></div>
<div select-list="items2"></div>
Controller
var myApp = angular.module('myApp',[]);
myApp.controller('mainController', function($scope) {
$scope.items = [
{
name: "1"
},
{
name: "2"
}
];
$scope.items2 = [
{
name: "3"
},
{
name:"4"
}
];
$scope.selectedValues = [];
});
Select directive
myApp.directive("selectList", function() {
return {
restrict: "EACM",
template: '<select ng-model="selectedValues" ng-options="item.name for item in data"></select>',
scope: {
data: '=selectList'
}
}
});
I need to add selected items of both "selects" into $scope.selectedValues.
I tried through ng-change, but it didn't work.
Your directive use isolated scope, so you can't access from the controller to the directive or from the directive to the controller.
You have to create a new entry.
I let you a fiddle that is working :
https://jsfiddle.net/Lv1q2sh2/1/
// Code goes here
var myApp = angular.module('app', []);
angular.module('app')
.directive("selectList", function(){
return {
restrict: "EACM",
require: 'ngModel',
template: '<select ng-model="selected" ng-change="onSelectedValue()" ng-options="item.name for item in data"></select>',
scope: {
data: '=selectList'
},
link: function (scope, element, attr, ngModel) {
scope.onSelectedValue = function () {
ngModel.$setViewValue(scope.selected);
}
}
}
})
.controller('mainController', function($scope) {
$scope.items = [
{name: "1"},
{name: "2"}
];
$scope.items2 = [
{name:"3"},
{name:"4"}
];
$scope.selectedValues = [];
});
Directive needs to be created properly:
Have a controller for your directive
If you are using isolated scope, make sure to pass selectedValue to the scope.
ex:
Directive:
myApp.directive("selectList", function(){
return{
restrict: "EACM",
template: '<select ng-model="selectedValues" ng-options="item.name for item in data"></select>',
scope: {
data: '=selectList',
ngModel: '='
}
//Add link function here, crate watcher on ngModel and update it back on select dropdown selection.
})};
HTML:
<div select-list="items" ng-model="selectedValue1" ></div>
<div select-list="items2" ng-model="selectedValue2"></div>
Add link function to directive and put a watcher on ngModel, once user makes change in selection, update parent ng-model.

Display selected data from dynamically created template of radio buttons

I created a custom directive which accepts data from a controller. What it does is that when called, it dynamically creates a layout of radio buttons.
What I want to do is display the selected data upon clicking the radio button.
my Controller:
var app = angular.module('myApp', []);
app.controller('DataController', ['$scope', function($scope){
$scope.gender = {
label: "Gender",
required:true,
valueList: [
{ text: "Male", value: "male" },
{ text:"Female", value:"female" }
]
};
$scope.my = { sel: '' };
}]);
Directive:
app.directive('appleRadio', function(){
return {
restrict: 'E',
require: 'ngModel',
scope: {
fieldOptions: '=options',
fieldModel: '=ngModel',
fieldName: '#fieldName',
},
controller: ['$scope', function($scope) {
}],
link: function(scope, element, attribute, controller) {
element.on('click', function(){
// test1: assign the selected value to $scope.my.sel in controller
scope.$parent.my.sel = scope.selected;
// test2: assign the selected value to ng-model in template
scope.fieldModel = scope.selected;
console.log(scope.selected);
});
},
template: '<label style="text-indent: 1em;" ng-repeat="option in fieldOptions.valueList"> <input type="radio" name="option-{{option.value}}"'+
'id="option-{{value}}" ng-model="$parent.selected" value="{{option.value}}">{{option.text}}'+
'</label>'
};
});
in Index.html:
<div ng-controller="DataController">
<apple-radio field-label="Gender" field-name="oGend" options="gender" ng-model="selectedOption"></apple-radio>
<br/>You Selected (via ng-model): {{selectedOption}}<br/>
You Selected (via controller): {{my.sel}}<br/>
</div>
here's the plunker link:
http://plnkr.co/edit/DBmsFmVXyjuZYE7s9LLa?p=preview
Whenever you make changes to the angular context outside of an angular method/event, you need to call scope.$apply().
So at the end of your onClick handler, call scope.$apply().

Two way binding in a directive that triggers a watch even after the data changes again?

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

directive with isolate scope and compilation wipes out mode

I have a directive that consists of a select dropdown, when the select is changed (im watching the modelid) i'm recompiling the template and setting the element to the compiled html. the bizarre thing is, the span shows the correct value where as the select text is incorrect.
what i am trying to do and if anyone can provide a better solution is have part of the template dynamic so when a select is changed, i want a different part loaded in (which would also need compiling as it would have binded fields)
can anyone see why its happening please?
Thx.
app.directive('editor', ['$http', '$compile', function ($http, $compile) {
function createTemplate(id) {
var template =
'<div><select class="form-control" ng-model="lettingsourceId" >' +
'<option ng-repeat="lettingsrc in lettingSources" value="{{lettingsrc.id}}">{{lettingsrc.description}}</option>' +
'</select></div>';
return template + "<span>" + id + "</span>";
}
return {
restrict: 'E',
scope: {},
link: function ($scope, $element, $attributes) {
$scope.values= [{ id: "1", description: 'testa' }, { id: "2", description: 'testb' }];
$scope.modelid= "1";
var html = createTemplate($scope.modelid);
$element.html($compile(html)($scope));
$scope.$watch('modelid', function () {
var html = createTemplate($scope.modelid);
$element.html($compile(html)($scope));
});
}
};
}]);
checkout this
app.directive('editor', ['$http', '$compile', function ($http, $compile) {
function createTemplate(id) {
var template =
'<div><select class="form-control" ng-model="' + id + '" >' +
'<option ng-repeat="lettingsrc in lettingSources" value="{{lettingsrc.id}}">{{lettingsrc.description}}</option>' +
'</select>';
return template + "<span>{{" + id + "}}</span></div>";
}
return {
restrict: 'E',
scope: {},
link: function ($scope, $element, $attributes) {
$scope.lettingSources= [{ id: "1", description: 'testa' }, { id: "2", description: 'testb' }];
$scope.modelid= "1";
var html = createTemplate('modelid');
var element = angular.element(html);
$element.append(element);
$compile(element)($scope);
// $scope.$watch('modelid', function () {
// var html = createTemplate($scope.modelid);
// // $element.html(html);
// // $compile(elm)($scope);
// });
}
};
}]);

Resources