Nested ng-transclude in Angular - angularjs

So, I have this piece of code using transclude in angular.
angular.module('playground', []).directive('tagA', function() {
return {
replace: true,
transclude: true,
controller: function() {
console.log('controller tagA')
},
compile: function($element, $attrs, $link) {
console.log('compile tagA', $element.html());
return {
pre: function($scope, $element, $attrs, controller, $transclude) {
console.log('pre tagA', $element.html());
},
post: function($scope, $element, $attrs, controller, $transclude) {
console.log('post tagA', $element.html());
}
}
},
template: '<u><tag-b><div ng-transclude></div></tag-b></u>'
}
}).directive('tagB', function() {
return {
require: '^tagA',
transclude: true,
controller: function() {
console.log('controller tagB')
},
compile: function($element, $attrs, $transclude) {
console.log('compile tagB', $element.html());
return {
pre: function($scope, $element, $attrs, controller, $transclude) {
console.log('pre tagB', $element.html());
},
post: function($scope, $element, $attrs, controller, $transclude) {
console.log('post tagB', $element.html());
}
}
},
template: '<h1 ng-transclude></h1>'
}
});
And the markup
<tag-a>
Test
</tag-a>
What I am trying to do is to pass the transcluded content from the parent (tagA) to the child (tagB). The above code works but I don't want to have <div ng-transclude></div> in my tagA's template. Apparently, using <u><tag-b ng-transclude></tag-b></u> doesn't work.
Can someone explain why the later (<u><tag-b ng-transclude></tag-b></u>) doesn't work?

The latter form doesn't work, because transclude replaces the inner HTML of the element that ng-transclude is placed on.
In the first, (working you have something like the following):
<tag-a>
<u>
<tag-b>
<div ng-transclude>
<h1 ng-transclude>
Test
</h1>
</div>
</tag-b>
</u>
</tag-a>
since the inner html is replaced, what you end up with is:
<u>
<div>
<h1>
Test
</h1>
<div>
</u>
In the second example, you have:
<tag-a>
<u>
<tag-b ng-transclude>
<h1 ng-transclude>
Test
</h1>
</tag-b>
</u>
</tag-a>
again, with the Directives removed and the inner html replaced, you get:
<u>
</u>

Related

Get ng-model from a input inside directive and put it in an attribute of directive

I have "nibTextbox" directive with an input inside it,input has a ng-model,I want that ng-model value always available in "value" attribute of directive.(I don't wanna use replace)
angular.module('nib', [])
.directive('nibTextbox', function () {
return {
restrict: 'E',
scope: {
id: '#',
title: '#',
},
compile: function (element, attributes) {
var linkFunction = function (scope, element, attributes) {
}
return linkFunction;
},
controller: ['$scope', '$http', function ($scope, $http) {
}],
template: '<div value="{{nibTextBoxValue}}"><img src="" alt=""/><label>{{title}}</label><input id="{{id}}_txt" type="text" ng-model="nibTextBoxValue"></input></div>'
};
});
<nib-textbox id="ngArmin1" title="ngArmin1Title" value="{{nibTextBoxValue}}"></nib-textbox>
value is not valid for a <div> element. So let's change it to data-div.
This is how it will look more or less (I usually work with typescript but I'll use plain javascript to pass the idea):
angular.module('nib', [])
.directive('nibTextbox', function () {
return {
restrict: 'E',
scope: {
id: '#',
title: '#',
},
compile: function (element, attributes) {
var linkFunction = function (scope, element, attributes) {
}
return linkFunction;
},
// Injected $element for manipulating attributes
controller: ['$scope', '$http', '$element', function ($scope, $http, $element) {
$scope.$watch("nibTextBoxValue", function(newValue) {
$element.attr("data-value", newValue);
});
}],
templateUrl: 'template.html' // Extracting the template to a file
};
});
The directive template (template.html):
<div>
<img src="" alt=""/><label>{{title}}</label>
<input id="{{id}}_txt" type="text" ng-model="nibTextBoxValue"></input>
</div>
In addition, remove the value attribute from your directive:
<nib-textbox id="ngArmin1" title="ngArmin1Title"></nib-textbox>
the answer:use $watch
change controller definition to this:
controller: ['$scope', '$http', '$element', function ($scope, $http, $element) {
$scope.$watch("nibTextBoxValue", function (nv) {
$element.attr("value", nv);
});
}]

How can I access to ngModel of an input from a parent Directive / angularjs

I have this view
<div my-directive="somevalue">
<input name="myField" ng-model="dataForMyField">
</div>
And this is my directive
app.directive('myDirective', function ($compile) {
return {
restrict: 'A',
template: `<div ng-transclude=""></div>
<div>SomeValue from directive <strong>{{ someReturnedValue }}</strong></div>`,
transclude: true,
controller: function($scope, $element, $attrs, $transclude) {
$scope.someReturnedValue = 'ValueFromDirective';
console.log('Name of input'); // myField
$scope.$watch('vm.ngModel', function(newValue, oldValue, scope) {
console.log('WOW! Input.ngModel changed', newValue); // world in the init
});
}
}
})
How can I access to ngModel of input.
---------> here is a plkr: http://plnkr.co/edit/nWNAuf9jbv0sgY2VYRtZ?p=preview
Try this
<div my-directive="somevalue">
<input name="myField" ng-model="dataForMyField">
</div>
app.directive('myDirective', function () {
return {
scope: {
dataForMyField: '#dataAttr'
},
restrict: 'A',
template: `<div ng-transclude=""></div>
<div>SomeValue from directive <strong>{{ someReturnedValue }}</strong></div>`,
transclude: true,
controller: function($scope, $element, $attrs, $transclude) {
$scope.someReturnedValue = 'ValueFromDirective';
console.log('Name of input'); // myField
}
}
})

AngularJS ng-transclude works unexpectedly with custom directives

I coded two nested directives, the outer one is wrapping all inner ones to build a nav. But seems the ng-transclude all inner directives. I have debugged a while, but can't figure it out. Below are the directives and the html snippet;
angular.module('custom.directives', [])
.directive('navTabs', ['$log', function($log){
$log.info('instantaiting directive navTabs...');
var _directive = {
scope: true,
restrict: 'EA',
replace: true,
transclude: true,
templateUrl: '/jsex/ab/templates/tabs.html',
controller: ['$scope', function($scope) {
$scope.currentTab = 0;
$scope.tabs = [];
$scope.selectTab = function(tab) {
$scope.currentTab = tab;
}
return $scope;
}]
};
$log.info('finish instantaiting directive navTabs');
return _directive;
}])
.directive('navTab', ['$log', '$filter', function($log, $filter){
$log.info('instantaiting directive navTab...');
var _directive = {
scope: true,
restrict: 'EA',
replace: true,
transclude: true,
require: '^navTabs',
template: '<div ng-show="showTab()" ng-transclude></div>',
link: function(scope, element, attrs, navTabs) {
var tabId = navTabs.tabs.length;
$log.info("tabId = " + tabId + " - " + attrs.tabHeading);
navTabs.tabs.push(attrs.tabHeading);
$log.info($filter('json')(navTabs.tabs));
scope.showTab = function() {
return tabId == scope.currentTab;
};
}
};
$log.info('finish instantaiting directive navTab');
return _directive;
}]);
<nav-tabs>
<nav-tab class="tab-content" tab-heading="First Tab">
<h1> First Tab</h1>
<p>Simple tab 1
</nav-tab>
<nav-tab class="tab-content" tab-heading="Second Tab">
<h1> Second Tab</h1>
<p>Simple tab 2
</nav-tab>
<nav-tab class="tab-content" tab-heading="Third Tab">
<h1> Third Tab</h1>
<p>Simple tab 3
</nav-tab>
</nav-tabs>
Thanks.

Dynamic Controller and templateUrl inject in an Angular Directive

I have troubles to find a solution to make a fully dynamic directive. I use the angular-gridster library to make an overview of tiles for a dashboard page. I want to dynamicly load some specific tiles by a flexible directive.
<div gridster="vm.model.gridsterOptions">
<ul>
<li gridster-item="tile.tileParams.gridParams" ng-repeat="tile in vm.model.dashboards.tiles">
<div class="box" ng-controller="tileGrid_TileController">
<eg-tile template-url={{tile.tileEngine.tempUrl}}
controller-name={{tile.tileEngine.tileCtrl}}>
</eg-tile>
</div>
</li>
</ul>
</div>
I have created the egTile directive :
(function () {
function implementation() {
return {
restrict: 'E',
transclude: true,
scope: true,
controller: '#',
bindToController:true,
controllerAs: 'vm',
name: 'controllerName',
templateUrl: function (elem, attrs) {
return attrs.templateUrl || 'app/module/tileGrid/view/templates/empty.html';
}
};
}
var declaration = [implementation];
angular.module('app.tileGrid').directive('egTile', declaration);
}());
This directive will work if I use a fixed string in the egTile directive like
<eg-tile template-url="string_url" controller-name= "string_ctrl"></eg-tile>
but I want to dynamicly select the controller and templateUrl.
I already tried to use the $parse and $observe service but without succes.
Is this even possible to make the directive so flexible ?
Thanks in advance
I found a solution for my problem.....
I used 2 extra directives that will provide the controller-string and the templateUrl-string for the "flexible directive" egTile.
One for creating the controller-string :
(function () {
function implementation($compile, $parse) {
return {
restrict: 'A',
scope: true,
terminal: true,
priority: 99999,
link: function (scope, elem) {
var name = $parse(elem.attr('eg-parse-controller'))(scope);
elem.removeAttr('eg-parse-controller');
elem.attr('controller-name', name);
$compile(elem)(scope);
}
};
}
var declaration = ['$compile', '$parse', implementation];
angular.module('app').directive('egParseController', declaration);
}());
And one for creating the template-string:
(function () {
function implementation($compile, $parse) {
return {
restrict: 'A',
scope: true,
terminal: true,
priority: 99998,
link: function (scope, elem) {
var name = $parse(elem.attr('eg-parse-template'))(scope);
elem.removeAttr('eg-parse-template');
elem.attr('template-url', name);
$compile(elem)(scope);
}
};
}
var declaration = ['$compile', '$parse', implementation];
angular.module('app').directive('egParseTemplate', declaration);
}());
Than I can use it as :
<div gridster="vm.model.gridsterOptions">
<ul>
<li gridster-item="tile.tileParams.gridParams" ng-repeat="tile in vm.model.dashboards.tiles" ng-controller="tileGrid_TileController">
<eg-tile tile="tile"
eg-parse-template=tile.tileEngine.tempUrl
eg-parse-controller='tile.tileEngine.tileCtrl'>
</eg-tile>
</li>
</ul>
with the directive definition :
(function () {
function implementation($parse) {
return {
restrict: 'E',
transclude: true,
scope: {
tile : '='
},
controller: '#',
bindToController:true,
controllerAs: 'vm',
name: 'controllerName',
templateUrl: 'app/module/tileGrid/view/tileTemplate.html',
link: function(scope, element, attrs) {
scope.getContentUrl = function() {
return attrs.templateUrl || 'app/module/tileGrid/view/templates/empty.html';
}
}
};
}
var declaration = ['$parse' , implementation];
angular.module('app.tileGrid').directive('egTile', declaration);
}());
With a tileTemplate.html :
<div class="box">
<div class="box-header">
<h3>{{ vm.tile.tileParams.title }}</h3>
<div class="box-header-btns pull-right">
<a title="Remove widget" ng-click="vm.removeTile(tile)">
<i class="fa fa-trash"></i>
</a>
</div>
</div>
<div class="box-content">
<div ng-include="getContentUrl()"></div>
</div>
</div>
With this aproach I have fully access to the tile that I passed to the egTile Directive in the dynamic loaded controllers and dynamic loaded views.
All remarks are welcome.

call method from directive to controller

I want to call a function in controller from directive.
my directive
.directive('multEcs', ['$http', function($http){
return{
restrict: 'A',
replace:false,
link: function(scope, elem, attr){
scope.addToArray();
}
}
}]);
Method in controller
$scope.addToArray = function(){
console.log('method called');
}
Try passing in the function you want to call to your directive.
.directive('multEcs', ['$http', function($http){
return{
restrict: 'A',
replace:false,
scope : {
myFunctionToCall : '='
},
link: function(scope, elem, attr){
scope.myFunctionToCall();
}
}
}]);
The first way is one-way binding between controller and directive:
JS:
angular.module('App',[])
.directive('multEcs', [function(){
return{
restrict: 'A',
replace: false,
scope: {
addToArray: '&'
},
link: function($scope, elem, attr){
$scope.addToArray();
}
}
}])
.controller('HomeCtrl', ['$scope', function($scope){
$scope.addToArray = function(){
console.log('method called');
}
}])
HTML:
<div ng-app="App">
<div ng-controller="HomeCtrl">
<div mult-ecs add-to-array="addToArray()">multEcs</div>
</div>
</div>
The second way is creating isolated scope in the diretive with personal controller. Recomended for communications between directives:
JS:
angular.module('App',[])
.directive('multEcs', [function(){
return {
restrict: 'A',
replace: false,
controller: 'HomeCtrl',
scope: {},
link: function($scope, element, attrs, ctrl){
ctrl.addToArray();
}
}
}])
.controller('HomeCtrl', ['$scope', function($scope){
this.addToArray = function(){
console.log('method called');
};
}]);
HTML:
<div ng-app="App">
<div>
<div mult-ecs>multEcs</div>
</div>
</div>

Resources