Directive within directive - angularjs

I have a custom markdown directive that works fine. Now I would like to use a custom youtube directive within the content that is loaded through that markdown directive. The youtube directive on its own works fine, but as soon as I put it within the markdown file, it gets ignored by angular.
The following works fine (but is not what I want to do):
.html
<div markdown link="contentfile.md">
</div>
<div my-youtube code="'videoidhere'"></div>
.md
markdown content here
The following is what I want to do (but does not work):
.html
<div markdown link="contentfile.md">
</div>
.md
markdown content here
<div my-youtube code="'videoidhere'"></div>
more markdown content here
In the second case, it seems as if the YouTube directive is never even called.
What do I need to do to tell angular to evaluate that directive, after the markdown directive was evaluated?
For completeness, here are the directives:
markdown:
app.directive( 'markdown', function( $http ) {
var converter = new Showdown.converter();
return {
restrict: 'A',
scope: { link: '#' },
link: function ( scope, element, attrs )
{
attrs.$observe('link',function(link)
{
$http.get('modules/test/files/' + link).success(function(response)
{
var htmlText = converter.makeHtml(response);
return element.html(htmlText);
});
});
}
};
});
youtube:
app.directive('myYoutube', function( $sce ) {
return {
restrict: 'EA',
scope: { code:'=' },
replace: true,
template: '<div style="height:400px;"><iframe style="overflow:hidden;height:100%;width:100%" width="100%" height="100%" src="{{url}}" frameborder="0" allowfullscreen></iframe></div>',
link: function (scope) {
scope.$watch('code', function (newVal) {
if (newVal) {
scope.url = $sce.trustAsResourceUrl("http://www.youtube.com/embed/" + newVal);
}
});
}
};
});

You do add to the DOM to the .md file using the markdown directive, but as it isn't compiled, angular does not register it.
Something like this should work:
$http.get('modules/test/files/' + link).success(function(response)
{
var htmlText = converter.makeHtml(response);
element.html(htmlText);
$compile( element.contents() )( scope );
return element;
});

Related

using ng-include along with $templateCache not finding file

I have a directive, where I'm trying to dynamically load different partials depending on an object that is injected into directive
function countSummary() {
var directive = {
scope: {
countData: '='
},
link: link,
template: '<div ng-include=\'countData.countType === "expected" ? ' + '"/app/count/countsummary/countExpected.html" :' +
'"/app/count/countsummary/countBlind.html"\'>' +
'</div>'
}
return directive;
function link(scope, element, attrs) { ... }
}
I'm using grunt-html2js to convert all html files to be added to $templateCache. I have verified that the html file is in added to $templateCache, however when I load the page it is having difficulty finding only the .html files that are referenced in the template function.
Is this a timing issue of any sort? Is there a better way to use the template function?
ng-include argument needs to evaluate to URL. I'd do the following, which will be dynamic as the scope variable changes (using the ng-if directive will conditionally switch between views):
function countSummary() {
var directive = {
scope: {
countData: '='
},
link: link,
template: '<div ng-if="countData.countType === \'expected\'" ng-include="\'/app/count/countsummary/countExpected.html\'"></div>' +
'<div ng-if="countData.countType !== \'expected\'" ng-include="\'/app/count/countsummary/countBlind.html\'"></div>'
}
return directive;
function link(scope, element, attrs) { ... }
}
An alternative way of doing this, which opens up a lot more options, is to compile in the link function:
<script type="text/ng-template" id="my_template_1">
<div ng-if="countData.countType === 'expected'" ng-include="/app/count/countsummary/countExpected.html"></div>
<div ng-if="countData.countType !== 'expected'" ng-include="/app/count/countsummary/countBlind.html"></div>
</script>
function link(scope, element, attrs) {
var html = $templateCache.get('my_template_1');
// parse HTML into DOM element
var template = angular.element( html );
// compile the template
var linkFn = $compile(template);
// link the compiled template with the scope
var el = linkFn(scope);
// append to DOM
element.appendChild(el);
}

KendoUI not working in AngularJS directive with transclude = true

In this example, I have two AngularJS KendoDatePickers. The first one works perfectly, if you click on the button you open the calendar. The second one is within a directive that has the transclude attribute set to true. If you click on the second button, you get an error.
My understanding is that the scope of the transcluded portion inherits from the control scope, so this should work. Where am I wrong?
This is the plunk
HTML
<input kendo-date-picker="picker" />
<button ng-click="openPicker()">Open Date Picker</button>
<my-window>
<input kendo-date-picker="picker2" />
<button ng-click="openPicker2()">Open Date Picker 2</button>
</my-window>
Javascript
var app = angular.module("app", [ "kendo.directives" ]);
app.controller('MyCtrl', function($scope) {
$scope.openPicker = function () {
$scope.picker.open();
};
$scope.openPicker2 = function () {
$scope.picker2.open();
};
});
app.directive('myWindow', function() {
return {
transclude: true,
scope: {
someVar: '='
},
restrict: 'EA',
template: function(element, attrs) {
var html = '<div ng-transclude></div>';
return html;
}
};
});
There are two things about your code:
first: you create an isolatedScope so you do not have access to the controller scope inside the directive scope.
second: transcluded content get their own scope. One way to work around this is by not using transclude at all, like the example below:
return {
transclude: false,
restrict: 'EA',
template: function(element, attrs) {
var html = '<div>'+element.html()+'</div>';
return html;
}
or use the link function and manually transclude the element with the scope of the directive

Directive with isolated scope and added properties, not available to inner directives

I'd like to have a directive with an isolated scope, and to set properties to this scope from within the directive. That is to create some environment variables, which would be displayed by other directives inside it, like so:
HTML:
<div environment> <!-- this directive set properties to the scope it creates-->
{{ env.value }} <!-- which would be available -->
<div display1 data="env"></div> <!-- to be displayed by other directives (graphs, -->
<div display2 data="env"></div> <!-- charts...) -->
</div>
JS:
angular.module("test", [])
.directive("environment", function() {
return {
restrict: 'A',
scope: {},
link: function(scope) {
scope.env = {
value: "property set from inside the directive"
};
}
};
})
.directive("display1", function() {
return {
restrict: 'A',
require: '^environment'
scope: {
data: '='
},
link: function(scope, elt, attr, envController) {
scope.$watch('data', function(oldV, newV) {
console.log("display data");
});
}
};
})
.directive("display2", function() {
return {/* ... */};
});
But it doesn't work. Here is a Plunker.
If I remove the isolation, it works ok though. What do I do wrong ? Is it a problem of transclusion ? It seems to work if I use a template in the 'environment' directive, but this is not what I want.
Thanks for your help.
Edit: I see this same problem answered here. The proposed solution would be to use a controller instead of a directive. The reason I wanted to use a directive is the possibility to use 'require' in the inner directives, thing that can't be done with ngController I think.
By introducing external templates, I managed to find a working solution to your problem.
I'm quite certain the way you have it set up has worked at some point but I can't be certain about when. The last time I built a directive not reliant on an external markup file, I don't even know.
In any case, the following should work, if you are willing to introduce separate templates for your directives:
app.directive('environment', function () {
return {
restrict: 'A',
templateUrl: 'env.html',
replace: true,
scope: {},
link: function (scope, el, attrs) {
scope.env = {
value: "property set from inside the directive"
};
}
};
});
app.directive('display1', function () {
return {
restrict: 'A',
scope: {
data: '='
},
templateUrl: 'display1.html',
replace: false,
link: function(scope) {
// console.log(scope.data);
}
};
});
And then for your markup (these wouldn't sit in <script> tags realistically, you would more than likely have an external template but this is simply taken from the fiddle I set up).
<script type="text/ng-template" id="display1.html">
<span>Display1 is: {{data}}</span>
</script>
<script type="text/ng-template" id="env.html">
<div>
<h1>env.value is: {{env.value}}</h1>
<span display1 data="env.value"></span>
</div>
</script>
<div>
<div environment></div>
</div>
Fiddle link: http://jsfiddle.net/ADukg/5421/
Edit: After reading that you do not want to use templates (should've done that first..), here's another solution to get it working. Unfortunately, the only one you can go with (aside from a few others, link coming below) and in my opinion it is not a good looking one...
app.directive('environment', function () {
return {
restrict: 'A',
template: function (element, attrs) {
return element.html();
},
scope: {},
link: function (scope, el, attrs) {
scope.env = {
value: "property set from inside the directive"
};
}
};
});
And the markup:
<div environment> {{env.value}} </div>
Fiddle: http://jsfiddle.net/7K6KK/1/
Say what you will about it, but it does do the trick.
Here's a thread off of the Angular Github Repo, outlining your issue and why it is not 'supported'.
I did a small edit to your Plunker
When you create a variable on scope of directive other directives can access it two ways (presented in plunker) either directly or by two-way data binding
HTML:
<body ng-app="test">
<div environment>
{{ env.value }}
<div display1 data="env"></div>
<div display2 data="env"></div>
</div>
</body>
<input type="text" ng-model="env.value"> #added to show two-way data binding work
<div display1 info="env"></div> #changed name of attribute where variable is passed, it's then displayed inside directive template
<div display2>{{env.value}}</div> #env.value comes from environment directive not from display2
</div>
JS
angular.module("test", [])
.directive("environment", function() {
return {
restrict: 'A',
scope: true, #changed from {} to true, each environment directive will have isolated scope
link: function(scope) {
scope.env = {
value: "property set from inside the directive"
};
}
};
})
.directive("display1", function() {
return {
restrict: 'A',
template: '<span ng-bind="info.value"></span>', #added template for directive which uses passed variable, NOTE: dot in ng-bind, if you try a two-way databinding and you don't have a dot you are doing something wrong (Misko Hevry words)
scope: {
info: '=' #set two-way data binding for variable from environment directive passed in 'info' attribute
}, #removed unnecessary watch for variable
};
})
.directive("display2", function() {
return {/* ... */};
});

Within an ng-repeat, how can I pass the value of a element to a directive?

I have an event object whose data comes from a json, this event has an array of videos which I am trying to display as YouTube embeds. I am trying to use a directive to accomplish this but it is not working within the ng-repeat I am doing for the videos. This is the code of the directive:
app.directive('youtube', function($sce) {
return {
restrict: 'EA',
scope: { code:'=' },
replace: true,
template: '<iframe width="560" height="315" src="{{url}}" frameborder="0" allowfullscreen></iframe>',
link: function (scope) {
scope.$watch('code', function (newVal) {
if (newVal) {
scope.url = $sce.trustAsResourceUrl("http://www.youtube.com/embed/" + newVal);
}
});
}
};
});
When I try to use it like this, then it doesn't works, the variable is not parsed if I use the curly brackets and it doesn't works either without them:
<div ng-repeat="video in event.videos" class="embed">
<div youtube code="{{video}}"></div>
</div>
The {{video}} element has the id of the YouTube video, if I do this ng-repeat without the directive, the video ids print fine, so it is working but not parsing it when used within the directive.
Any help is appreciated, thanks in advance!
try ng-src in your template
app.directive('youtube', function($sce) {
return {
restrict: 'EA',
scope: { code:'=' },
replace: true,
template: '',
link: function (scope) {
scope.$watch('code', function (newVal, oldVal) { // use newVal and oldVal
console.log(newVal); // log it to see that it is passed
if (newVal !== undefined) {
scope.url = $sce.trustAsResourceUrl("http://www.youtube.com/embed/" + newVal);
}
});
}
};
});
also, in your HTML, you don't need {{ }} around video, its already a variable on the scope
<div ng-repeat="video in event.videos" class="embed">
<div youtube code="video"></div>
</div>

Load angular directive in view, based on $scope value

I have a directive defined as
Application.Directives.directive('listview', function() {
return {
restrict: 'EAC',
templateUrl: 'directives/listview/view.html'
};
});
And then want to include it from the main view like this
<div class="{{directiveName}}">
</div>
where directiveName equals "listview". However, it does not work. It generates the below code, but the listview directive does not get loaded
<div class="listview">
</div>
Yet, when I type the above generated code directly into the main template, it does load the directive. How come? How can I make it work?
So I found a way. What you'd want is something like this
<div {{directiveNameInScope}}></div>
But again, that doesn't work. So I created a directive to do it for you. It works like
<div loaddirective="directiveNameInScope"></div>
where the loaddirective directive looks like
Application.Directives.directive('loaddirective', function($compile) {
return {
restrict: 'A',
scope: { loaddirective : "=loaddirective" },
link: function($scope, $element, $attr) {
var value = $scope.loaddirective;
if (value) {
// Load the directive and make it reactive
$element.html("<div "+value+"></div>");
$compile($element.contents())($scope);
}
},
replace: true
};
});
I put it up on github here: https://github.com/willemmulder/loaddirective

Resources