Selected item in directive not working - angularjs

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.

Related

angularjs how to access controller $scope variable in directive isolated scope

In angularjs I have been trying to access main controller $scope variable in my directive isolated scope.
My html code,
<body ng-controller="MainCtrl">
<div id="TestContainer" class="TestContainer" ng-init=Intialfunc()>
<collection collection='testdata'>{{testdata}}</collection>
</div>
</body>
My directive code,
var app = angular.module('plunker', []);
app.directive('collection', function () {
return {
restrict: "E",
replace: true,
scope: {collection: '='},
//controller: 'TreeController',
//bindToController: true,
template: "<ul><member ng-repeat='member in collection' member='member'></member></ul>"
}
})
app.directive('member', function ($compile) {
var linkerfunc = function(scope, element, attrs) {
var collectionSt = '<collection collection="member.children"></collection>';
$compile(collectionSt)(scope, function(cloned, scope) {
element.append(cloned);
});
}
return {
restrict: "E",
replace: true,
scope: {member: '=', ShowDetailsCtrlFunc : '&'},
template: "<li><span ng-click=ShowDetailsCtrlFunc()>{{member.NodeName}}</span></li>",
controller: 'MainCtrl',
//controllerAs: 'MainCtrl',
//bindToController: true,
link: linkerfunc
}
})
My controller code,
app.controller('MainCtrl', function ($scope) {
$scope.Intialfunc = function() {
$scope.testdata = []
var myjsondata = JSON.parse('{ "NodeName": "Parent", "children": [ { "NodeName": "mychild", "children": [ { "NodeName": "chld1", "children": [] } ] } ] }');
$scope.testdata.push(myjsondata);
console.log($scope.testdata) //This one is showing
}
$scope.ShowDetailsCtrlFunc = function(element,event) {
console.log("in function ShowDetailsCtrlFunc"); // coming to this fucntion on click.
console.log($scope.testdata) // but this one is not showing . shows undefined.
//event.stopImmediatePropagation();
};
});
it is coming to the function but not showing the controller $scope. I have created a plunker ,
plunker
Please help me. I have been struggling for many days.
You need to add a function expression to both of your directives' isolate scopes in order to properly call a function in your parent scope. Taking your original code, it should look something like this:
var app = angular.module('plunker', []);
app.directive('collection', function () {
return {
restrict: "E",
//replace: true, <- this is deprecated and should no longer be used
scope: {
collection: '=',
onMemberClick: '&'
},
template: "<ul><member ng-repeat='member in collection' member='member' on-click='onMemberClick()'></member></ul>"
}
})
app.directive('member', function ($compile) {
return {
restrict: "E",
//replace: true, <- this is deprecated and should no longer be used
scope: {
member: '=',
onClick : '&'
},
template: "<li><span ng-click='onClick()'>{{member.NodeName}}</span></li>"
}
});
And you original html should look something like this:
<body ng-controller="MainCtrl">
<div id="TestContainer" class="TestContainer" ng-init=Intialfunc()>
<collection collection='testdata' on-member-click='ShowDetailsCtrlFunc ()'>{{testdata}}</collection>
</div>
</body>
Argument binding
If you would like to actually know which member was clicked, you'll need to bind arguments to your function calls.
var app = angular.module('plunker', []);
app.directive('collection', function () {
return {
restrict: "E",
scope: {
collection: '=',
onMemberClick: '&'
},
template: "<ul><member ng-repeat='member in collection' member='member' on-click='onMemberClick({member: member})'></member></ul>"
}
})
app.directive('member', function ($compile) {
return {
restrict: "E",
scope: {
member: '=',
onClick : '&'
},
template: "<li><span ng-click='onClick({member: member})'>{{member.NodeName}}</span></li>"
}
});
Html:
<body ng-controller="MainCtrl">
<div id="TestContainer" class="TestContainer" ng-init=Intialfunc()>
<collection collection='testdata' on-member-click='ShowDetailsCtrlFunc (member)'>{{testdata}}</collection>
</div>
</body>
MainCtrl:
app.controller('MainCtrl', function ($scope) {
$scope.Intialfunc = function() {
$scope.testdata = []
var myjsondata = JSON.parse('{ "NodeName": "Parent", "children": [ { "NodeName": "mychild", "children": [ { "NodeName": "chld1", "children": [] } ] } ] }');
$scope.testdata.push(myjsondata);
console.log($scope.testdata) //This one is showing
}
$scope.ShowDetailsCtrlFunc = function(member) {
console.log("In show details function");
console.log(member);
};
});
plunker
Lets Begin with the query you have. You want to call a function from link inside the directive even when the scope is isolated. It's simple you want to access parent scope.
Here's the code you can use to access parent scope.
scope.$parent.yourFun();
//or you can do this by the code give below.
//Inside Directive Use this.
scope:{
fun:"&"
},
//now you can call this function inside link
link:function(scope, element,attr){
scope.fun();
}
In your app.directive, just put scope : false.
Your directive will use the same scope as his parent scope.

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().

AngularJs, when directive compile new attributes, the events are not triggered if there is a scope

I'm a bit stuck on an directive which add attributes and recompile the element.
If I had a scope on the directive ng-change is not triggered anymore (without it it works). I based my test on this answer
The HTML
<div ng-app="myApp">
<div ng-controller='testController'>
<div ng-repeat="field in fields">
<input type="text" ng-model="ngModel[field.fieldName]" property="{{formParams.getProperties(field.fieldName)}}" update-attr ng-change="test()" />
</div>
</div>
</div>
The directive:
angular.module('myApp', [])
.controller('testController', function ($scope) {
$scope.properties = {
"something": {
"style": "float:left;"
},
"something2": {
"style": "float:right;"
}
};
$scope.ngModel = {};
$scope.fields = [{
fieldName: 'something'
}, {
fieldName: 'something2'
}];
$scope.test = function () {
alert('i dont get triggered');
};
$scope.formParams = {
getProperties: function (fieldName) {
return $scope.properties[fieldName];
}
};
})
.directive('updateAttr', function ($compile) {
return {
restrict: 'A',
replace: true,
terminate: true,
scope: {
ngModel : '='
},
link: function (scope, elem, attrs) {
if (angular.isDefined(attrs['property']) && attrs['property'].lenght != 0) {
var json = JSON.parse(attrs['property']);
angular.forEach(json, function (value, key) {
elem.attr(key, value);
});
elem.removeAttr('property');
var $e = $compile(elem[0].outerHTML)(scope);
elem.replaceWith($e);
}
}
};
});
Here a fork of the fiddle to test with a scope on the directive: fiddle
Do you have any suggestion ?
I found why ng-change was not trigger so I share the answer:
When we add scope attribute on the directive, a new scope is created. So we have to use $scope.$parent for the compilation. I have updated the fiddle with the correction.

Directive to load data from service, present it with select and bind to model with ng-model

I'm stuck creating a 'countries' directive which load data from a service, shows a list of countries in a select control and allow to bind the selected country to a model with ng-model:
Here is the fiddle: http://jsfiddle.net/4hg4cu9p/1
The view:
<div ng-controller: 'personCtrl'>
<countries ng-model='birthCountry'/>
</div>
The code:
var app = angular.module('myApp', [])
app.controller('personCtrl', ['$scope', function($scope) {
$scope.birthCountry = 'CO';
}]);
app.service('Country', [
'$http', function($http) {
return {
list: function() {
return $http.get('http://restcountries.eu/rest/v1/region/americas', {cache: true});
}
};
}]);
app.directive('countries', [
'Country', '$log', function(Country, $log) {
return {
restrict: 'E',
template: "<select data-ng-model='selectedValue' data-ng-options='country.name for country in countries track by country.alpha2Code'></select?",
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
Country.list().then(function(countries) {
scope.countries = countries.data;
});
}
};
}]);
I want to use the ngModelController to:
1.- Set the country in select control when model birthCountry changes.
2.- Change the model birthCountry when user change the select control.
The model is saving the birthCountry as ISO code ('CO' = Colombia, 'US' = United States)
Here is the fiddle: http://jsfiddle.net/4hg4cu9p/1
UPDATE:
Thanks to #PSL and #apairet, here is the jsfiddle working:
http://jsfiddle.net/4hg4cu9p/3/
Since you are specifying ng-model at the directive node, do no specify it at the template instead just use replace:true option in the directive so ng-model will be applied automatically.
Try
{
restrict: 'E',
replace:true,
template: "<select data-ng-options='country.alpha2Code as country.name for country in countries'></select>",
require: 'ngModel',
Demo
Here is a working plunkr: http://plnkr.co/edit/zw5vfjkkESJ78ypCWggi
use an isolated scope, to avoid collision between different instances of your directive
while not strictly required (see http://plnkr.co/edit/e9Vs8AwK5Aqx2G4ZmYVw), I prefer not to use ngModel as custom attribute of a directive --> use of 'my-model'. UPDATE Following your comment and the answer of #PSL, here is another plunker using ng-model and the directive option replace: true http://plnkr.co/edit/YVp6CauBWg3sMLrjwoQL
I bound the model to country.alpha2Code so Colombia is selected
The JS has been modified like this:
var app = angular.module('myApp', []);
app.controller('personCtrl', ['$scope', function($scope) {
$scope.birthCountry = 'CO';
}]);
app.service('Country', [
'$http', function($http) {
return {
list: function() {
return $http.get('http://restcountries.eu/rest/v1/region/americas', {cache: true});
}
};
}]);
app.directive('countries', [
'Country', '$log', function(Country, $log) {
return {
scope: {
myModel: '='
},
restrict: 'E',
template: "<select ng-model='myModel' data-ng-options='country.alpha2Code as country.name for country in countries' track by country.alpha2Code'></select>",
link: function(scope, element, attrs) {
console.log('I am called');
Country.list().then(function(countries) {
console.log(countries);
scope.countries = countries.data;
});
}
};
}]);
and your markup:
<div ng-controller="personCtrl">
<countries my-model="birthCountry"></countries>
{{birthCountry}}
</div>

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

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

Resources