Update child directive on click - angularjs

is there a way to update a child directive on click? In my plnkr, column 1 contains a list of names. If you click on the name it will populate the info into the contact directive in column 2. If I make a change in the textbox in column 2, the data in the info directive in column 3 will also change as well. Here is my plnkr:
http://plnkr.co/edit/gcZbd9letYhA4ViBQJ0Q?p=preview
Here is my JS:
var app = angular.module('myApp', []);
app.controller('contactController', function() {
this.contacts = [
{
id: 1,
name: 'Bob'
},
{
id: 2,
name: 'Sally'
},
{
id: 3,
name: 'Joe'
}
]
this.selectedContact;
this.PublishData = function(data) {
this.selectedContact = data;
}
this.UpdateData = function(data) {
for (var i = 0; i < this.contacts.length; i++) {
if (this.contacts[i].id === data.id) {
this.contacts[i] = angular.copy(data);
}
}
}
});
app.directive('contactDirective', function () {
return {
restrict: 'E',
templateUrl: 'contact.html',
scope: {
myModel: '=',
updateData: '&'
},
link: function (scope, element, attrs) {
scope.$watch('myModel', function (newValue, oldValue) {
scope.contact = angular.copy(newValue);
});
}
}
});
app.directive('infoDirective', function () {
return {
restrict: 'E',
templateUrl: 'info.html',
scope: {
contactObject: '='
},
link: function (scope, element, attrs) {
}
}
});

you can simply use $broadcast and $on services
with $broadcat you create an event and you pass a parameter
with $on you listen that event and take that value
I edited your code in this way:
<!--Contact template-->
<div class="col-sm-6">
<b>Column 2</b>
<input type="text" ng-model="newName" />
<button ng-click="updateData({data:contact,newName:newName})">Update</button>
</div>
<div class="col-sm-6">
<b>Column 3</b>
<info-directive contact-object="contact"></info-directive>
</div>
<!-- Your Index file -->
<body ng-app="myApp">
<div ng-controller="contactController as $ctrl">
<div class="row col-md-12">
<div class="col-sm-2">
<b>Column 1</b>
<div ng-repeat="contact in $ctrl.contacts">
</div>
</div>
<div class="col-sm-6">
<contact-directive my-model="$ctrl.selectedContact" update-data="$ctrl.UpdateData(data,newName)"></contact-directive>
</div>
</div>
</div>
</body>
//and your controller
var app = angular.module('myApp', []);
app.controller('contactController', function() {
this.contacts = [
{
id: 1,
name: 'Bob'
},
{
id: 2,
name: 'Sally'
},
{
id: 3,
name: 'Joe'
}
]
this.selectedContact;
this.PublishData = function(data) {
this.selectedContact = data;
}
this.UpdateData = function(data,newName) {
for (var i = 0; i < this.contacts.length; i++) {
if (this.contacts[i].id === data.id) {
this.contacts[i].name = newName;
}
}
}
});
app.directive('contactDirective', function () {
return {
restrict: 'E',
templateUrl: 'contact.html',
scope: {
myModel: '=',
updateData: '&'
},
link: function (scope, element, attrs) {
scope.$watch('myModel', function (newValue, oldValue) {
scope.newName = newValue.name;
scope.contact = angular.copy(newValue);
});
}
}
});
app.directive('infoDirective', function () {
return {
restrict: 'E',
templateUrl: 'info.html',
scope: {
contactObject: '='
},
link: function (scope, element, attrs) {
}
}
});

Related

How to make custom directive run function in angularjs 1.x?

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

Cannot access DOM inside ngRepeat inside custom directive

In the following example (http://jsfiddle.net/akanieski/t4chbuwq/1/) I have a directive that has an ngRepeat inside it... during the link stage I cannot access html inside the ngRepeat. Only thing inside shade-element is <!-- ngRepeat i in items --> What am I doing wrong?
Controller:
module.controller("MainCtrl", function($scope, $timeout) {
$scope.currentPanel = 1;
$timeout(function(){
$scope.items = [1,2];
}, 1000)
$scope.loadPanelOne = function() {
if (!$scope.$$phase) $scope.$apply(function(){$scope.loadingPanelOne = true;});
$timeout(function(){
$scope.loadingPanelOne = false;
}, 2000);
}
$scope.loadPanelTwo = function() {
if (!$scope.$$phase) $scope.$apply(function(){$scope.loadingPanelTwo = true;});
$timeout(function(){
$scope.loadingPanelTwo = false;
}, 2000);
}
$scope.loadPanel3 = function() {
if (!$scope.$$phase) $scope.$apply(function(){$scope.loadingPanel3 = true;});
$timeout(function(){
$scope.loadingPanel3 = false;
}, 2000);
}
$scope.loadPanel4 = function() {
if (!$scope.$$phase) $scope.$apply(function(){$scope.loadingPanel4 = true;});
$timeout(function(){
$scope.loadingPanel4 = false;
}, 2000);
}
});
Directives:
var module = angular.module("myApp", []);
module.directive('shadeSet', ['$rootScope', '$timeout', function($rootScope, $timeout) {
return {
restrict: "E",
scope: {
"mode": "=",
"currentPanel": "="
},
controller: function() {
},
link: function(scope, element, attributes) {
$rootScope.$on('panelOpening', function(ev, args) {
if (scope.mode === 'exclusive') {
element
.find('shade-panel')
.addClass('hide')
$('[id="' + args.id + '"]',element).removeClass('hide');
} else if (args.state === 'open') {
$('[id="' + args.id + '"]',element).removeClass('hide');
} else if (args.state === 'close') {
$('[id="' + args.id + '"]',element).addClass('hide');
} else {
$('[id="' + args.id + '"]',element).toggleClass('hide');
}
$rootScope.$broadcast('panelOpened', {id: args.id, state: 'open'});
})
if (scope.currentPanel) {
$rootScope.$broadcast('panelOpening', {id: scope.currentPanel, state: 'open'});
}
}
}
}])
module.directive('shadePanel', ['$rootScope', function($rootScope) {
return {
restrict: "E",
require: '^shadeSet',
scope: {
"id": "=",
"onOpen": "&",
"scrollOnOpen": "="
},
link: function(scope, element, attributes) {
$rootScope.$on('panelOpened', function(ev, args){
if (args.id === scope.id && scope.onOpen) scope.onOpen();
if (scope.scrollOnOpen) {
console.log("Scrolling to " + args.id)
$('html, body').animate({
scrollTop: $(element).offset().top
}, 500);
}
})
}
}
}])
module.directive('shadeToggle', ['$rootScope', function($rootScope) {
return {
restrict: "E",
require: '^shadeSet',
scope: {
target: "="
},
compile: function() {
return {
pre: function(scope, element, attributes) {
element.on('click', function() {
$rootScope.$emit('panelOpening', {id: scope.target});
});
scope.$on('$destroy', function() {
element.off('click');
});
}
};
}
}
}])
HTML
<div ng-controller="MainCtrl" ng-app="myApp">
<shade-set current-panel="currentPanel">
<div ng-repeat="i in items">
<shade-toggle target="1"><a href>Open One</a></shade-toggle>
<shade-panel id="1" class="hide" on-open="loadPanelOne()">
<span ng-show="loadingPanelOne">... Loading Panel 1 ...</span>
Hello World
</shade-panel>
</div>
</shade-set>
</div>

Directive variable overwritting

I am working on angularjs directive, i made one JsBin, here I am using two arrays and each array selection is storing in two different variables name temp1 and temp2, the problem is when i select one array the other value changes to empty array and vice-versa.
HTML is this
<div ng-controller="ctrl">
<script type="text/ng-template" id="partials/checkbox.html">
<div ng-repeat="obj in data">
<input on-check type="checkbox" ng-model="checked" value="{{obj}}" click-checkbox="checkstatus(checked,obj)" checked-me="checked" />{{obj}}</div>
</script>
<check-boxes get-type="data"></check-boxes>
<check-boxes get-type="bestnights"></check-boxes>
</div></div>
Javascript code is
var app = angular.module('myApp', []);
app.controller('ctrl', function($scope) {
var data = [];
var bestnights = [];
var daysArray = [];
var getType;
var temp1, temp2;
$scope.$on($scope.getType, function() {
getType = $scope.getType;
if (getType == 'data') {
$scope.data = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
temp1 = data;
daysArray = data;
}
if (getType == 'bestnights') {
$scope.data = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'];
temp2 = bestnights;
daysArray = bestnights;
}
});
$scope.checkstatus = function(checked, obj) {
var index = daysArray.indexOf(obj);
if (checked) {
if (index === -1) {
daysArray.push(obj);
}
}
if (!checked) {
daysArray.splice(index, 1);
}
var str = daysArray.toString();
console.log(temp1);
console.log(temp2);
};
});
app.directive('checkBoxes', function() {
return {
restrict: "EA",
scope: {
getType: "#"
},
controller: "ctrl",
templateUrl: "partials/checkbox.html",
link: function(scope, ele, attrs, dctrl) {
ele.bind('click', function() {
//console.log(scope.getType);
scope.$emit(scope.getType);
});
var defaultFunction = function() {
scope.$emit(scope.getType);
}();
}
};
});
app.directive('onCheck', function() {
return {
restrict: "A",
scope: {
clickCheckbox: "&",
value: "#",
checkedMe: "="
},
link: function(scope, ele, attrs) {
ele.bind('click', function() {
scope.clickCheckbox(scope.checkedMe, scope.value);
});
}
};
});
I think the reason why one of your variable is always null is because of scopes: A directive has its own scope.
Which means: Checkbox data has temp1 and temp2 as variables.
Checkbox bestnights has temp1 and temp2 as variables too.
HOWEVER there are not the same : data.temp1 != bestnights.temp1.
To find what are the values of your directives, do the following. In the html:
<div ng-controller="test">
<script type="text/ng-template" id="partials/checkbox.html">
<div ng-repeat="obj in data">
<input on-check type="checkbox" ng-model="checked" value="{{obj}}"
click-checkbox="checkstatus(checked,obj)" checked-me="checked" />{{obj}}
</div>
</script>
<check-boxes get-type="data" values="days"></check-boxes>
<check-boxes get-type="bestnights" values="months"></check-boxes>
<input type="button" ng-click="showValues()" value="Show values" />
</div>
In the js:
var app = angular.module('myApp', []);
app.controller('test', function($scope){
$scope.days = [];
$scope.months = [];
$scope.showValues = function(){
console.log('Values:');
console.log($scope.days);
console.log($scope.months);
};
});
app.controller('ctrl', function($scope) {
$scope.$on($scope.getType, function() {
if ($scope.getType == 'data') {
$scope.data = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
}
if ($scope.getType == 'bestnights') {
$scope.data = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'];
}
});
$scope.checkstatus = function(checked, obj) {
var index = $scope.values.indexOf(obj);
if (checked) {
if (index === -1) {
$scope.values.push(obj);
}
}
if (!checked) {
$scope.values.splice(index, 1);
}
};
});
app.directive('checkBoxes', function() {
return {
restrict: "EA",
scope: {
getType: "#",
values: "="
},
controller: "ctrl",
templateUrl: "partials/checkbox.html",
link: function(scope, ele, attrs, dctrl) {
ele.bind('click', function() {
scope.$emit(scope.getType);
});
var defaultFunction = function() {
scope.$emit(scope.getType);
}();
}
};
});
app.directive('onCheck', function() {
return {
restrict: "A",
scope: {
clickCheckbox: "&",
value: "#",
checkedMe: "="
},
link: function(scope, ele, attrs) {
ele.bind('click', function() {
scope.clickCheckbox(scope.checkedMe, scope.value);
});
}
};
});
I think it's best to separate the controller from your page and the controller from your directive. In my example, I created a controller named "test" for the page. I also added a new param to your directive which will contains the values.
Finally, for the example, I added a button to print the values.

angularJS: watch async data in directive

I'm trying to watch a async data in my directive, here is my JS code:
(function(angular) {
var myApp = angular.module('testTree', []);
myApp.config(function($httpProvider) {
$httpProvider.defaults.headers.get = {};
$httpProvider.defaults.headers.get["Content-Type"] = "application/json";
});
myApp.factory('DataService', function($http) {
return { getData: function(prmParentId, prmParentSrc) {
var data = $.param({ 'parentId': prmParentId, 'parentSrc': prmParentSrc });
return $http.get("Temp.aspx/GetData", { params: { parentId: prmParentId, parentSrc: prmParentSrc }, data: '' }).
success(function(result) {
return result.d;
});
}
}
});
myApp.controller('myController', myController);
function myController($scope, DataService) {
$scope.treeNodes = [];
$scope.init = function() {
DataService.getData(0, 0).then(function(promise) {
$scope.treeNodes = promise.data.d;
});
}
$scope.focusNode = function() {
console.log("kuku2");
}
}
myApp.directive('nodes', function() {
return {
restrict: "E",
replace: true,
scope: {
nodes: '=',
clickFn: '&'
},
template: "<ul><node ng-repeat='node in nodes' node='node' click-fn='clickFn()'></node></ul>",
link: function(scope, element, attrs) {
scope.$watch('treeNodes', function(newVal, oldVal) {
console.log(scope.treeNodes);
}, true);
}
}
});
myApp.directive('node', function($compile) {
return {
restrict: "E",
replace: true,
scope: {
node: '=',
clickFn: '&'
},
template: "<li><span ng-click='clickFn()(node)'>{{node.ObjectName}}</span></li>",
link: function(scope, element, attrs) {
if (angular.isArray(scope.node.Children)) {
element.append("<nodes nodes='node.Children' click-fn='clickFn()'></nodes>");
$compile('<nodes nodes="node.Children" click-fn="clickFn()"></nodes>')(scope, function(cloned, scope) {
element.append(cloned);
});
}
}
}
});
})(angular);
And this is my HTML:
<div ng-app="testTree">
<div ng-controller="myController">
<div ng-init="init()">
<nodes node="treeNodes" click-fn="focusNode"></nodes>
</div>
</div>
</div>
The console in the directive's watch is always "undefined". What am I doing wrong here?
thanks.
Pass treeNodes as nodes in your directive. So you need to watch nodes.
scope.$watch('nodes', function(newVal, oldVal) {
console.log($scope.nodes);
}, true);
<div ng-app="testTree">
<div ng-controller="myController">
<div ng-init="init()">
<nodes nodes="treeNodes" click-fn="focusNode"></nodes>
</div>
</div>
</div>

ngOptions conflicts with my directive and does not work

I have written a directive for bootstrap popover. The popover directive itself works fine but when i use it with ngOptions, ngOptions does not work and does not bind data to select atribute.
The directive code is here:
app.directive("ngPopover", function () {
return {
restrict: "A",
scope: { popoverVisible: '=', popoverTitle: "=", popoverContent: "=", popoverTrigger: "#", popoverPlacement: "#" },
link: function (scope, iElement, iAttrs) {
if (scope.popoverTrigger) {
$(angular.element(iElement)).popover({
title: scope.popoverTitle,
content: scope.popoverContent,
trigger: (scope.popoverTrigger) ? scope.popoverTrigger : "manual",
placement: (scope.popoverPlacement) ? scope.popoverPlacement : "right"
});
}
scope.$watch(function () { return scope.popoverVisible; }, function () {
$(angular.element(iElement)).popover('destroy');
$(angular.element(iElement)).popover({
title: scope.popoverTitle,
content: scope.popoverContent,
trigger: (scope.popoverTrigger) ? scope.popoverTrigger : "manual",
placement: (scope.popoverPlacement) ? scope.popoverPlacement : "right"
});
if (scope.popoverVisible)
$(angular.element(iElement)).popover('show');
else
$(angular.element(iElement)).popover('hide');
});
}
};
});
Here is my view code:
<div ng-app="app">
<div ng-controller="Ctrl">
<button class="pop btn btn-danger" ng-click="show()">Show</button>
<br />
<select id="select" ng-options="act for act in activities" ng-model="activity" ng-popover
popover-visible="visPopover" popover-content="'Content'">
</select>
<br />
<button ng-click="hide()">Hide</button>
</div>
</div>
And this is my controller function:
function Ctrl($scope) {
$scope.visPopover = false;
$scope.activities = ['a1', 'a2', 'a3'];
$scope.activity = 'a1';
$scope.hide = function () {
$scope.visPopover = false;
};
$scope.show = function () {
$scope.visPopover = true;
};
}
If there are any other problems or bad practices in my code (specially in writing directive) please let me know!
Update:
Fiddle Link : http://jsfiddle.net/alisabzevari/sZbjt/1/
Stewie was right!
AngularJS Developer guide:
If multiple directives on the same element request a new scope, only
one new scope is created.
So I didn't use scope at all. I got all of my directive properties from attributes. The only trick was that I had to watch one of my attributes popover-visible.
here is the directive code:
app.directive("ngPopover", function () {
return {
restrict: "A",
link: function (scope, iElement, iAttrs) {
if (iAttrs.popoverTrigger) {
$(angular.element(iElement)).popover({
title: iAttrs.popoverTitle,
content: iAttrs.popoverContent,
trigger: (iAttrs.popoverTrigger) ? iAttrs.popoverTrigger : "manual",
placement: (iAttrs.popoverPlacement) ? iAttrs.popoverPlacement : "right"
});
}
scope.$watch(function () { return iAttrs.popoverVisible; }, function () {
$(angular.element(iElement)).popover('destroy');
$(angular.element(iElement)).popover({
title: iAttrs.popoverTitle,
content: iAttrs.popoverContent,
trigger: (iAttrs.popoverTrigger) ? iAttrs.popoverTrigger : "manual",
placement: (iAttrs.popoverPlacement) ? iAttrs.popoverPlacement : "right"
});
if (scope.$eval(iAttrs.popoverVisible))
$(angular.element(iElement)).popover('show');
else
$(angular.element(iElement)).popover('hide');
});
}
};
});
Here is the fix. The trick is to separate the ng-popover from select.
<div ng-app="App">
<div ng-controller="Ctrl">
<button class="pop btn btn-danger" ng-click="show()">Show</button>
<br />
<ng-popover popover-visible="visPopover" popover-content="'Content'"></ng-popover>
<select id="select" ng-options="act for act in activities" ng-model="activity"></select>
<br />
<button ng-click="hide()">Hide</button>
</div>
</div>
var app = angular.module('App', []);
app.directive("ngPopover", function () {
return {
restrict: "E",
scope: {
popoverVisible: '=',
popoverTitle: "=",
popoverContent: "=",
popoverTrigger: "#",
popoverPlacement: "#"
},
link: function (scope, iElement, iAttrs) {
scope.$watch(function () {
return scope.popoverVisible;
}, function () {
var s = iElement.parent().find("select");
$(s).popover('destroy');
$(s).popover({
title: scope.popoverTitle,
content: scope.popoverContent,
trigger: (scope.popoverTrigger) ? scope.popoverTrigger : "manual",
placement: (scope.popoverPlacement) ? scope.popoverPlacement : "right"
});
if (scope.popoverVisible) $(s).popover('show');
else $(s).popover('hide');
});
}
};
}).controller("Ctrl", function ($scope) {
$scope.visPopover = false;
$scope.activities = ['a1', 'a2', 'a3'];
$scope.activity = 'a1';
$scope.hide = function () {
$scope.visPopover = false;
};
$scope.show = function () {
$scope.visPopover = true;
};
});
Try it here.

Resources