Get value from Directive into Controller - angularjs

So I have this filter directive:
app.directive('filter', function(){
return {
restrict: 'E',
transclude: true,
scope: {
callFunc: '&'
},
template:
' <div>' +
' <div ng-transclude></div>' +
' </div>',
controller: function($scope, $element, $attrs){
this.getData = function() {
$scope.callFunc()
}
}
}
});
app.directive('positions', function(){
return {
require: '^filter',
scope: {
selectedPos: '='
},
template:
' Positions: {{selectedPos}}' +
' <ul>' +
' <li ng-repeat="pos in positions">' +
' {{pos.name}}</a>' +
' </li>' +
' </ul>',
controller: function($scope, $element, $attrs){
$scope.positions = [
{name: '1'},
{name: '2'},
{name: '3'},
{name: '4'},
{name: '5'}
];
$scope.selectedPos = $scope.positions[0].name;
$scope.setPosition = function(pos){
$scope.selectedPos = pos.name;
};
},
link: function(scope, element, attrs, filterCtrl) {
scope.posRegData = function() {
filterCtrl.getData();
}
}
}
})
And the controller:
app.controller('keyCtrl', ['$scope', function($scope) {
var key = this;
key.callFunc = function() {
key.value = key.selectedPos;
console.log(key.selectedPos)
}
}]);
The main question is why the key.selectedPos in the controller get's the right value only on the second click?
Here is a plunker replicating my issue.

You can send a param when you call your callFunc().
Update your func in the ctrl: key.callFunc = function(filterParams), also don't forget to update you passed method call-func="key.callFunc(filterParams)
In filter directive change your getData method to:
this.getData = function(val) {
$scope.callFunc({filterParams: val})
}
In positions directive pass the value that you need:
scope.posRegData = function() {
filterCtrl.getData({position: scope.selectedPos});
}
Now in your keyCtrl you can get the value:
key.callFunc = function(filterParams) {
key.value = filterParams.position;
console.log(filterPrams.position)
}
Here is a working plunker

As suggested, I think an isolated scope works:
app.directive('positions', function(){
return {
require: '^filter',
scope : {
selectedPos: '='
},
template:
'<button dropdown>' +
' {{selectedPos}}' +
' <ul class="dropdown-menu">' +
' <li ng-repeat="pos in positions">' +
' {{pos.name}}</a>' +
' </li>' +
' </ul>' +
'</button>',
controller: function($scope, $element, $attrs){
$scope.setPosition = function(pos){
$scope.selectedPos = pos.name;
};
$scope.positions = [
{name: '1'},
{name: '2'},
{name: '3'}
];
$scope.selectedPos = $scope.positions[0].name;
}
}
});
See:http://plnkr.co/edit/boKjmMmsCCZem0lETT3B?p=preview

You can use $broadcast on the $rootScope
in your controller do:
$rootScope.$broadcast('myEvent', $scope.selectedPos);
in your target controller you then have a listener:
$scope.$on('myEvent', function(event, myData){...})
angularJS documentation $on
and a plunker

Related

AngularJS - binding a function to an isolate scope via '&', not firing on ng-click

I have a parent directive with a controller that sets a method on $scope.
Then, in the child directive, I try to require that scope method via &.
However, the ng-click inside the child directive will not fire with the parent scope function. Am I doing this wrong?
Parent directive (where method is defined)
app.directive('ParentDirective', [
'$http',
function($http) {
return {
restrict: 'AE',
template: '' +
'<div child-directive ' +
'list="list" ' +
'/>' +
'',
controller: function ($scope, $http) {
$scope.setSelected = function (selector) {
console.log('hit!', selector);
}
$scope.list = [];
},
link: function postLink(scope, element, attrs) {}
};
}
]);
Child Directive (try to call parent method on ng-click, but not working)
app.directive('childDirective', [
'$window',
function($window) {
return {
restrict: 'AE',
scope: {
setSelected: '&',
list: '=',
},
template: '' +
'<ul>' +
'<li ng-repeat="person in list" ng-click="setSelected(person)">{{person.uri}}</li>' +
'</ul>' +
'',
link: function postLink(scope, element, attrs) {
}
};
}
]);
Am I passing $scope.setSelected() incorrectly from the parent to the child directive?
EDIT: So I changed the parent template to assign the function like so:
template: '' +
'' +
'',
this will now trigger the function, but the argument does not get passed from the child directive. How can I get the child directive to pass an argument into the parent directive function?
ParentDirective has typo should be: parentDirective
in the parentDirective template you should pass the function 'list="list" ' + 'set-selected="setSelected(person)"'
try this:
html:
<parent-directive ></parent-directive>
js:
app.directive('parentDirective', [
'$http',
function($http) {
return {
restrict: 'AE',
template: '' +
'<div child-directive ' +
'list="list" ' + 'set-selected="setSelected(person)"'+
'/>' +
'',
controller: function ($scope, $http) {
$scope.setSelected = function (selector) {
console.log('hit!', selector);
}
$scope.list = [];
},
link: function postLink(scope, element, attrs) {}
};
}
])
app.directive('childDirective', [
'$window',
function($window) {
return {
restrict: 'AE',
scope: {
setSelected: '&',
list: '=',
},
template: '' +
'<ul>' +
'<li ng-repeat="person in list" ng-click="setSelected({person:person})">{{person.uri}}</li>' +
'</ul>' +
'',
link: function postLink(scope, element, attrs) {
}
};
}
]);
working plunker:
https://plnkr.co/edit/KUWZ9bYbhnbumFwIgcIX?p=preview

angular directive scope inheritance/ isolation

I have a directive to build dynamic table from a collection. It have two types of columns checkbox and dropdowns. However, the scope is not resolving correctly at directive level. They are getting as 'undefined'. Also I need to bind drop down options from the controller.
html
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<ui-table source="students">
<check-col title="passed" field="passed"/>
<drop-down-col title="std" field="std" />
</ui-table>
</div>
</div>
Angular js
var myApp = angular.module('myApp',[]);
myApp.directive("uiTable", function ($compile) {
var generateTableHtml = function ($scope) {
// header
var html = "<table class='table table-condensed table-bordered table-responsive'><thead class='thead12'><tr class='active'>";
angular.forEach($scope.columns, function (col) {
debugger;
html += "<th scope='col'" +
(col.cssClass ? " class='" + col.cssClass + "'" : "") +
">" + col.title + "</th>";
});
html += "</tr></thead><tbody>";
// body
html += "<tr ng-repeat='item in dataSource'>";
angular.forEach($scope.columns, function (col) {
html += "<td" + (col.cssClass ? " class='" + col.cssClass + "'" : "") + ">";
if (col.type === ColumnType.Check) {
html += "<input type='checkbox' ng-model='item." + col.dataField + "'/>";
} else if (col.type === ColumnType.DropDown) {
html += "<select ng-model='item." +col.dataField + "' ng-options = 'option.Value for option in dataOptions'></select>";
}
html += "</td>";
});
html += "</tr>";
html += "</tbody></table>";
return html;
};
return {
restrict: "E",
replace: true,
transclude: true,
scope: {
dataSource: "=source"
},
controller: function ($scope) {
$scope.columns = [];
this.addColumn = function (col) {
$scope.columns.splice(0, 0, col);
};
},
template: "<div ng-transclude></div>",
compile: function () {
return function ($scope, $elem) {
$elem.html(generateTableHtml($scope));
$compile($elem.contents())($scope);
};
}
};
});
myApp.directive("checkCol", function () {
return {
require: "^uiTable",
restrict: "E",
replace: true,
scope: {
title: "#title",
cssClass: "#class",
dataField: "#field"
},
link: function ($scope, element, attrs, tableControl) {
$scope.type = ColumnType.Check;
tableControl.addColumn($scope);
}
};
});
myApp.directive("dropDownCol", function() {
return {
require: "^uiTable",
restrict: "E",
replace: true,
scope: {
title: "#title",
cssClass: "#class",
dataField: "#field"
},
link: function($scope, element, attrs, tableControl) {
$scope.type = ColumnType.DropDown;
tableControl.addColumn($scope);
}
};
});
//myApp.factory('myService', function() {});
var ColumnType = { Check: 1, DropDown: 2 };
myApp.controller('MyCtrl', function($scope) {
$scope.students = [{id:1, std:10, passed: true}, {id:2, std:9, passed: false}];
$scope.stds= [{Value: 10, Name: '10the std'},{Value: 9, Name: '9the std'},{Value: 8, Name: '8the std'}];
});
JSFIDDLE
Uprade the angular ver. to 1.2 and it works fine
`http://jsfiddle.net/HB7LU/18635/`

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);
// });
}
};
}]);

How to pass multiple attribute in AngularJS directive

How to pass multiple attribute to a directive.
How to pass value 12 of click-to-edit1 inside below div like
<div click-to-edit="location.state" click-to-edit1=12></div>
and should be accessible in directive controller.please help me out.
Code:
App HTML:
<div ng-controller="LocationFormCtrl">
<h2>Editors</h2>
<div class="field">
<strong>State:</strong>
<div click-to-edit="location.state"></div>
</div>
<h2>Values</h2>
<p><strong>State:</strong> {{location.state}}</p>
</div>
App directive:
app = angular.module("formDemo", []);
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
App controller:
app.controller("LocationFormCtrl", function($scope) {
$scope.location = {
state: "California",
};
});
Add new property inside directives scope:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.location = {
state: "California",
};
});
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
' {{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div>{{value1}}</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
value1: "=clickToEdit1"
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
html:
<div class="field">
<strong>State:</strong>
<div click-to-edit="location.state" click-to-edit1="12"></div>
</div>
working example: http://plnkr.co/edit/e7oTtZNvLdu6w5dgFtfe?p=preview
you first tell the div that you want your directive on it.
<div click-to-edit value='location.state' value1='12'></div>
app.directive("clickToEdit", function() {
return {
restrict: "A",
scope : {
value : "=",
value1 : "="
},
link : function($scope) {
console.log("the first value, should be location.state value on the controller", $scope.value);
console.log("the second value, should be '12'", $scope.value);
}
}
it might seem more logic when you use the directive as a element.
but bottom line is that the diretive looks for attributes on the element, which you then can attach to the directive via the scope. '=' for two way binding, '#' for one way.

Resources