Multiple use of a template in the same page in AngularJS - angularjs

I have some repetitive components on my AngularJS page such as billingAddress, shippingAddress and primaryAddress. I have created a separate template file for address components and expected to be able to use ng-include to include the template thrice on my page. I am unable to find documentation around passing models to templates. What I am looking for is something like
<div ng-include="address.tpl.html" ng-model="{address: primaryAddress}"></div>
<div ng-include="address.tpl.html" ng-model="{address: billingAddress}"></div>
<div ng-include="address.tpl.html" ng-model="{address: shippingAddress}"></div>
Is this even possible as of now?

his is for what directives are made.
angular.module('docsSimpleDirective', [])
.directive('myAddress', function() {
return {
scope: {
address : '='
},
templateUrl: 'address.tpl.html'
};
});
Then in your template simple use the $scope.address.
On declaring the directive you should use it like this.
<my-address address="primaryAddress"><my-address>
<my-address address="billingAddress"><my-address>
<my-address address="shippingAddress"><my-address>

Related

Angular directive from array element in ngRepeat

I am working on a project that will have any number of HTML cards, and the format and positioning of the cards is the same, but the content of them will differ.
My plan was to implement the content of each card as an angular directive, and then using ngRepeat, loop through my array of directives which can be in any order, displaying each directive in a card. It would be something like this:
inside my controller:
$scope.cards = [
'directice-one',
'directive-two',
//etc..
]
my directive:
.directive('directiveOne', function () {
return {
restrict: "C",
template: '<h1>One!!</h1>'
};
})
.directive('directiveTwo', function () {
return {
restrict: "C",
template: '<h1>Two!!</h1>'
};
})
//etc..
my html:
<div class="card" ng-repeat="item in cards">
<div class="{{item}}"></div>
</div>
But.. the directives don't render. So I just get a div with class="directive-one".
This might be a design flaw on my part, some sort of syntax error, or just a limitation of angular. I'm not sure.
I've also considered making a directive <card>, and then passing the templateUrl: into it, but that would cause me to lose my access to $scope and the javsacript capabilities that I would have if each card was it's own directive.
So, advise, code help, anything would be very helpful!
I choose directives only when I need to use them in HTML mark up. For example, assuming cards layout is same and it takes different information based on user preference.
HTML File
<my-card Name="First" Option="Myoptions"></Card>
<my-card Name="Second" Option="GenOptions"></Card>
Directive
angular.module("testapp").directive("MyCard", function() {
scope: {
name: '#',
Option: '#'
Controller: "myCardController",
templateURL: "~/myCard/myCardTemplate.html"
}
});
In Template you can implement the information passed from HTML page via the directive.
Hope this helps.
Do take note that the above approach is preferred when you are developing a framework sort of things. For example you develop a web framework and the header takes 5 parameters and these 5 parameters needs to be passed via mark up. Most important thing is that the framework/header is independent
In your controller, you need to require the directive modules. Then assign them to a scope variable which would be that array you have. Will update with code when I get to desktop, tried doing with phone kinda tuff.

Angular 1.5: dynamically load a component

I am trying to create some sort of generic gridview using AngularJS 1.5 and its components. A (pseudocode) version of what I've got going right now:
// inside <my-grid-component data="data" metadata="metadata">
<div ng-repeat="item in $ctrl.data">
<my-row-component item="item" metadata="$ctrl.metadata"></my-row-component>
</div
// inside <my-row-component item="item" metadata="metadata">
<div ng-repeat="column in $ctrl.metadata.columns">
<my-cell-component value="$ctrl.item[column]"></my-cell-component>
</div>
Now <my-cell-component>could have some basic ng-switchstatement that handles the obvious cases, like if the value is text or an image or something, but since this will be used by many people and on many projects, it is possible that someone will want to do something fancy and/or highly specific inside a cell. They could just ammend <my-cell-component> with more ng-switches, but then they are messing with base framework code which hurts maintainability.
So, ideally, I'd want to make something where a developer can optionally provide his own custom template for a specific field in the metadata, e.g. metadata.columns[3].customCellComponentName = 'some-custom-template';
Then <my-row-component> would look something like this:
<div ng-repeat="column in $ctrl.metadata.columns">
<div ng-if="!column.isCustomCellComponent">
<my-cell-component value="$ctrl.item[column]"></my-cell-component>
</div>
<div ng-if="column.isCustomCellComponent">
??? --> <column.customCellComponentName value="$ctrl.item[column]"></column.customCellComponentName>
</div>
</div>
The project automatically puts all templates in $templateCache, so resolving the template should not be a problem, but other than that, the marked line with the "???" obviously does not work. It demonstrates what I would like to achieve, but I have no idea how to actually do something like this. I looked into transclusion, ng-include and other solutions, but none seem to provide the option to dynamically load a template AND model-bind some data to it.
Any and all ideas very much welcome. I would like to stay as far away from overly complex directives as possible. While they allow you to do many things, in my experience they are also a debugging and maintainability nightmare.
Thanks.
You can create a directive for your <my-cell-component> such a way that it either accepts a templateUrl or it determines the templateUrl and uses it. Let me give you an example for the latter,
angular.module('myApp')
.directive('myCellComponent', ['CELL_TYPE', function (CELL_TYPE) {
return {
restrict: 'E',
scope:{
cellType: '='
},
template: '<div ng-include="templateUrl"></div>',
link: function (scope) {
function setTemplate(cellType) {
scope.templateUrl = CELL_TYPE[cellType.value].templateUrl;
// or figure out some other way to determine templateUrl
}
scope.$watch(function () {
return scope.cellType;
}, function (newVal) {
if(newVal) {
setTemplate(scope.cellType);
}
});
}
};
}]);
So, we've got the directive's template having an ng-include that uses the templateUrl determined on the basis of some constants, say CELL_TYPE.
Now you've got a directive that dynamically loads its template based on your attributes!
You can (obviously) get rid of $watch if dynamically changing the templateUrl isn't applicable to your use-case.

Angular directive with dynamic controller

I'd like to create a panel directive with a dynamic controller and template so that I can display context-sensitive options in my application. For example, clicking edit on a "Font" setting would show the "Font Options".
I can't find much documentation on it but it looks like it's now possible to specify the controller name on a directive using the name attribute:
app.directive('dynamicPanel', function() {
return {
restrict: 'A',
scope: {
config: '=dynamicPanel'
},
controller: '#',
name: 'ctrlName'
};
});
What I was hoping is that given a panel configuration like this:
vm.panel = {
controller: 'FontCtrl',
template: 'font.template.html'
};
I could then load the panel like so:
<div class="options-panel"
ng-if="vm.panel"
dynamic-panel="vm.panel"
ctrl-name="{{ vm.panel.controller }}"
ng-include="vm.panel.template">
</div>
Unfortunately this does not work. I get the error Badly formed controller string. Also it looks like only the text binding works for the name attribute (#).
Is there a way to bind the name attribute to a dynamic value or an alternative to dynamic controllers in directives.
I figured it out in the end by embracing the component pattern. By registering my options panels as individual directives with their own controllers it simply became a question of rendering the directive markup e.g. <font-options/> and then compiling the "dynamic panel".
http://plnkr.co/edit/Ickkz1GGbDdSbUOUcvfj?p=preview

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);
}
})

return a template when a user clicks on an event in Angular

I'm trying to grok the syntax for angular (just started yesterday) and the best way to achieve the following. What I'd like to do is load some compiled html and inject it into the DOM. This is a follow-up to this question: first time writing an Angular directives and returning a compiled template . Rather than call
<div my-customer></div>
I'd like to load the template dynamically based upon user interaction. Say something like:
<div ng-click="loadMyCustomer()">load customer</div><!-- should load here -->
....
arcModule.controller('MainCtrl', function($scope, $http){
$scope.sayHello(){
alert("hello here");
}
$scope.loadMyCustomer=function(){
//alert("hello");
// return the template in the mycustomer directive and place it after the current DOM element from which it is called
}
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
});
arcModule.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}}<button ng-click="sayHello()">Click to say hello.</button> Address: {{customer.address}}'
};
});
Sorry if this sounds simple but I find Angular docs a bit opaque and, if I can figure out how to return this, I'll be very happy. So, how would I return the template in the directive via the loadMyCustomer method?
For what you have highlighted it can be done without creating new directive and using the current ng-include directive.
Even if you have to do it this way
<div ng-click="showCustomer=true">...
<div my-customer ng-if="showCustomer"></div>
The advantage of ngif is that the template would not be loaded till the showCustomer is true.

Resources