Angular: template from inner html and add existing data - angularjs

Trying to add li from template and add items from $scope.menuItems (controller)
Items don't display
http://plnkr.co/edit/Jo7Vml?p=preview
app.directive("menuDemo", function(){
return {
templateUrl: 'tree-renderer.html'
}
});

Here you go : http://plnkr.co/edit/8YV6cdCIPCjEOq3DtjAz?p=preview
First : added the following to the directive :
scope:{
menuItems:"=menuDemo"
},
This enable the two-way binding on the data you pass to the menu-demo attribute when using your directives, without this you can't pass data to your directive.
Second : remove the script tag in the tree-renderer.html and sub-tree-renderer.html. Script tag is only usefull when using ng-include along with it. You were just define a template part without using it like this.
Third adding a top level ng-repeat for the 1st level in tree-renderer.html and remove the one from index.html.
<div ng-repeat="menuItem in menuItems">
As you can see menuItems match with what i have defined in the scope:{} part.
Fourth : I added the binding of the function addItem and addSubItem so you can define your own way of adding items to the tree outisde of the directive.
EDIT : Another plnkr where i move out the <div ng-repeat="menuItem in menuItems"> http://plnkr.co/edit/MZ3nsY6WTG3EiNKhXjAv?p=preview.
As you can see i put the menu-demo call in a <span> tag within the <li> tag. This is because they both use isoled scope and if i want to use data from the ng-repeat loop the easiest way is to move the other directive in a child DOM element.
Note that i also change the name of the binding element to menuItem (i remove the 's').

That is not a correct way to declare a directive try something like this:
angular.module('myModule')
.directive('menu', [function () {
return {
restrict: 'A',
scope: true,
templateUrl: 'tree-renderer.html',
link: function ($scope, $element, $attrs) {
//here some functions that do something
}
};
}]);
By the way, try improve your questions!

Related

Run 'ng-click' inside a directive's isolated scope

Thanks in advance for taking the time to look into this question
I have serverside generated code that renders a directive wrapped around pre-rendered content.
<serverside-demo color="blue">
<p><strong>Content from Server, wrapped in a directive.</strong></p>
<p>I want this color to show: <span ng-style="{color: color}">{{color}}</span></p>
<button ng-click="onClickButtonInDirective()">Click Me</button>
</serverside-demo>
This means that 1.) the directive tag, 2.) the content inside the directive tag, 3.)the ng-click and 4.) The curly braces syntax are all generated by the server.
I want AngularJs to pick up the generated code, recompile the template and deal with the scope.
The problem is that I am having trouble getting it working. I understand that because the ng-click is inside the controller block, it is picked up not by the directive isolated scope, but the parent controllers. Instead I want the opposite... to pick up the onClickButtonInDirective scope function inside the serversideDemo link
I have created a jsfiddle best explaining my problem, which aims to clearly demonstrate the working "traditional" way of loading the template separately (which works) comparing it to the server-side way.
https://jsfiddle.net/stevewbrown/beLccjd2/3/
What is the best way to do this?
Thank you!
There are two major problem in your code
1- directive name and dom element not matched, - missing in dom element
app.directive('serverSideDemo', function() {
use <server-side-demo color="blue"> instead of <serverside-demo color="blue">
2- you need to compile the html code of server-side-demo dom with directive scope in link function
$compile(element.contents())(scope);
Working jsfiddle
Use templateUrl instead of template to fetch the content of directive from server:
app.directive('serverSideDemo', function() {
return {
restrict: 'AE',
scope: {
color: '='
},
templateUrl: 'link/that/returns/html/content',
link: function(scope, element, attrs) {
scope.onClickButtonInDirective = function() {
console.log('You clicked the button in the serverside demo')
scope.color = scope.color === 'blue' ? 'red' : 'blue';
}
}
};
});
Have a look at angular docs for more details

AngularJS : directives which take a template through a configuration object, and show that template multiple times

I'm looking to create a custom directive that will take a template as a property of a configuration object, and show that template a given number of times surrounded by a header and footer. What's the best approach to create such a directive?
The directive would receive the configuration object as a scope option:
var app = angular.module('app');
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
config: '=?'
}
...
}
}
This object (called config) is passed optionally to the directive using two way binding, as show in the code above. The configuration object can include a template and a number indicating the number of times the directive should show the template. Consider, for example, the following config object:
var config = {
times: 3,
template: '<div>my template</div>'
};
It would, when passed to the directive, cause the directive to show the template five times (using an ng-repeat.) The directive also shows a header and a footer above and below the template(s):
<div>the header</div>
<div>my template</div>
<div>my template</div>
<div>my template</div>
<div>the footer</div>
What's the best way to implement this directive? Note: When you reply, please provide a working example in a code playground such as Plunker, as I've run into problems with each possible implementation I've explored.
Update, the solutions I've explored include:
The use of the directive's link function to append the head, template with ng-repeat, and footer. This suffers from the problem of the template not being repeated, for some unknown reason, and the whole solutions seems like a hack.
The insertion of the template from the configuration object into middle of the template of the directive itself. This proves difficult because jqLite seems to have removed all notion of a CSS selector from its jQuery-based API, leading me to wonder if this solution is "the Angular way."
The use of the compile function to build out the template. This seems right to me, but I don't know if it will work.
You could indeed use ng-repeat but within your directive template rather than manually in the link (as that wouldn't be compiled, hence not repeated).
One question you didn't answer is, should this repeated template be compiled and linked by Angular, or is it going to be static HTML only?
.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
config: '=?'
},
templateUrl: 'myTemplate',
link: function(scope) {
scope.array = new Array(config.times);
}
}
}
With myTemplate being:
<header>...</header>
<div ng-repeat="item in array" ng-bind-html="config.template"></div>
<footer>...</footer>
I'd think to use ng-transclude in this case, because the header & footer wrapper will be provided by the directive the inner content should change on basis of condition.
Markup
<my-directive>
<div ng-repeat="item in ['1','2','3']" ng-bind-html="config.template| trustedhtml"><div>
</my-directive>
Directive
var app = angular.module('app');
app.directive('myDirective', function($sce) {
return {
restrict: 'E',
transclude: true,
template: '<div>the header</div>'+
'<ng-transclude></ng-transclude>'+
'<div>the footer</div>',
scope: {
config: '=?'
}
.....
}
}
Filter
app.filter('trustedhtml', function($sce){
return function(val){
return $sce.trustedHtml(val);
}
})

angularjs: click on a tag to invoke a function and pass the current tag to it

I need to handle a click on a tag that enables the opening of a popover.
I try to figure out the best way to do this with angularjs and naturally used hg-click.
<div ng-repeat="photo in stage.photos"
ng-click="openPopoverImageViewer($(this))"
>
$scope.openPopoverImageViewer = function (source) {
alert("openPopoverImageViewer "+source);
}
The issue is that I cannot manage to pass the $(this) to it.
Q1) How to pass the jQuery element?
Q2) In addition, ng-click sounds
to require the function being part of the controller: is it possible
to invoke a function in the partial instead?
You need to stop "thinking in jQuery" :)
Like #oori says, you can pass in photo.
Or better yet, create a custom directive. Directives is the way to go when you need new functionality in your dom, like an element that you can click to open an overlay. For example:
app.directive('popOver', function() {
return {
restrict: 'AE',
transclude: true,
templateUrl: 'popOverTemplate.html',
link: function (scope) {
scope.openOverlay = function () {
alert("Open overlay image!");
}
}
};
});
You can then use this as a custom elemen <pop-over> or as an attribute on regular HTML elements. Here is a plunker to demonstrate:
http://plnkr.co/edit/P1evI7xSMGb1f7aunh3G?p=preview
Update: Just to explain transclusion: When you say that the directive should allow transclusion (transclude:true), you say that the contents of the tag should be sent on to the directive.
So, say you write <pop-over><span>This will be passed on</span></pop-over>, then the span with "This will be passed on" is sent to the directive, and inserted wherever you put your ng-transclude element in your template. So if your pop-over template looks something like this:
<div>
<ng-transclude/>
</div>
Then your resulting DOM after the template has compiled will look like this:
<div>
<span>This will be passed on</span>
</div>
Pass it "photo"
<div ng-repeat="photo in stage.photos" ng-click="openPopoverImageViewer(photo)">
or the current $index
<div ng-repeat="photo in stage.photos" ng-click="openPopoverImageViewer($index)">

angularJS directive with isolated scope, attribute binding doesn't work

Please see this jsfiddle: http://jsfiddle.net/viro/DK5pC/3/
What I did looks right compared to the tutorials and replies I've found, so I'm sure I'm overlooking something trivial.
I'm trying to do a directive on a html element, that will create a sibling div to display a message associated with the original element.
for example, for this html :
<input ng-model="inp" tst-msg="message" />
I would create as a sibling element:
<div class="msg">Msg:<span ng-bind="tstMsg"></span></div>
I was hoping that tstMsg in the div's scope would be bound to message in the input's scope.
Here's what the directive looks like :
angular.module('tst', [])
.directive('tstMsg', function(){
var template = "<div class='msg' >Msg:<span ng-bind='tstMsg'></span></div>";
var link = function(scope,element,attrs) {
element.parent().append(template);
console.log("link called");
};
return {
restrict: 'A',
scope: {
tstMsg: '='
},
link: link
};
});
Well that doesn't work and I can't figure out why.
You need to $compile the template you're adding to the DOM. Angular hasn't had a chance to add it's handlers, for instance the ng-bind directive to that part of the dom.
So instead of just adding the element like this:
element.parent().append(template);
These steps will let Angular process your template and then add it.
newe = angular.element(template);
$compile(newe)(scope);
element.parent().append(newe);
Updated fiddle

Replace the html of an element with the content of an external template in a directive?

I'm trying to create a directive which is a sidebar in my shell page that will change accordingly whenever a new route is hit, and will populate itself with the sub menu items relevant to that parent route. I have 4 different menus which are external templates and i want the contents of those html files to replace the menu, the link function of my directive looks like this so far:
link: function(scope, element, attrs, ngModel) {
scope.$on("$routeChangeSuccess", function (event, current, previous) {
element.html('<div ng-include=\'enterprisesMenu.html\'></div>');
});
};
But the element is not updating, however when i use inline templates the elements updates according, but because each template is complex i prefer not to have that html inside my directive, I've also tried element.html('<div ng-include src=\'enterprisesMenu.html\'></div>');
Any ideas?
Try $compile:
element.html($compile('<div ng-include=\'enterprisesMenu.html\'></div>')
(scope));
You could achieve this result by dynamically ng-including the desired template. For instance:
HTML:
<div class="your-sidebar" ng-controller="SidebarCtrl">
<div ng-include="sidebar.url" ></div>
</div>
Controller:
app.controller("SidebarCtrl", function($scope) {
$scope.sidebar = {
url: "initial-url"
};
$scope.$on("$routeChangeSuccess", function(event, current, previous) {
// decide potentially new value for $scope.sidebar.url
$scope.sidebar.url = newValueCalculatedAbove;
});
});
This solution does not require a directive, only an extra controller. It can be done with directive too, the HTML above is the template of the directive and the JS code the controller (no link function required).

Resources