I have the following directive:
app.directive("mydirective", ['$compile', function($compile) {
function link(scope, element, attrs, ctrl, $transclude) {
var actionBtnHTML = `<button type="submit" ng-show="show"></button>`;
element.parent().append(actionBtnHTML);
$compile(element)(scope);
}
return {
restrict: 'A',
scope: {},
link: link,
controller: ['$scope', function MyDirectiveController($scope) {
$scope.show = true;
}]
}]);
My directive simply adds a button after the HTML tag with the mydirective attribute.
I want that the added HTML has the same scope as the directive (ie. the new isolated scope). But it is not the case in this configuration. I guess this is because the added HTML is outside the directive HTML tag.
Whence my question, how can I apply the isolated scope of my directive on the template appended to parent element?
You can use ngTransclude to insert the extra HTML content while keeping the same scope of the directive.
directive("mydirective", ['$compile', function($compile) {
return {
restrict: 'A',
scope: {},
controller: ['$scope', function MyDirectiveController($scope) {
$scope.show = true;
}],
transclude: true,
template: '<ng-transclude></ng-transclude>' +
' <button type="submit" ng-show="show">Submit!</button>'
}
}])
Here's a demo fiddle for your directive!
Related
Note: ui-grid is name of my custom grid. Sorry about the confusion.
I have a custom directive which will have a child custom directive and will be called in this fashion in the html.
<ui-grid resource="/api/data.json">
<ui-gridcolumns>
</ui-gridcolumns>
</ui-grid>
When the child directive is enclosed within the parent directive, the console statements do not print, but when the child directive is outside of the parent directive, it prints console.log just fine. Any insight into how to make it work with the child directive within the parent directive is appreciated.
parent directive:
module.exports = function () {
return {
restrict: 'E',
templateUrl: 'app/ui-grid/grid.template.html',
link: function (scope, element, attrs) {
console.log('linked Grid');
},
controller: ['$scope', function ($scope) {
$scope.onthescreen = 'test value';
}]
};
};
Child directive:
module.exports = function () {
return {
restrict: 'E',
templateUrl: 'app/ui-grid/gridcolumns/grid.columns.template.html',
link: function (scope, element, attrs) {
console.log('linked Grid Columns');
},
controller: ['$scope', function ($scope) {
console.log('calling Grid Columns');
}]
};
};
The entire code base is in git for reference.
https://github.com/eshrinivasan/angular-gulp-browserify-boilerplate
To achieve desired behavior use transclusion:
config:
restrict: 'E',
templateUrl: 'app/ui-grid/grid.template.html',
link: function (scope, element, attrs) {
console.log('linked Grid');
},
controller: ['$scope', function ($scope) {
$scope.onthescreen = 'test value';
}],
transclude: true
and somewhere in template:
<div ng-transclude>
child directives will be placed here
</div>
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
I have a directive:
app.directive('eventView', function () {
return {
restrict: 'E',
replace:true,
templateUrl: 'partials/EventView.html',
controller: 'eventController',
link: function(scope, element){
scope.$on("$destroy",function() {
element.remove();
});
}
}
});
The controller for the directive:
app.controller('eventController', function($scope, $rootScope){
$scope.removeEvent = function (){
$scope.$broadcast("$destroy")
}
});
There are several instances of the same directives in the rootscope. Each directive is a div in which there is a remove button which is bind to the removeEvent method in the controller definition.
The problem is that when a remove button is pressed in one those div(from eventView directive), all the divs are being removed. I only want the current directive from where the destroy is being broadcasted to be removed. I know its because i'm broadcasting $scope.$broadcast("$destroy") but how can I remove only the current scope, is there a predefined method only for that current scope
if you are using ng-repeat then you can use $index as the unique identifier for those div...
just update your code like below:
app.directive('eventView', function () {
return {
restrict: 'E',
replace:true,
templateUrl: 'partials/EventView.html',
controller: 'eventController',
link: function(scope, element){
scope.$on("$destroy" + identifier,function() {
element.remove();
});
},
scope: {identifier : '#'}
}
});
and broadcast the event with identifier
app.controller('eventController', function($scope, $rootScope){
$scope.removeEvent = function (identifier){
$scope.$broadcast("$destroy" + identifier)
}
});
if the divs are not part of ng-repeat, then you will need to have some kind of identifier to identify which div to destroy...
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");
}
};
});
I'm not sure this is the way to do this, but my goal is the following:
I have a parent directive
Inside the parent directive's block, I have a child directive that will get some input from the user
The child directive will set a value in the parent directive's scope
I can take it from there
Of course the problem is that the parent and child directives are siblings. So I don't know how to do this. Note - I do not want to set data in the
Fiddle: http://jsfiddle.net/rrosen326/CZWS4/
html:
<div ng-controller="parentController">
<parent-dir dir-data="display this data">
<child-dir></child-dir>
</parent-dir>
</div>
Javascript
var testapp = angular.module('testapp', []);
testapp.controller('parentController', ['$scope', '$window', function ($scope, $window) {
console.log('parentController scope id = ', $scope.$id);
$scope.ctrl_data = "irrelevant ctrl data";
}]);
testapp.directive('parentDir', function factory() {
return {
restrict: 'ECA',
scope: {
ctrl_data: '#'
},
template: '<div><b>parentDir scope.dirData:</b> {{dirData}} <div class="offset1" ng-transclude></div> </div>',
replace: false,
transclude: true,
link: function (scope, element, attrs) {
scope.dirData = attrs.dirData;
console.log("parent_dir scope: ", scope.$id);
}
};
});
testapp.directive('childDir', function factory() {
return {
restrict: 'ECA',
template: '<h4>Begin child directive</h4><input type="text" ng-model="dirData" /></br><div><b>childDir scope.dirData:</b> {{dirData}}</div>',
replace: false,
transclude: false,
link: function (scope, element, attrs) {
console.log("child_dir scope: ", scope.$id);
scope.dirData = "No, THIS data!"; // default text
}
};
});
If you want that kind of communication, you need to use require in the child directive. That will require the parent controller so you need a controller there with the functionality you want the children directives to use.
For example:
app.directive('parent', function() {
return {
restrict: 'E',
transclude: true,
template: '<div>{{message}}<span ng-transclude></span></div>',
controller: function($scope) {
$scope.message = "Original parent message"
this.setMessage = function(message) {
$scope.message = message;
}
}
}
});
The controller has a message in the $scope and you have a method to change it.
Why one in $scope and one using this? You can't access the $scope in the child directive, so you need to use this in the function so your child directive will be able to call it.
app.directive('child', function($timeout) {
return {
restrict: 'E',
require: '^parent',
link: function(scope, elem, attrs, parentCtrl) {
$timeout(function() {
parentCtrl.setMessage('I am the child!')
}, 3000)
}
}
})
As you see, the link receives a fourth param with the parentCtrl (or if there is more than one, an array). Here we just wait 3 seconds until we call that method we defined in the parent controller to change its message.
See it live here: http://plnkr.co/edit/72PjQSOlckGyUQnH7zOA?p=preview
First, watch this video. It explains it all.
Basically, you need to require: '^parentDir' and then it will get passed into your link function:
link: function (scope, element, attrs, ParentCtrl) {
ParentCtrl.$scope.something = '';
}