How do I access a controllers scope after a transclusion? - angularjs

Correct me if I am wrong, but I think in the firstDirective scenario, I am unable to achieve the behavior of the secondDirective because it's creating a sibling scope; I am unable to access the template's controller's scope. I want the behavior of the secondDirective with the power of transclusion. Is there a way to achieve this? or am I attacking this problem the wrong way?
http://jsfiddle.net/3QRDt/68/
var app = angular.module('myApp', []);
app.directive('firstDirective', function(){
return {
restrict: 'EA',
replace: true,
scope: true,
transclude: true,
template: '<div id="holder" data-ng-controller="MyController">{{shouldBeOpen}}<div ng-transclude></div><button data-ng-click="close()">Close</button></div>',
link: function(scope, element) {
scope.openDirective = function() {
scope.open()
alert("Hello from Directive")
}
scope.hello ='dad'
}
};
})
.directive('secondDirective', function(){
return {
restrict: 'EA',
replace: true,
scope: true,
transclude: true,
template: '<div id="holder" data-ng-controller="MyController">{{shouldBeOpen}}<button data-ng-click="openDirective()">{{hello}} Open</button><button data-ng-click="close()">Close</button></div>',
link: function(scope, element) {
scope.openDirective = function() {
scope.open()
alert("Hello from Directive")
}
scope.hello ='dad'
}
};
});;
app.controller('MyController', ['$scope', function($scope) {
$scope.shouldBeOpen = false
$scope.close = function() {
$scope.shouldBeOpen = false
}
$scope.open = function() {
$scope.shouldBeOpen = true
alert("Hello from Controller")
}
}]);

You can use $$prevSibling to reference from the transcluded scope to the isolated scope created by the directive:
<button data-ng-click="$$prevSibling.openDirective()">{{hello}} Open</button>

Related

how angular directive bind controller's service data

controller:
service.checkSub(function(data){
$scope.showSub = data.subscribe? false : true;
})
directive:
app.directive('showSub', function() {
return {
restrict: 'E',
replace: true,
scope: {
showSub: '=show'
},
templateUrl: '<div data-ng-show="show">test</div>',
link: function(scope, element, attrs) {
console.log(scope.showSub); // undifined
if(scope.showSub) {
scope.show = true;
}else {
scope.show = false;
}
}
}
});
<show-sub show="showSub"></show-sub>
why the scope.showSub in directive is undefined ,and I want to use it to control the directive? how should I do it?
The scope.showSub gives undefined, because when loading in the directive, the showSub of your controller scope isn't filled yet. What you can do to fix it:
Change templateUrl to template
Change ng-show="show" to ng-show="showSub"
Lose the link function (it is not needed, as you can directly bind to the scope variables in your template)
code:
app.directive('showSub', function($timeout) {
return {
restrict: 'E',
replace: true,
scope: {
showSub: '=show'
},
template: '<div data-ng-show="showSub">test</div>',
link: function(scope, elem) {
// this function isn't needed, but to show you it gives undefined due to the async call
console.log(scope.showSub); // undefined
$timeout(function(){
console.log(scope.showSub); // true
}, 1500);
}
}
});
Here is a jsfiddle
Your directive is fine but problem with the service.
service.checkSub(function(data){
$scope.showSub = data.subscribe? false : true;
})
$scope.showSub should be in parent scope.
make sure you have data in $scope.showSub
You can get value of showSub by scope.$parent.showSub
So your code will be like ..
app.directive('showSub', function() {
return {
restrict: 'E',
replace: true,
scope: {
showSub: '=show'
},
templateUrl: '<div data-ng-show="show">test</div>',
link: function(scope, element, attrs) {
console.log(scope.$parent.showSub);
if(scope.$parent.showSub) {
scope.show = true;
}else {
scope.show = false;
}
}
}
});

Controller as with directive not working

I want to accomplish scroll-able content by clicking on Bootstrap module. Its working fine. This is following code of my directive:
'use strict';
angular.module('cbookApp')
.directive('scrollTo', scrollTo);
scrollTo.$inject = ['$anchorScroll'];
function scrollTo($anchorScroll) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (event) {
event.stopPropagation();
var location = attrs.scrollTo;
if (scope.vm.isEdit || typeof scope.vm.isEdit =="undefined" ) {
$anchorScroll(location);
} else {
$anchorScroll(location+'1');
}
});
}
};
}
But only problem is i am not sure how to apply active class to current affix li. This DEMO way i found to apply class active to current li and remove from other. It was working without Controller as but once i added controller as it stopped working and give some error of scope.
var app = angular.module('app', ['directives']);
app.controller('firstController',[function(){
var vm = this;
vm.model = { value: 'dsf'};
}]);
angular.module('directives', []).directive('toggleClass', function () {
var directiveDefinitionObject = {
restrict: 'A',
template: '<span ng-click="localFunction()" ng-class="selected" ng-transclude></span>',
replace: true,
bindToController: true,
scope: {
model: '='
},
transclude: true,
link: function (scope, element, attrs) {
scope.localFunction = function () {
scope.model.value = scope.$id;
};
scope.$watch('model.value', function () {
if (scope.model.value === scope.$id) {
scope.selected = "active";
} else {
scope.selected = '';
}
});
}
};
return directiveDefinitionObject;
});
Can you please add this in your directive.
element.parent().parent().children().each(function() {
$(this).find('a').removeClass('active');
});
element.addClass('active');
http://jsfiddle.net/hngzxmda/1/
I suggest using controllerAs in your directive too
angular.module('directives', []).directive('toggleClass', function () {
var directiveDefinitionObject = {
restrict: 'A',
template: '<span ng-click="vmd.localFunction()" ng-class="selected" ng-transclude></span>',
replace: true,
bindToController: {
model: '=',
$id: '='
},
scope: {},
transclude: true,
controller: function() {
var _this = this;
this.localFunction = function () {
_this.model.value = _this.$id;
};
},
controllerAs: 'vmd'
};
return directiveDefinitionObject;
});

AngularJS : Change parent scope value from custom directive

For some reason I can't make this work based on the other examples I've seen here on SO.
Here's my directive:
(function () {
angular.module('materialDesign')
.directive('aSwitch', directive);
function directive() {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false, // I've tried true here
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
scope.toggleState = !scope.toggleState;
});
}
};
}
})();
And the controller scope value that I want to change when toggling the switch/checkbox:
$scope.hideInactive = true;
The html:
<a-switch toggle-state="hideInactive"></a-switch>
and further down in my html page, I have this:
<div ng-show="!hideInactive">
<!-- stuff -->
</div>
EDIT:
This version is "working now", but as soon as I click my switch/checkbox a second time, the element.on fires twice, this flipping my scope value back to what it was.....basically, it's not letting me "un-check" my toggle.
angular.module('material')
.directive('aSwitch', [
'$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function (scope, element) {
element.on('click touchstart', function () {
$timeout(function () {
scope.toggleState.state = !scope.toggleState.state;
scope.$apply();
});
});
}
};
}
]);
EDIT and FINAL SOLUTION:
Here's the updated directive link property that fixed everything. I'd like to add that Oleg Yudovich's answer was also used (passing an object as the property instead of a true/false by itself)
link: function (scope, element) {
element.on('click touchstart', function (event) {
if (event.srcElement && event.srcElement.id && event.srcElement.id === "switch") {
event.stopPropagation();
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
});
}
});
}
Try to pass object instead of primitive variable like this:
$scope.hideInactive = {
state: false;
}
html without changes:
<a-switch toggle-state="hideInactive"></a-switch>
in your directive:
scope.toggleState.state = !scope.toggleState.state;
Reed this awesome article: https://github.com/angular/angular.js/wiki/Understanding-Scopes
You need to run digest cycle after changes in scope, because changing scope binding from event will not run angular digest cycle, you need to run it manually by doing scope.$apply()
Directive
(function () {
angular.module('materialDesign')
.directive('aSwitch', directive);
function directive($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false, // I've tried true here
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
$timeout(function(){
scope.toggleState = !scope.toggleState;
});
});
}
};
}
})();
Try below code:
angular.module('material').directive('aSwitch', ['$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
scope.$apply();
});
});
}
};
}]);

within AngularJs how can we use registered controller $scope object in other directive module conroller

app.controller('myController',['$scope',function($scope){
$scope.temp = '007'; // my controller variable
}]);
app.directive('mydir', function() {
return {
restrict: 'A',
transclude: true,
scope: { mydirobj: '=mydir' },
link: function(scope, element, attrs)
{
console.log(scope.temp);
// here i am trying to access 'myController' controller scope var
// getting error
}
};
});
app.controller('myController', ['$scope',
function($scope) {
$scope.temp = '007'; // my controller variable
}
]);
app.directive('mydir', function() {
return {
restrict: 'A',
transclude: true,
controller: 'myController', // add it here
scope: {
mydirobj: '=mydir'
},
link: function(scope, element, attrs) {
console.log(scope.temp);
// here i am trying to access 'myController' controller scope var
// getting error
}
};
});
You can see my comment inline.

AngularJS - accessing parent directive properties from child directives

This should not be too hard a thing to do but I cannot figure out how best to do it.
I have a parent directive, like so:
directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
replace: true,
transclude: true,
template: '
<div class="editable-fieldset" ng-click="edit()">
<div ng-transclude></div>
...
</div>',
controller: ['$scope', function ($scope) {
$scope.edit = ->
$scope.editing = true
// ...
]
};
});
And a child directive:
.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
template: function (element, attrs) {
'<div>
<label>' + attrs.label + '</label>
<p>{{ model.' + attrs.field + ' }}</p>
...
</div>'
},
require: '^editableFieldset'
};
});
How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?
Put together, what I'd like to have is:
<editable-fieldset model="myModel">
<editable-string label="Some Property" field="property"></editable-string>
<editable-string label="Some Property" field="property"></editable-string>
</editable-fieldset>
The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.
Taking inspiration from this SO post, I've got a working solution here in this plunker.
I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).
Here is the result:
JS:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.myModel = { property1: 'hello1', property2: 'hello2' }
});
myApp.directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
transclude: true,
replace: true,
template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
link: function(scope, element) {
scope.edit = function() {
scope.editing = true;
}
},
controller: ['$scope', function($scope) {
this.getModel = function() {
return $scope.model;
}
}]
};
});
myApp.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
scope: {
label: '#',
field: '#'
},
template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
require: '^editableFieldset',
link: function(scope, element, attrs, ctrl) {
scope.model = ctrl.getModel();
}
};
});
HTML:
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<editable-fieldset model="myModel">
<editable-string label="Some Property1:" field="property1"></editable-string>
<editable-string label="Some Property2:" field="property2"></editable-string>
</editable-fieldset>
</body>
You can get access to parent controller by passing attribute in child directive link function
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.$scope.editing = true;
}

Resources