Cannot access DOM inside ngRepeat inside custom directive - angularjs

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>

Related

Update child directive on click

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

angularjs nested cascading directive for dropdown

I want to create a cascading dropdown directive .
<my-dropdown label="states" url="http://statelisturl">
<my-dropdown label="cities" url="http://citylisturl">
</my-dropdown>
</my-dropdown>
But states will be list first, when I select a state cities will be get from url.
Is this possible for angularjs technically? Or I should be seperated directive for every dropdown?
You could do something like this:
.directive("myDropdown", function() {
return {
restrict: "E",
require: ["myDropdown", "?^^myDropdown"],
template: "<select ng-options='opt as opt.label for opt in $ctrl.options' ng-model='$ctrl.selectedOption' ng-change='$ctrl.changed($ctrl.selectedOption)'></select><div ng-transclude></div>",
scope: true,
bindToController: {
url: "#",
label: "#"
},
controller: function($scope, $http) {
var _self = this;
_self.init = function() {
$http.get(_self.url).then(function(response) {
_self.options = response.data;
});
}
_self.parentChanged = function(item) {
var id = item.id;
$http.get(_self.url + "?id=" + id).then(function(response) {
_self.options = response.data;
});
}
},
link: function(scope, element, attrs, ctrls) {
var ctrl = ctrls[0];
var parentCtrl = ctrls[1];
if (parentCtrl) {
scope.$watch(function() {
return parentCtrl.selectedOption;
}, function(newval) {
if (newval) {
ctrl.parentChanged(newval);
}
});
} else {
ctrl.init();
}
},
controllerAs: "$ctrl",
transclude: true
}
});
EDIT: I have a working example here

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>

Why is my $watch not getting called?

I just don't understand why my $watch is NOT called a second time with the updated value of "entity".
.directive('myParent', function() {
return {
controller: function($scope, $log) {
$scope.$on('thingChanged', function(e, newVal) {
$log.log('thingChanged event hit...');
$scope.$apply(function() {
$scope.thing = newVal;
});
});
},
template: '<div><div my-child entity="thing" v="{{thing.awesome}}"></div><div my-child2></div></div>'
};
})
.directive('myChild', function($parse, $log) {
return {
controller: function($scope) {
},
scope: {
entity: '=',
v: '#'
},
link: function(scope, element, attrs) {
$log.log('v in link:');
$log.log(scope.v);
attrs.$observe('v', function(v) {
$log.log('v in observe in link:');
$log.log(v);
})
$log.log('entity in link:');
$log.log(scope.entity);
scope.$watch(scope.entity, function(nv, ov) {
$log.log('$watch detected change: ');
$log.log(scope.entity);
});
},
template: '<span>{{v}}</span>-<span>{{entity}}</span>'
};
})
.directive('myChild2', function($parse, $log, $timeout) {
return {
controller: function($scope) {
var count = 0;
$timeout(function() {
$scope.$emit('thingChanged', { awesome: 'cool'+count++ });
}, 2000);
},
scope: true,
link: function(scope, element, attrs) {
},
template: '<span><button ng-click="change()"</span>'
};
})
Plunker: http://plnkr.co/edit/2jfa1dcwguO400zRjJxU?p=preview
Watch should be the name of the property to be watched, try scope.$watch("entity", ..., if you want to deep watch (that is object properties) call it with true as the last parameter.
scope.$watch("entity", function() { ... }, true);

Resources