Issue in Custom Directives - angularjs

In the below code i have added a controller to store the name which can be used in child scope without use of prelinks.But still the name value is undefined.Where am i going wrong.
<!DOCTYPE html>
<html ng-app="test">
<head>
<title>AngularJS</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.8/angular.min.js">
</script>
</head>
<body>
<parent></parent>
</body>
<script>
var test1 = angular.module("test", []);
test1.directive("child", function() {
return {
restrict: "E",
template: "<div>{{message1}}</div>",
link: function(scope, ctrl) {
scope.message1 = "i m child of " + ctrl.name;
}
}
});
test1.directive("parent", function() {
return {
restrict: "E",
template: "<div style='color:red'>{{message}}{{name}}" + "<child></child>" + " </div>",
link: function(scope, ctrl) {
scope.name = ctrl.name;
alert(ctrl.name);
scope.message = "hi i am parent ";
},
controller: function() {
this.name = "aditya";
}
}
});
</script>
</html>

As you declared the parent controller using controller as syntax, this, you can add controllerAs option to your directive and define a name of the controller:
var test1 = angular.module("test", []);
test1.directive("child", function() {
return {
restrict: "E",
template: "<div>{{message1}}</div>",
link: function(scope) {
scope.message1 = "i m child of " + scope.parentCtrl.name;
}
}
});
test1.directive("parent", function() {
return {
restrict: "E",
template: "<div style='color:red'>{{message}}{{name}}" + "<child></child>" + " </div>",
link: function(scope, element, attrs, ctrl) {
scope.name = ctrl.name;
alert(scope.name);
scope.message = "hi i am parent ";
},
controller: function($scope) {
this.name = "aditya";
},
controllerAs: 'parentCtrl' // Name of the controller
}
});
Then, in your child, you can access the parent scope by using its defined name, scope.parentCtrl.name.

Related

Error: [$compile:ctreq] data passing between two directive from different module

I have two different module in which I have two directives. I need to pass data between these two directive. I use require property. but I get some error
Error: [$compile:ctreq] Controller 'yearSort', required by directive 'budgetSort', can't be found!
My first directive is
angular.module('movieApp.yearsort.directives', []).directive('yearSort',[function(){
return{
restrict : 'AEC',
replace : true,
transclude : true,
controller : 'YearsortController',
templateUrl : 'app/components/yearsort/yearsort.html',
};
}]);
In the YearsortController I have the code
angular.module('movieApp.yearsort.controller', []).controller('YearsortController', ['$scope','HomeFactory','$timeout','$state',function($scope,HomeFactory,$timeout,$state) {
this.sayHello = function() {
$scope.words = "my requier";
console.log( $scope.words);
};
}]);
In my second directive I have the code
angular.module('movieApp.budgetsort.directives', []).directive('budgetSort',[function(){
return{
restrict : 'AEC',
replace : true,
transclude : true,
controller : 'BudgetsortController',
templateUrl : 'app/components/budgetSort/budgetSort.html',
require : "yearSort",
link : function(scope,element, attrs, demoCtrl){
demoCtrl.sayHello();
}
};
}]);
Why don't you try using a Service/Factory? It is a good option when you need to pass data through components or directives
I've made this plunkr to explain: http://plnkr.co/edit/V7BLbOrrtNhXl1QlUKxA?p=preview
HTML:
<body ng-controller="myCtrl">
<first-directive></first-directive>
<second-directive></second-directive>
</body>
Javascript:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
$scope.name = 'World';
//set up the items.
angular.copy([ { name: 'test'} , { name: 'foo' } ], dataService.items);
});
app.directive('firstDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 1</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.directive('secondDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 2</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.factory('dataService', [function(){
return { items: [] };
}]);
The Demo
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
$scope.name = 'World';
//set up the items.
angular.copy([ { name: 'test'} , { name: 'foo' } ], dataService.items);
});
app.directive('firstDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 1</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.directive('secondDirective', function(dataService){
return {
restrict: 'E',
template: '<h3>Directive 2</h3>' +
'<div ng-repeat="item in data.items">' +
'<input type="text" ng-model="item.name"/>' +
'</div>',
link: function(scope, elem, attr) {
scope.data = dataService;
}
};
});
app.factory('dataService', [function(){
return { items: [] };
}]);
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<first-directive></first-directive>
<second-directive></second-directive>
</body>

How do I access a parent directive variable?

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.

Send data from controller to directive

I want to send that to my directive but I want that data to stay updated if the data in the controller changes.
// Controller
angular
.module('app')
.controller('IndexController', IndexController)
IndexController.$inject = [];
function IndexController() {
var vm = this;
vm.name = 'John';
newName = function() {
vm.name = 'Brian';
}
newName();
}
// Directive
angular
.module('app')
.directive('userName', userName);
userName.$inject = ['$document'];
function userName($document) {
var directive = {
restrict: 'EA',
template: '<div id="user"></div>',
replace: true,
scope: {
name: '='
},
link: function(scope, elem, attrs) {
console.log(scope.data);
}
}
return directive;
}
this is how I use the directive. the problem is that it always returns the first name and not the new name after the change in the controller.
<div ng-controller="indexController">
<user-name name="indexController.name">
</div>
thank you.
Try this, you just have to inject $scope into your Indexcontroller
angular
.module('app', [])
.controller('IndexController', function($scope) {
var vm = this;
vm.name = 'John';
vm.newName = function() {
vm.name = 'Brian';
console.log(vm.name);
}
//vm.newName();
})
.directive('userName', ['$document', function() {
var directive = {
restrict: 'E',
template: '<div id="user"></div>',
replace: true,
scope: {
name: '='
},
link: function(scope, elem, attrs) {
console.log(scope.name);
}
}
return directive;
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="IndexController as vm">
<user-name name="vm.name"></user-name>
<button ng-click="vm.newName()">Click</button>
</div>
Without using as in controller, you cannot use controller.prop inside the scope.
Inside the controlleryou need to call the method using its $scope or this.
Check the below code.
angular
.module('app', [])
.controller('IndexController', function($scope) {
$scope.name = 'John';
$scope.newName = function() {
$scope.name = 'Brian';
}
$scope.newName();
})
.directive('userName', ['$document', function() {
var directive = {
restrict: 'E',
template: '<div id="user"></div>',
replace: true,
scope: {
name: '='
},
link: function(scope, elem, attrs) {
console.log(scope.name);
}
}
return directive;
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="IndexController">
<user-name name="name"></user-name>
</div>

angularjs transclude not working in my customized directive

Can any one take a look at my simple directive and figure out why the transclude is not working?
html:
<div ng-app="app">
<div ng-controller="MainController">
<p>Your name</p><input ng-model="name"></input><button ng-click="greeting()">click</button>
<greeter nationality="English" name="{{name}}">Hahahahah</greeter>
<greeter nationality="French" name="{{name}}">Hahahahah</greeter>
<greeter nationality="Russian" name="{{name}}">Hahahahah</greeter>
</div>
</div>
js:
var app = angular.module("app", []);
app.controller("MainController", function($scope) {
});
app.directive("greeter", function() {
return {
restrict: "AE",
scope: {
name: "#",
nationality: "#"
},
transclude: true,
template: "<div style='display:block'>{{name}}<div ng-transclude></div></div>",
link: function(scope, elem, attr, ctrl) {
scope.$watch("name", function() {
elem.html(greeting + " " + scope.name);
});
var greeting = "";
if (scope.nationality === "English") {
greeting = "Hello";
}
else if (scope.nationality === "French") {
greeting = "Bonjour";
}
else {
greeting = "Howdy";
}
}
};
});
You are overwriting the transcluded content when you insert the greeting text with elem.html(...).
Rather than manipulate the DOM with jqLite, you can include the greeting text directly in your template. For this to work, greeting must be a scope property (scope.greeting instead of var greeting).
template: "<div style='display:block'>{{greeting}} {{name}}<div ng-transclude></div></div>",
link: function(scope, elem, attr) {
if (scope.nationality === "English") {
scope.greeting = "Hello";
} else if (scope.nationality === "French") {
scope.greeting = "Bonjour";
} else {
scope.greeting = "Howdy";
}
}
If you want to try the code for yourself, here it is on Plunkr.

Can multiple directives for one element share an isolated scope?

Two directives on the same element can not both have isolated scope, but can they both use the same scope isolated from their parent? And can they both use properties bound to the isolated scope?
For example, if I have two directives on an element
<e-directive a-directive prop="parentProp"/>
And one directive defines an isolated scope with a bound property
App.directive('eDirective', function() {
return {
restrict: 'E',
scope: {
localProp: '=prop'
},
...
};
});
Does the other directive get that scope and can it use the bound property?
App.directive('aDirective', function() {
return {
restrict: 'A',
link: function postLink(scope, element, attrs) {
scope.$watch('localProp', function(newProp, oldProp) {
...
}
},
...
};
});
My initial attempt (pretty much coded as above) failed.
I suggest you make use of communicating between the directives' controllers via the require property of the secondary directive. The first directive (e-directive) holds the isolated scope, while the second helper directive (a-directive) has a reference to the first directive and sets properties via functions defined on the first directive. A small sample would be (see plunker):
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" data-semver="1.2.16"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div e-directive config="parentConfig" a-directive></div>
</body>
</html>
and the javascript:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.parentConfig = {};
});
app.controller('ECtrl', function ( $scope ) {
this.setProp = function(newProp){$scope.config.prop = newProp;};
$scope.$watch('config', function(newProp, oldProp) {
console.log(oldProp, newProp);
});
});
app.directive('eDirective', function() {
return {
restrict: 'A',
scope: {
config: '='
},
controller: 'ECtrl',
link: function(scope, element, attrs) {
scope.config.prop ="abc";
}
};
});
app.directive('aDirective', function() {
return {
restrict: 'A',
require: 'eDirective',
link: function(scope, element, attrs,ctrl) {
ctrl.setProp("def");
}
};
});
Instead of an isolate scope, the directives can create a new child scope, which will be shared by both directives. If you need to modify parentProp in a directive, inject and use $parse:
<div ng-controller="MyCtrl">
<e-directive a-directive prop="parentProp"></e-directive>
</div>
Javascript:
var app = angular.module('myApp', []);
app.controller('MyCtrl', function($scope) {
$scope.parentProp = { prop1: 'value1' };
});
app.directive('eDirective', function($parse) {
return {
restrict: 'E',
scope: true,
template: '<div>dir template: {{eDirLocalProp}}<br>'
+ '<a href ng-click="eDirChange()">change</a></div>',
link: function(scope, element, attrs) {
scope.eDirProp1 = 'dirPropValue';
var model = $parse(attrs.prop);
scope.eDirLocalProp = model(scope);
scope.eDirChange = function() {
scope.eDirLocalProp.prop1 = "new value";
};
}
};
});
app.directive('aDirective', function() {
return {
scope: true,
link: function postLink(scope, element, attrs) {
scope.$watchCollection(attrs.prop, function(newValue) {
console.log('aDirective', newValue);
});
},
};
});
fiddle
If both directives need to create properties on the new child scope, use some kind of naming convention to prevent name clashes. E.g., scope.eDirProp1 = ... and scope.aDirProp1 = ....
Yes by using element.isolateScope() for example (or see fiddle):
HTML
<div ng-app="app" ng-controller="BaseController as baseCtrl">
<input type="text" ng-model="inputA.value" directive-config="{data: 'bar'}" >
<input type="text" ng-model="inputB.value" directive-config="{parsers: externalParser, data: 'buzz'}" custom-input >
<br><br>
<span style="font-style: italic; font-size: 12px; color: red;">*Open Console to view output</span>
</div>
JS
(function(angular){
"use strict";
angular.module("app", [])
.controller("BaseController", ['$scope', function($scope){
$scope.inputA = {value: "This is inputA"};
$scope.inputB = {value: "This is inputB"};
$scope.externalParser = function(value) {
console.log("...parsing value: ", value);
}
}])
.directive("input", [function() {
return {
restrict: "E",
require: '?ngModel',
scope: {
directiveConfig: "="
},
link: function(scope, element, attrs, ngModelCtrl) {
console.log("input directive - scope: ", scope);
console.log("input directive - scope.directiveConfig.data: ", scope.directiveConfig.data);
}
}
}])
.directive("customInput", [function() {
return {
restrict: "A",
require: '?ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
console.log("");
console.log("--------------------------------------------");
console.log("customInput directive - scope: ", scope);
// Use `element.isolateScope()`
var parentScope = element.isolateScope();
console.log("customInput directive - parentScope.directiveConfig.parsers: ", parentScope.directiveConfig.parsers);
console.log("customInput directive - parentScope.directiveConfig.data: ", parentScope.directiveConfig.data);
console.log("");
console.log("--------------------------------------------");
console.warn("DO NOT USE `$$childHead` as it may not target the element you are expecting; use `element.isolateScope()` instead.");
// DO NOT USE `$$childHead` as it may not be the element you expect
console.log("customInput directive - scope.$$childHead.directiveConfig.parsers: ", scope.$$childHead.directiveConfig.parsers);
console.log("customInput directive - scope.$$childHead.directiveConfig.data: ", scope.$$childHead.directiveConfig.data);
}
}
}])
;
})(angular)
console output
//input directive - scope: n {$id: 3, $$childTail: null, $$childHead: null, $$prevSibling: null, $$nextSibling: null…}
//input directive - scope.directiveConfig.data: bar
//input directive - scope: n {$id: 4, $$childTail: null, $$childHead: null, $$prevSibling: n, $$nextSibling: null…}
//input directive - scope.directiveConfig.data: buzz
//--------------------------------------------
//customInput directive - scope: b {$$childTail: n, $$childHead: n, $$nextSibling: null, $$watchers: Array[4], $$listeners: Object…}
//customInput directive - parentScope.directiveConfig.parsers: function (value) {
// console.log("...parsing value: ", value);
// }
//customInput directive - parentScope.directiveConfig.data: buzz
//--------------------------------------------
//DO NOT USE `$$childHead` as it may not target the element you are expecting; use `element.isolateScope()` instead.
//customInput directive - scope.$$childHead.directiveConfig.parsers: undefined
//customInput directive - scope.$$childHead.directiveConfig.data: bar

Resources