I have a directive that can contain some HTML inside it. How can I get it? I have tried link and compile but I am getting the HTML that is defined in the template. Here is my view:
<my-directive ng-model="ctrl.SomeField">
<p> This is the HTML I want! <p>
</my-directive>
Here is my directive:
return {
restrict: 'E',
scope: {
ngModel: "="
},
template: "<p>This is the HTML that is being returned from compile and link!</p>" +
"<p>This is not the HTML that I want!</p>"
link: {
pre: function preLink(scope, element, attrs) {
var html = element.html(); //returns the html in the directive template
},
post: function postLink(scope, element, attrs) {
var html = element.html(); //returns the html in the directive template
}
},
compile: function(element, attrs){
var html = element.html(); //returns the html in the directive template
}
}
How do I get the HTML from my view and not from my directive template?
Edit: Here is an example - http://plnkr.co/edit/z6gFOrGG01jKoKwISHcW?p=preview
Here is my solution! http://plnkr.co/edit/OlRyBN1I0jCkAREIKVeC?p=preview. Basically I just needed to use another directive to do some fancy transclusion stuff in a link step.
Here is the directive:
function returnFn() {
return {
link: {
pre: function (scope, element, attr, ctrl, transclude) {
if (transclude) {
transclude(scope, function (clone) {
element.append(clone);
});
}
}
}
}
}
return [returnFn];
Related
I have used angular JS custom directive compile function, in that I am not able to get the compiled HTML element. I am getting-
<div id="test_{{value}}" modulename="test_{{value}}"></div>
How to get the compiled html element in Post link method?
like this,
<div id="test_1" modulename="test_1"></div>
compile function code as follows,
module.directive("custom", ['$compile', function ($compile) {
return {
restrict: 'CEA',
transclude:true,
compile: function(el) {
if (settings.require && settings.require.length) {
return function(scope, el) {
$compile(el)(scope);
}
}
return {
pre: function(scope, element, attrs, ctrls) {
},
post: function(scope, element, attrs, ctrls) {
//here need to get compiled html element
}
}
}
};
}
Thanks in advance
I have this html fragment with my directive asScrollTop as attribute:
<div data-as-scroll-top>
<div data-ng-repeat="chatMessageOfUser in vm.chatMessagesOfUser">
<!-- use chatMessageOfUser -->
</div>
</div>
and this is my directive:
(function() {
'use strict';
angular
.module('myProject.common')
.directive('asScrollTop', asScrollTop);
function asScrollTop(validateService) {
var directive = {
restrict: 'A',
link: link
};
return directive;
////////////
function link(scope, element, attr) {
console.log(element);
element.on('scroll', function() {
if(element[0].scrollTop <= 0) {
// here I need vm.chatMessagesOfUser or the first entry of the
// vm.chatMessagesOfUser array
}
});
}
}
})();
My question now would be how I can make the the vm.chatMessagesOfUser array available in directive?
You can defined scope in directive like
var directive = {
restrict: 'A',
scope: { yourList: '=yourList' },
link: link
};
return directive;
function link(scope, element, attr) {
console.log(scope.yourList);
};
and set value in html markup
<div data-as-scroll-top your-list="vm.chatMessagesOfUser"></div>
Since your attribute directive uses the existing scope, the variables are on that scope.
function link(scope, element, attr) {
console.log(element);
element.on('scroll', function() {
if(element[0].scrollTop <= 0) {
// here I need vm.chatMessagesOfUser
console.log(scope.vm.chatMessaagesOfUser);
// or the first entry of the
// vm.chatMessagesOfUser array
console.log(scope.vm.chatMessangerOfUser[0]);
}
});
}
I'm using ng-repeat and I need to pass a scope variable into a directive's compile function. I know how to do it with the link function, but not the compile function.
My html looks like:
<div ng-repeat="item in chapter.main">
<block type="item.type"></block>
</div>
Let's say item.type="blah" no matter the item.
Then this link function works fine
app.directive('block', function() {
return {
restrict: 'E',
link: function(scope, element, attributes){
scope.$watch(attributes.type, function(value){
console.log(value); //will output "blah" which is correct
});
}
}
});
But I can't do the same with compile?
app.directive('block', function() {
return {
restrict: 'E',
compile: function(element, attrs, scope) {
scope.$watch(attrs.type, function(value){
console.log(value);
});
}
}
});
The error I get is "cannot read property $watch of undefined"..
This is how I'd like my directive to look like:
app.directive('block', function() {
return {
restrict: 'E',
compile: function(element, attrs) {
element.append('<div ng-include="\'{{type}}-template.html\'"></div>');
//or element.append('<div ng-include="\'{' + attrs.type + '}-template.html\'"></div>');
//except the above won't interpret attr.type as a variable, just as the literal string 'item.type'
}
}
});
The compile function doesn't have scope as one it's parameter.
function compile(tElement, tAttrs, transclude) { ... }
NOTE: transclude is deprecated in the latest version of Angular.
Is there any reason you don't want to use link?
From the DOC
The compile function deals with transforming the template DOM. Since most directives do not do template transformation, it is not used often. The compile function takes the following arguments:
tElement - template element - The element where the directive has been declared. It is safe to do template transformation on the element and child elements only.
tAttrs - template attributes - Normalized list of attributes declared on this element shared between all directive compile functions.
transclude - [DEPRECATED!] A transclude linking function: function(scope, cloneLinkingFn)
UPDATE
To access the scope from inside compile function, you need to have either a preLink or postLink function. In your case, you need only the postLink function. So this ...
compile: function compile(tElement, tAttrs, transclude) {
return function postLink(scope, element, attrs) { ... }
},
PROPOSED SOLUTION Might not be exact but should help you on your way.
html
<div ng-app="myApp" ng-controller="app">
<block type="item.type"></block>
</div>
JS (Controller + Directive)
var myApp = angular.module('myApp', []);
myApp.controller('app', function ($scope, $http) {
$scope.item = {
type: 'someTmpl'
};
}).directive('block', ['$compile', function ($compile) {
return {
restrict: 'AE',
transclude: true,
scope: {
type: '='
},
compile: function (element, attrs) {
return function (scope, element, attrs) {
var tmpl;
tmpl = scope.type + '-template.html';
console.log(tmpl);
element.append('<div ng-include=' + tmpl + '></div>');
$compile(element.contents())(scope);
};
}
};
}]);
Say I have a directive like such:
<my-directive>This is my entry!</my-directive>
How can I bind the content of the element into my directive's scope?
myApp.directive('myDirective', function () {
return {
scope : {
entry : "" //what goes here to bind "This is my entry" to scope.entry?
},
restrict: "E",
template: "<textarea>{{entry}}</textarea>"
link: function (scope, elm, attr) {
}
};
});
I think there's much simpler solution to the ones already given. As far as I understand, you want to bind contents of an element to scope during initialization of directive.
Given this html:
<textarea bind-content ng-model="entry">This is my entry!</textarea>
Define bind-content as follows:
directive('bindContent', function() {
return {
require: 'ngModel',
link: function ($scope, $element, $attrs, ngModelCtrl) {
ngModelCtrl.$setViewValue($element.text());
}
}
})
Here's a demo.
I may have found a solution. It relies on the transclude function of directives. It works, but I need to better understand transclusion before being sure this is the right way.
myApp.directive('myDirective', function() {
return {
scope: {
},
restrict: 'E',
replace: false,
template: '<form>' +
'<textarea ng-model="entry"></textarea>' +
'<button ng-click="submit()">Submit</button>' +
'</form>',
transclude : true,
compile : function(element,attr,transclude){
return function (scope, iElement, iAttrs) {
transclude(scope, function(originalElement){
scope.entry = originalElement.text(); // this is where you have reference to the original element.
});
scope.submit = function(){
console.log('update entry');
}
}
}
};
});
You will want to add a template config to your directive.
myApp.directive('myDirective', function () {
return {
scope : {
entry : "=" //what goes here to bind "This is my entry" to scope.entry?
},
template: "<div>{{ entry }}</div>", //**YOU DIDN'T HAVE A TEMPLATE**
restrict: "E",
link: function (scope, elm, attr) {
//You don't need to do anything here yet
}
};
});
myApp.controller('fooController', function($scope){
$scope.foo = "BLAH BLAH BLAH!";
});
And then use your directive like this:
<div ng-controller="fooController">
<!-- sets directive "entry" to "foo" from parent scope-->
<my-directive entry="foo"></my-directive>
</div>
And angular will turn that into:
<div>THIS IS MY ENTRY</div>
Assuming that you have angular setup correctly and are including this JS file onto your page.
EDIT
It sounds like you want to do something like the following:
<my-directive="foo"></my-directive>
This isn't possible with ELEMENT directives. It is, however, with attribute directives. Check the following.
myApp.directive('myDirective', function () {
return {
template: "<div>{{ entry }}</div>", //**YOU DIDN'T HAVE A TEMPLATE**
restrict: "A",
scope : {
entry : "=myDirective" //what goes here to bind "This is my entry" to scope.entry?
},
link: function (scope, elm, attr) {
//You don't need to do anything here yet
}
};
});
Then use it like this:
<div my-directive="foo"></div>
This will alias the value passed to my-directive onto a scope variable called entry. Unfortunately, there is no way to do this with an element-restricted directive. What is preventing it from happening isn't Angular, it is the html5 guidelines that make what you are wanting to do impossible. You will have to use an attribute directive instead of an element directive.
How do I get the input element from within the directive before the template overwrites the contents?
html
<div xxx>
<input a="1" />
</div>
js
app.directive('xxx', function(){
return {
restrict: 'A',
template: '<p></p>',
replace: true, //if false, just leaves the parent div, still no input
compile: function(element, attrs) {
console.log(element);
return function (scope, iElement, iAttrs) {
}
}
};
});
i am on angular 1.0.x, I cannot pass in optional scope parameters with the '=?' syntax and i want to be able to override a portion of the default template of the directive in a very flexible way. instead of adding a scope variable or attribute everytime that I just plan on passing through the directive, I want to be able to supply the whole element to be used.
edit
the input must retain the scope of the directive, and not the parent.
edit
I am trying to include a partial template inside a directive that will overwrite a piece of the actual template. The piece I am including therefore needs to have access to the directive's scope and not the parent's.
Update
It seems if I do not provide a template or a template URL and instead replace the contents manually using the $templateCache I can have access to the inner elements. I want to let angular handle the template and the replacement though and just want to be able to access the contents in the directive naturally before they get replaced.
Solution
Plunkr
html
<body ng-controller="MainCtrl">
<div editable="obj.email">
<input validate-email="error message" ng-model="obj.email" name="contactEmail" type="text" />
</div>
</body>
js
app.controller('MainCtrl', function($scope) {
$scope.obj = {
email: 'xxx'
};
});
app.directive('editable', function($log){
return {
restrict: 'A',
transclude: true,
template: '<div ng-show="localScopeVar">{{value}}<div ng-transclude></div></div>',
scope: {
value: '=editable'
},
link: function(scope) {
scope.localScopeVar = true;
}
};
});
app.directive('validateEmail', function($log){
return {
restrict: 'A',
require: 'ngModel',
scope: true,
link: function(scope, el, attrs, ctrl) {
console.log(attrs['validateEmail']);
}
};
});
I believe you're looking for the transclude function (link is to 1.0.8 docs). You can see what's going on with:
app.directive('xxx', function($log){
return {
restrict: 'A',
transclude: true,
compile: function(element, attrs, transclude) {
$log.info("every instance element:", element);
return function (scope, iElement, iAttrs) {
$log.info("this instance element:", element);
transclude(scope, function(clone){
$log.info("clone:", clone);
});
}
}
};
});