How do I access a parent directive's variable? I keep getting undefined.
I'm trying to access from productName to productTitle. Running angular 1.6
angular.module('appStore').controller(
'TestController', function($scope, Note){
$scope.yell = function(){
return Note(5);
};
}
);
angular.module('appStore').directive('productTitle', function(){
return {
restrict : 'E',
templateUrl : 'product-title.html',
scope : {
age : '='
},
controller : function($scope){
$scope.name = "mary";
},
link : function(scope, element, attrs){
}
};
});
angular.module('appStore').directive('productName', function(){
return {
restrict: 'E',
templateUrl: 'product-name.html',
require: '^productTitle',
link : function(scope, element, attrs, ctrl){
console.log('test');
console.log(ctrl.name); // undefined???
console.log(ctrl.age); // undefined???
},
transclude: true
};
});
index.html:
<div ng-controller="TestController">
<product-title age="6"></product-title>
</div>
product-title.html
<h3>Product Titles</h3>
<product-name></product-name>
product-name is blank.
Related
Lets say i have a controller A:
app.controller('A', function($scope) {
$scope.commonvalue = "";
})
app.directive('dir1', function() {
return {
restrict: 'E',
templateUrl: 'template1.html',
controller: 'A'
};
});
app.directive('dir2', function() {
return {
restrict: 'E',
templateUrl: 'template2.html',
controller: 'A'
};
});
DIR1
template1.html:
<label>Enter value: </label>
<input ng-model="commonvalue"> </input>
DIR2
template2.html:
<p> The value of commonvalue variable is {{ commonvalue }} </p>
All i want to do is change the value of commonvalue from dir1 and get its value in dir2. One solution is to make the commonvalue variable in $rootScope. but i do not want to do that. I only want to change it in 'A' Controllers scope.
You can try something like this.
<div ng-app="myapp" ng-controller="myCtrl">
<my-changer ng-model="someVal"></my-changer>
<my-receiver ng-model="someVal"></my-receiver>
</div>
angular.module("myapp", [])
.controller("myCtrl", function($scope){
$scope.someVal = "Hello";
}).directive("myChanger", function(){
return {
restrict: "E",
scope: {
txtVal : "=ngModel"
},
template: "<input type='text' ng-model='txtVal'/>",
link: function(scope, elem, attr, ngModelCtrl){
}
};
}).directive("myReceiver", function(){
return {
restrict: "E",
scope: {
txtVal : "=ngModel"
},
template: "<input type='text' ng-model='txtVal'/>",
link: function(scope, elem, attr, ngModelCtrl){
}
}
});
JSFiddle
--EDIT---
If you are looking for one way binding then do this.
angular.module("myapp", [])
.controller("myCtrl", function($scope){
$scope.someVal = "Hello";
}).directive("myChanger", function(){
return {
restrict: "E",
scope: {
txtVal : "=ngModel"
},
template: "<input type='text' ng-model='txtVal'/>",
link: function(scope, elem, attr, ngModelCtrl){
}
};
}).directive("myReceiver", function(){
return {
restrict: "E",
scope: {
txtVal : "=ngModel"
},
template: "<p ng-bind='txtVal'/>",
link: function(scope, elem, attr, ngModelCtrl){
}
}
});
Updated JSFiddle
what you want is actualy normal behavior.
when you dont specify a scope for your directive, it will inherit properties from its controller, and when ever a value changes it'll reflect back in the controller..
see this plnkr
app.controller('MainCtrl', function($scope) {
$scope.obj = {};
$scope.obj.commonvalue = "initial value";
});
app.directive('dir1', function() {
return {
restrict: 'A',
templateUrl: 'dir1.html'
}
})
app.directive('dir2', function() {
return {
restrict: 'A',
templateUrl: 'dir2.html'
}
})
I've updated the answer to use a 'dotted' ng-model, I think that was your issue at first. You can review my answer where it is explained why it's important.
from the answer:
What happens is that the child scope gets its own property that hides/shadows the parent property of the same name
You can define scope variable with '=' char in your directives and pass 'commonvalue' to them:
https://jsbin.com/fobofepuji/1/edit?html,js,output
I have two nested directive and a few controllers and I want inject controller to second controller.
When I bind action to some button it work but list don't show up, some one know why?
Dynamic Controller directive
.directive("dynamicController", ["$compile", function($compile) {
return {
restrict: "A",
scope: {
dynamicController: "#"
},
compile: function(tElement, tAttrs) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
iElement.attr("ng-controller", scope.dynamicController);
iElement.removeAttr("dynamic-controller");
$compile(iElement)(scope);
}
}
}
}
}])
V1: http://codepen.io/anon/pen/LVeaWo
V2: http://codepen.io/anon/pen/EjoJVx
[ EDIT ]
I almost do it but it's one more problem.
I have two directive:
.directive("wrapDirective", function() {
return {
restrict: "A",
template: "<div dynamic-controller=\"Ctr1\">" +
"<button ng-click='action()'>Click</button>" +
"<ul>" +
"<li ng-repeat=\"item in list\">{{item}}</li>" +
"</ul>" +
"</div>",
scope: {
controller: "#wrapDirective"
}
}
})
and
.directive("dynamicController", function($compile) {
return {
restrict: "A",
scope: true,
controller: "#",
name: "dynamicController"
}
})
The problem is this line <div dynamic-controller=\"Ctr1\"> in warpDirective
I can't do something like this <div dynamic-controller=\"{{controller}}\">
CodePen with both cases: http://codepen.io/anon/pen/EjoJXV
You should use require and link to get the controllers of parent directives.
See Creating Directives that Communicate.
.directive('myDirective', function() {
return {
require: '^ngController', // <-- define parent directive
restrict: 'E',
scope: {
title: '#'
},
link: function(scope, element, attrs, ctrl) { // <-- get the controller via the link function
ctrl.doSomething();
}
};
The reason behind your code is not working is, {{}} interpolation value is not evaluated in you pre link function. So by compiling ng-controller with not value in it is throwing an error. You should use iAttrs.$observe as you are evaluating expression inside {{}}.
Code
var dynamicControllerObserver = iAttrs.$observe('dynamicController', function(newVal, oldVal) {
wrapElement.attr("ng-controller", scope.dynamicController);
wrapElement.append(iElement.html());
console.log(wrapElement)
iElement.html("");
console.log(iElement)
iElement.append(wrapElement);
$compile(wrapElement)(scope);
dynamicControllerObserver(); //destruct observe
})
Working Codepen
I did it, really helpful was this post: Dynamic NG-Controller Name
I modified it to my needs:
.directive('dynamicCtrl', ['$compile', '$parse', function($compile, $parse) {
return {
restrict: 'A',
terminal: true,
scope: {
dynamicCtrl: "#"
},
link: function(scope, elem, attr) {
var initContent = elem.html();
var varName = getName(elem.attr('dynamic-ctrl'));
update();
scope.$watch("dynamicCtrl", function() {
update();
})
function update() {
var wrapper = angular.element("<div></div>");
wrapper.append(initContent);
var name = $parse(varName)(scope.$parent);
wrapper.attr('ng-controller', name);
elem.empty();
elem.append(wrapper);
$compile(wrapper)(scope);
}
function getName(attr) {
var startIndex = attr.lastIndexOf("{") + 1,
endIndex = attr.indexOf("}");
return attr.substring(startIndex, endIndex);
}
}
};
}])
http://codepen.io/anon/pen/xGYyqr
I have in my directive the controller and link in the following way:
myapp.directive('MyDirective', function(){
return{
restrict: 'A',
transclude: true,
scope: {
....
},
controller: function($scope) {
$scope.variable = true;
},
templateUrl: './app/templates/template.html',
link: function(scope, element, attrs){
scope.$watch('variable ', function(){
console.log("variable : " + scope.variable )
});
}
.....
I am not able to see the scope.variable in the block link. How I can resolve it?
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.
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;
}