angularjs transclude not working in my customized directive - angularjs

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.

Related

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>

Angular directive calling another directive not working

In this plunk I have directive dir1 calling a method in directive dir2 as described here.
The problem is that the control object (scope.dir2Ctl) is empty in dir1 and I get TypeError: scope.dir2Ctl.call2 is not a function. Any ideas how to fix this?
HTML
<body ng-app="myModule" ng-controller="ctl">
<dir1 x1="1"></dir1>
</body>
Javascript
angular.module("myModule", [])
.controller('ctl', function($scope) {})
.directive('dir1', function ($timeout) {
return {
restrict: 'EA',
scope: {
x1: '='
},
template: '<p>x2 should be 2 = {{x2}} </p>' +
'<dir2 control="dir2Ctl"></dir2>',
link: function (scope, element, attrs) {
scope.dir2Ctl = {};
$timeout(function(){
console.log(scope.dir2Ctl)
scope.x2 = scope.dir2Ctl.call2();
},1000);
}
}
})
.directive('dir2', function () {
return {
restrict: 'EA',
scope: {
control: '='
},
template: '<p>some text in dir2</p>',
link: function (scope, element, attrs) {
scope.control = scope.control || {};
scope.control.call2 = function(){
return 2;
};
}
}
});

Calling parent directive scope function from child directive which triggers parent watcher

I have 2 directives: calculatorForm and calculatorAttribute. CalculatorForm is the parent directive, specifically a form which contains input tags which are calculatorAttribute directives.
I want the calculatorAttribute call calculatorForm function that changes a scope variable and trigger a watcher.
Here's my code:
angular
.module('calculator')
.directive('calculatorForm', ['CalculatorDataModel', 'CalculatorPriceModel',
function(CalculatorDataModel, CalculatorPriceModel) {
return {
restrict : 'A',
replace : true,
templateUrl : function(element, attrs) {
return attrs.templateUrl;
},
link : function(scope, element, attrs) {
scope.model = CalculatorDataModel;
scope.price = CalculatorPriceModel;
scope.model.initialize(calculator_data);
scope.updateSelectedSpecs = function(attribute_id, prod_attr_val_id) {
var selected_specs = JSON.parse(JSON.stringify(scope.model.selected_specs));
selected_specs[attribute_id] = prod_attr_val_id;
scope.model.selected_specs = selected_specs;
}
scope.$watch('model.selected_specs', function(selected_specs, previous_selected_specs) {
if (selected_specs != previous_selected_specs) {
scope.model.setCalculatorData();
scope.price.computePrice();
}
});
}
}
}
])
.directive('calculatorAttribute', [
function() {
return {
restrict : 'A',
template : "<input type='radio' name='attr{{attribute_id}}' ng-value='prod_attr_val_id'/>",
replace : true,
link : function(scope, element, attrs) {
scope.attribute_id = attrs.attributeId;
scope.prod_attr_val_id = attrs.prodAttrValId;
element.on('click', function() {
scope.$parent.updateSelectedSpecs(scope.attribute_id, scope.prod_attr_val_id);
});
}
}
}
]);
My problem is updateSelectedSpecs in the parent is called but watcher has never been triggered when I use element.on click in the child directive.
Please help everyone Thank you!!!
Okay, after wrestling with this for a bit, I managed to produce a working version of a slimmed-down example:
angular.module('myApp', [])
.directive('calculatorForm', function() {
return {
restrict: 'A',
replace: true,
transclude: true,
template: '<div ng-transclude></div>',
link: function(scope, element, attrs) {
scope.model = {};
scope.price = {};
scope.updateSelectedSpecs = function(attribute_id, prod_attr_val_id) {
scope.$apply(function() {
console.log('update selected specs');
var selected_specs = {};
selected_specs[attribute_id] = prod_attr_val_id;
scope.model.selected_specs = selected_specs;
});
}
scope.$watch('model.selected_specs', function(selected_specs, previous_selected_specs) {
console.log('new selected specs', selected_specs, previous_selected_specs);
if (selected_specs != previous_selected_specs) {
console.log("and they're different");
}
});
}
};
})
.directive('calculatorAttribute', function() {
return {
restrict: 'A',
template: "<input type='radio' name='attr{{attribute_id}}' ng-value='prod_attr_val_id'/>",
replace: true,
link: function(scope, element, attrs) {
scope.attribute_id = attrs.attributeId;
scope.prod_attr_val_id = attrs.prodAttrValId;
element.on('click', function() {
scope.$parent.updateSelectedSpecs(scope.attribute_id, scope.prod_attr_val_id);
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<form calculator-form ng-app="myApp">
<input calculator-attribute attribute-id=1 prod-attr-val-id=1>
</form>
Just look at the console to see it getting into the $watch. The problem seemed to be the fact that you didn't trigger a $digest cycle in your updateSelectedSpecs function. Usually a $timeout, $http call, or ngClick or other event would start the $digest cycle for you, but in this case you have to start it yourself using scope.$apply().

Issue in Custom Directives

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.

AngularJS - cannot access directive controller in another directive

I have two angularjs directives (extWindow and taskBar) and want to inject taskBar's controller into extWindow in order to access it's scope. Because they don't share the same scope I used
require : '^$directive'
syntax to include it.
Doing so I could get rid of the error 'Controller 'taskBar', required by directive 'extWindow', can't be found!' but TaskBarCtrl is still undefined in link(..) method of the extWindow directive.
Any suggestions how to fix it?
var mod = angular.module('ui', [])
.directive('taskBar', function() {
var link = function(scope, el, attrs) {
$(el).css('display', 'block');
$(scope.titles).each(function(i,t) {
el.append('<span>' + t + '</span>')
});
};
return {
scope: {},
restrict : 'E',
controller: function($scope, $element, $attrs) {
$scope.titles = [];
this.addTitle = function(title) {
$scope.titles.push(w);
};
this.removeTitle = function(title) {
$scope.titles = jQuery.grep(function(n,i) {
return title != n;
});
}
},
link: link
};
}).directive('extWindow', function() {
return {
scope: {},
require: '^?taskBar',
restrict: 'E',
transclude: true,
template: '<div class="ui-window">\
<div class="ui-window-header"><span>{{windowTitle}}</span><div class="ui-window-close" ng-click="close()">X</div></div>\
<div class="ui-window-content" ng-transclude></div>\
</div>',
link: function(scope, element, attrs, taskBarCtrl) {
scope.windowTitle = attrs['windowTitle'];
scope.close = function() {
$(element).css('display', 'none');
}
//taskBarCtrl is not recognized!!!
taskBarCtrl.addTitle(scope.windowTitle);
}
}
});
http://jsfiddle.net/wa9fs2nm/
Thank you.
golbie.
If you have a controller for your parent directive and you need something like.
this.scope = $scope;
this.attrs = $attrs;
And in your in you link function for the child you need something like
var Ctrl = ctrl || scope.$parent.tBarCtrl;
Here's a Plunker

Resources