scope variable of the controller is not recognized in nested directives - angularjs

I have created two directives and inserted the first directive into the second one. The content of template attribute works fine but the scope variable of the controller is not recognized. Please provide me solution on this
sample link: http://jsbin.com/zugeginihe/2/

You didn't provide the attribute for the second directive.
HTML
<div second-dir first-dir-scope="content">
<div first-dir first-dir-scope="content"></div>
</div>
Link demo: http://jsbin.com/jotagiwolu/2/edit

The best option would using parent directive, We could take use of require option of directive like require: '?secondDir' in firstDir
Code
var myApp = angular.module("myApp", []);
myApp.controller("myController", function($scope) {
$scope.content = "test1";
});
myApp.directive("firstDir", function() {
return {
restrict: "AE",
require: '?secondDir',
scope: {
firstDirScope: "="
},
template: "<div>first content</div>",
link: function(scope, element, attrs, secondDir) {
console.log(scope.firstDirScope, 'first');
}
};
});
myApp.directive("secondDir", function() {
return {
restrict: "AE",
scope: {
firstDirScope: "="
},
controller: function($scope) {
console.log($scope.firstDirScope, 'second');
}
};
});
Working JSBin

Related

Angularjs Is there a cleaner more "angular" way to replace a transcluded class value in a directive template?

Below is the only way i could figure out how to get a directive to pull out an attribute from its origin element, get a new value by hitting a service, and then adding that new service method return as a class in the directive template. i'm wondering if there is an alternative pattern that might be cleaner then this pattern that might use ng-class or possibly ng-transclude:
html:
<my-directive data-condition="{{hour.condition}}"></my-directive>
js:
angular.module('myApp')
.directive('myDirective', function (myService) {
return {
transclude: true,
replace: true,
scope: true,
template: '<i class="{{wiIconClass}}"></i>',
restrict: 'E',
link: function($scope, $elm, attrs){
$scope.wiIconClass=myService.getValue(attrs.condition);
}
}
});
If your function myService.getValue is synchronous, you could simply do:
<div ng-class="getClass(hour.condition)">
And in your controller:
$scope.getClass = function(condition) {
return myService.getValue(condition);
}
Alternatively, you can directly put your service within your scope:
$scope.myService = myService;
So the HTML becomes
<div ng-class="myService.getValue(hour.condition)">
In both cases, you will need to inject your service into your controller:
myModule.controller('myController', function($scope, myService) {
// this controller has access to myService
})
I would use the Directives scope parameter instead of using the Directives Attribute values. This is because when using the attributes you will need to setup a $watch to see when that value updates, with using $scope you get the benefit of the binding aspect.
As far as to respond to the best way, its hard to say without knowing your actual task. You can have Angular update the elements css class value in several different ways.
Here's a working Plunker with some small updates to your existing code.
http://plnkr.co/edit/W0SOiBEDE03MgostqemT?p=preview
angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.hour = {
condition: 'good'
};
})
.factory('myService', function() {
var condValues = {
good: 'good-class',
bad: 'bad-class'
};
return {
getValue: function(key) {
return condValues[key];
}
};
})
.directive('myDirective', function(myService) {
return {
transclude: true,
replace: true,
scope: {
condition: '='
},
template: '<i class="{{myService.getValue(condition)}}"></i>',
restrict: 'E',
link: function(scope, elm, attrs) {
scope.myService = myService;
}
};
});

AngularJS Missing required controller

I want to create two directives that have the following structure:
<div ng-app="myApp">
<div ui-foo="test">
<div ui-bar="test2"></div>
</div>
</div>
First directive is uiFoo, the second one is uiBar.
To define these directives I have setup the following module definition:
angular.module('ui', []).directive('uiFoo',
function() {
return {
restrict: 'A',
link: function($scope, element, attrs) {
$scope.message = function() {
alert(1);
};
}
};
}
).directive('uiBar',
function() {
return {
restrict: 'A',
require: '^uiFoo',
scope: true,
link: function($scope, element, attrs, uiBarController) {
uiBarController.message();
}
};
}
);
angular.module('myApp', ['ui']);
The problem that I am experiencing is known as error/$compile/ctreq (Controller 'uiFoo', required by directive 'uiBar', can't be found!) and the documentation for it can be found here (https://docs.angularjs.org/error/$compile/ctreq?p0=uiFoo&p1=uiBar). I know, a little lackluster.
It doesn't seem to solve my issue.
I've created a JSFiddle for this which you can find here http://jsfiddle.net/A8Vgk/1187/
Thanks!
Like the error says, you're missing the controller on the uiFoo directive.
When you use the require: ^uiFoo, it tells Angular that you want to have access to the controller in the directive called uiFoo.
You didn't define a controller in that directive, so you get the error.
Just define the controller:
angular.module('ui', []).directive('uiFoo',
function() {
return {
restrict: 'A',
link: function($scope, element, attrs) {
$scope.message = function() {
alert(1);
};
},
controller: function($scope) {
this.message = function () {
alert("works!");
}
}
};
}
)
Working Fiddle.

how to pass variable from controller to directive using click event in angularjs

i am using directive concept in angularjs to pass data varible from controller to directive.i am writing one html page and controller,directive in angularjs.
test.html:
<div ng-controller="myController"
data-angular-treeview="true"
data-tree-model="roleList"
data-node-id="roleId"
data-tree-id="mytree"
data-node-label="roleName"
data-node-children="children"
data-ng-click="selectNode(currentNode)"
>
</div>
testcontroller.js:
(function(){
app.controller('myController', function($scope,$http,TreeService){
$scope.selectNode=function(val)
{
$scope.nodeval=val.roleName;
alert("select:"+$scope.nodeval);
};
});
})();
testdirective.js:
app.directive('tree1', function($rootScope) {
function compile(scope, element, attributes) {
return {
post:function(scope, iElement, iAttrs) {
iElement.bind('click', function() {
alert("click: "+scope.node);
});
}
};
}
return {
compile: compile,
scope: {
},
restrict: 'AE',
};
});
here i am getting selected node name in testcontroller.js but i want to pass that nodename in my directive so please suggest me how to this.
Thanks
What i would do is define inside the directive isolated scope an attribute like this:
scope: {
node: '='
}
And inside the view controller have a node initialized as $scope.node = {}; and with the click event change the value of the local $scope.node to the node you are interested in, and as the directive recieved the reference to the $scope.node attribute inside the view controller, it would automatically update itself
<div test-directive node-value="node"></div>
isolate scope directive:
directive('testDirective', function(){
return {
restrict: 'EA',
scope: {
nodeValue: '='
},
link: function(scope, element, attrs){
console.log(scope.nodeValue);
}
}
Access controller scope:
directive('testDirective', function(){
return {
restrict: 'EA',
link: function(scope, element, attrs){
console.log(scope.$eval(attrs['nodeValue']));
}
}

How can I get my directive to access the controllers scope

I have a setup like this:
<controller>
<directive>
in my controller that has a function that returns an html string. How can I get my directive to render this by accessing the controllers scope?
Or maybe I should just put the controller in the directive?
app.controller('controller', ['$scope', 'DataService', function ($scope, DataService) {
$scope.parseJson = function () {
//returns the html
};
}]);
directive
app.directive('Output', function () {
return {
restrict: 'A',
replace: true,
template: '<need html from controller>',
link: function(scope, element, attr) {
//render
//scope.parseJson();
}
};
});
You should use the isolated scope: '&' option
app.directive('output', ['$sce', function ($sce) {
return {
restrict: 'A',
replace: true,
template: "<div ng-bind-html='parsed'></div>",
scope:{
output: "&"
},
link: function(scope){
scope.parsed = $sce.trustAsHtml(scope.output());
}
};
}]);
Template:
<div output="parseJson()"></div>
The directive and the controller should be sharing the scope already. Don't bother using a template for the directive, just get the HTML string in you linking function (you already have the method call in there) and modify the element directly using element.html(). Take a look at the element docs for more info.
app.directive('Output', function ($compile) {
return {
restrict: 'A',
link: function(scope, element, attr) {
var templateString = scope.parseJson();
var compiledTemplate = $compile(templateString)(scope);
compiledTemplate.appendTo("TheElementYouWishtoAppendYourDirectiveTo");
}
};
});

AngularJS - pass function to directive

I have a example angularJS
<div ng-controller="testCtrl">
<test color1="color1" updateFn="updateFn()"></test>
</div>
<script>
angular.module('dr', [])
.controller("testCtrl", function($scope) {
$scope.color1 = "color";
$scope.updateFn = function() {
alert('123');
}
})
.directive('test', function() {
return {
restrict: 'E',
scope: {color1: '=',
updateFn: '&'},
template: "<button ng-click='updateFn()'>Click</button>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
</script>
</body>
</html>
I want when I click button, the alert box will appear, but nothing show.
Can anyone help me?
To call a controller function in parent scope from inside an isolate scope directive, use dash-separated attribute names in the HTML like the OP said.
Also if you want to send a parameter to your function, call the function by passing an object:
<test color1="color1" update-fn="updateFn(msg)"></test>
JS
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.color1 = "color";
$scope.updateFn = function(msg) {
alert(msg);
}
});
app.directive('test', function() {
return {
restrict: 'E',
scope: {
color1: '=',
updateFn: '&'
},
// object is passed while making the call
template: "<button ng-click='updateFn({msg : \"Hello World!\"})'>
Click</button>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
Fiddle
Perhaps I am missing something, but although the other solutions do call the parent scope function there is no ability to pass arguments from directive code, this is because the update-fn is calling updateFn() with fixed parameters, in for example {msg: "Hello World"}. A slight change allows the directive to pass arguments, which I would think is far more useful.
<test color1="color1" update-fn="updateFn"></test>
Note the HTML is passing a function reference, i.e., without () brackets.
JS
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.color1 = "color";
$scope.updateFn = function(msg) {
alert(msg);
}
});
app.directive('test', function() {
return {
restrict: 'E',
scope: {
color1: '=',
updateFn: '&'
},
// object is passed while making the call
template: "<button ng-click='callUpdate()'>
Click</button>",
replace: true,
link: function(scope, elm, attrs) {
scope.callUpdate = function() {
scope.updateFn()("Directive Args");
}
}
}
});
So in the above, the HTML is calling local scope callUpdate function, which then 'fetches' the updateFn from the parent scope and calls the returned function with parameters that the directive can generate.
http://jsfiddle.net/mygknek2/
In your 'test' directive Html tag, the attribute name of the function should not be camelCased, but dash-based.
so - instead of :
<test color1="color1" updateFn="updateFn()"></test>
write:
<test color1="color1" update-fn="updateFn()"></test>
This is angular's way to tell the difference between directive attributes (such as update-fn function) and functions.
How about passing the controller function with bidirectional binding? Then you can use it in the directive exactly the same way as in a regular template (I stripped irrelevant parts for simplicity):
<div ng-controller="testCtrl">
<!-- pass the function with no arguments -->
<test color1="color1" update-fn="updateFn"></test>
</div>
<script>
angular.module('dr', [])
.controller("testCtrl", function($scope) {
$scope.updateFn = function(msg) {
alert(msg);
}
})
.directive('test', function() {
return {
scope: {
updateFn: '=' // '=' bidirectional binding
},
template: "<button ng-click='updateFn(1337)'>Click</button>"
}
});
</script>
I landed at this question, because I tried the method above befire, but somehow it didn't work. Now it works perfectly.
use dash and lower case for attribute name ( like other answers said ) :
<test color1="color1" update-fn="updateFn()"></test>
And use "=" instead of "&" in directive scope:
scope: { updateFn: '='}
Then you can use updateFn like any other function:
<button ng-click='updateFn()'>Click</button>
There you go!
I had to use the "=" binding instead of "&" because that was not working.
Strange behavior.
#JorgeGRC Thanks for your answer. One thing though, the "maybe" part is very important. If you do have parameter(s), you must include it/them on your template as well and be sure to specify your locals e.g. updateFn({msg: "Directive Args"}.

Resources