How to use $compile inside an Angular directive? - angularjs

I have a collection of 500 items (persons) that I am rendering in an Angular ng-repeat directive. Each item has three fields (FirstName, LastName, Company). I want the user to be able to see details / edit the items below each rendered row. I have a button (Font Awesome square-plus) that when clicked needs to show the item details / edit. I do not want to include this markup/logic within the controller because having it rendered but hidden is very slow...multiple seconds in Chrome. I assume this is due to all the watches.
To resolve the issue, I created a directive that injects the details / edit items under the current record at run-time. I attempt to $compile the markup to link it to the current scope of the ng-repeat row.
Problems.. I think I am having issues with the scope. The directive is references within the ng-repeat block (p in Persons), so I would think I would be passed the record scope in the directive link function. But I can only get the record object by getting the parent scope (scope.$parent.p instead of scope.p). I don't understand.
Also, once the $compile function is executed, I do see the person info displayed in the new details blocks. But changes are not reflected in the current record data, nor an I dismiss the details block.
Any suggestions?
Markup:
<div class="row" ng-repeat="p in Persons">
<div class="col-lg-1">
<i class="fa fa-plus-square" ng-show="!p.showDetail" manage-details></i>
</div>
<div class="col-lg-2">{{::p.FirstName}}</div>
<div class="col-lg-2">{{::p.LastName}}</div>
<div class="col-lg-2">{{::p.Company}}</div>
<div id="marker{{$index}}"></div>
<hr ng-if="!$last" />
</div>
JS:
(function () {
'use strict';
angular
.module('ngRepeatMystery', [])
.controller('TestController', TestController)
.directive('manageDetails', manageDetails);
TestController.$inject = ['$scope'];
function TestController($scope) {
$scope.Persons = [
{ 'FirstName': 'Joe', 'LastName': 'Delbridge', 'Company': 'Dow', 'showDetail': false },
{ 'FirstName': 'Tony', 'LastName': 'Ingram', 'Company': 'Samtec', 'showDetail': false },
{ 'FirstName': 'Mike', 'LastName': 'Smolinski', 'Company': 'HCHB', 'showDetail': false },
{ 'FirstName': 'Lee', 'LastName': 'Shipman', 'Company': 'Cummins', 'showDetail': false },
{ 'FirstName': 'Eric', 'LastName': 'ONeal', 'Company': 'MSD', 'showDetail': false },
];
$scope.DismissDetails = function (index) {
$scope.Persons[index].showDetail = false;
var wrappedMonkey = angular.element($document[0].getElementById('details' + index));
angular.element(wrappedMonkey).hide();
}
};
manageDetails.$inject = ['$compile', '$document', '$timeout'];
function manageDetails($compile, $document, $timeout) {
return {
restrict: 'A',
scope: {},
link: function (scope, element, attrs) {
element.bind('click', function () {
// scope.$parent !!? WAT!
scope.$parent.p.showDetail = !scope.$parent.p.showDetail;
if (scope.$parent.p.showDetail) {
var index = scope.$parent.$index;
var wrappedMarker = angular.element($document[0].getElementById('marker' + index));
var details = getDetailsTemplate(index);
wrappedMarker.replaceWith(details);
var wrappedDetails = angular.element($document[0].getElementById('details' + index));
$compile(wrappedDetails.contents())(scope.$parent);
};
});
}
};
function getDetailsTemplate(index) {
var detailsTemplate =
"<div id=\"details" + index + "\" style=\"padding: 20px;\">" +
"<div class=\"row\">" +
"<div class=\"col-lg-2\"></div>" +
"<div class=\"col-lg-8\">" +
"<label>Last Name</label>" +
"<input ng-model=\"p.LastName\" placeholder=\"Last Name\"><br/>" +
"<label>First Name</label>" +
"<input ng-model=\"p.FirstName\" placeholder=\"First Name\"><br/>" +
"<label>Company</label>" +
"<input ng-model=\"p.Company\" placeholder=\"Company\"><br/>" +
"<button class=\"btn btn-primary\" ng-click=\"p.DismissDetails($index);\">Done</button><br/>" +
"</div>" +
"<div class=\"col-lg-2\"></div>" +
"</div>" +
"</div>";
return detailsTemplate;
}
};
})()
Plunker: http://plnkr.co/edit/64TcuhaNi2JcC1hzon15
I am also open to other alternatives...

Ok, I think there are a lot of issues with your code.
I would advise against having the directive modify something outside of itself.
As I commented earlier, don't use $parent. Just pass data as attributes. And create a new scope when you call $compile to avoid polluting the existing scope.
I modified your code so it works, but it's still not pretty:
http://plnkr.co/edit/eLNxewwFzobqTkQ4867n
HTML:
<div class="row" ng-repeat="p in Persons">
<div class="col-lg-1">
<i class="fa fa-plus-square" ng-show="!showDetail" manage-details monkey="p" index="$index" show-detail="showDetail"></i>
</div>
<div class="col-lg-2">{{p.FirstName}}</div>
<div class="col-lg-2">{{p.LastName}}</div>
<div class="col-lg-2">{{p.Company}}</div>
<div id="marker{{$index}}"></div>
<hr ng-if="!$last" />
</div>
JS:
return {
restrict: 'A',
scope: {
monkey: '=',
index: '=',
showDetail: '='
},
link: function (scope, element, attrs) {
var childScope;
element.bind('click', function () {
scope.showDetail = !scope.showDetail;
if (scope.showDetail) {
childScope && childScope.$destroy();
childScope = scope.$new();
var index = scope.index;
var wrappedMarker = angular.element($document[0].getElementById('marker' + index));
wrappedMarker.html(getDetailsTemplate(index));
childScope.p = angular.copy(scope.monkey);
childScope.dismissDetails = function () {
scope.showDetail = false;
scope.monkey = angular.copy(childScope.p);
wrappedMarker.html('');
};
$compile(wrappedMarker.contents())(childScope);
};
});
}
};
function getDetailsTemplate(index) {
var detailsTemplate =
"<div id=\"details" + index + "\" style=\"padding: 20px;\">" +
"<div class=\"row\">" +
"<div class=\"col-lg-2\"></div>" +
"<div class=\"col-lg-8\">" +
"<label>Last Name</label>" +
"<input ng-model=\"p.LastName\" placeholder=\"Last Name\"><br/>" +
"<label>First Name</label>" +
"<input ng-model=\"p.FirstName\" placeholder=\"First Name\"><br/>" +
"<label>Company</label>" +
"<input ng-model=\"p.Company\" placeholder=\"Company\"><br/>" +
"<button class=\"btn btn-primary\" ng-click=\"dismissDetails();\">Done</button><br/>" +
"</div>" +
"<div class=\"col-lg-2\"></div>" +
"</div>" +
"</div>";
return detailsTemplate;
}

Related

AngularJs Custom Directive scope data overwritten

I have displayed the products based on branch and billing account. In the product template, i have a "+" button, if we click on the button, then i'm displaying the particular product id below that product template.
Now the problem is, when i click the "+" button of "Product 1" , then it display product id as "300152". its fine. After that If i click the "+" button next to "Product 2", its displaying product id as "300153" below both "Product 1" and "Product 2". This is the issue. Please check the following fiddle. Any help would be greatly appreciated.
JS Fiddle
TabsApp.directive('productTemplate', ['$compile',
function($compile){
return {
restrict: "E",
scope: {
branchdata : '='
},
//templateUrl : templateSupportTabV3,
template: ' <li ng-repeat= "(prod_index, product) in branchdata.moduleLevel3List "><span class="normal-negrita">{{product.name}} (ID.{{product.id}})</span><a class = "cursor" ng-click="load_productInfo_branch( branch_index + 1 , prod_index + 1 , product.id , branchdata.id );"><span id="more_product_body_{{branchdata.id}}_{{ product.id }}" class="normal" style="font-size:10px;"> + </span> </a><div id="product_body_{{branchdata.id}}_{{product.id}}" class="product_panel_container"></div></li> ',
link: function (scope, elem, attrs) {
scope.load_productInfo_branch = function(baIndex, productIndex, productId,branchId){
debugger;
scope.prdouctType = productId;
var resp = "<p >ID : {{prdouctType}} </P>";
var divId = document.getElementById("product_body_" + branchId+"_"+productId);
divId.innerHTML=resp;
$compile(divId)(scope);
};
}
};
}]);
You are using two-way binding while adding new DOM child; and there is one "prdouctType" in the scope. So,
var resp = "<p >ID : {{prdouctType}} </P>";
should be something like
var resp = "<p >ID : " + scope.prdouctType + "</P>";
Here is the working JS Fiddle: http://jsfiddle.net/fokv7Lhh/38/
You can use one way binding
var resp = "<p >ID : {{::prdouctType}} </p>";
Do you really need to apply to the scope? Another way to show the value is something like this:
var resp = "<p >ID : " + productId + "</p>";
Add in bindToController:
bindToController: true,
scope: {
branchData: '='
}
That should stop it happening.
Hello If you want to keep the {{prdouctType}} the same. You can try something like this.
You can hide the previous div and open the new one.
var TabsApp = angular.module('TabsApp', []);
TabsApp.controller('IndexCtrl', function ($scope) {
$scope.tabdata =[{"id":49844,"name":"Billing account 1","entityType":"BA","moduleLevel2List":[{"id":2239,"name":"branch 1","entityType":"BRANCH","moduleLevel3List":[{"id":300152,"name":"PRoduct 1","entityType":"PRODUCT"},{"id":300153,"name":"PRoduct 2","entityType":"PRODUCT"},{"id":300154,"name":"PRoduct 3","entityType":"PRODUCT"}]}]},{"id":49845,"name":"Billing account 2","entityType":"BA","moduleLevel2List":[{"id":2240,"name":"branch 2","entityType":"BRANCH","moduleLevel3List":[{"id":300127,"name":"PRoduct 4","entityType":"PRODUCT"}]}]}];
});
TabsApp.directive('supportTabV3Directive', ['$compile',
function($compile){
return {
restrict: "E",
scope: {
tabdata : '='
},
//templateUrl : templateSupportTabV3,
template: '<li name="billing_{{ ba_index + 1}}" ng-repeat = "(ba_index, ba) in tabdata "><span class="bold">{{ba.name}} (ID. {{ba.id}})</span><ul><li ng-repeat = "(branch_index, branch) in ba.moduleLevel2List "><span class="normal">Nombre: {{branch.name}}</span><ul><product-template branchdata="branch" ></product-template></ul></li>',
link: function (scope, elem, attrs) {
}
};
}]);
TabsApp.directive('productTemplate', ['$compile',
function($compile){
return {
restrict: "E",
scope: {
branchdata : '='
},
//templateUrl : templateSupportTabV3,
template: ' <li ng-repeat= "(prod_index, product) in branchdata.moduleLevel3List "><span class="normal-negrita">{{product.name}} (ID.{{product.id}})</span><a class = "cursor" ng-click="load_productInfo_branch( branch_index + 1 , prod_index + 1 , product.id , branchdata.id );"><span>{{productType}} </span> <span id="more_product_body_{{branchdata.id}}_{{ product.id }}" class="normal" style="font-size:10px;"> + </span> </a><div id="product_body_{{branchdata.id}}_{{product.id}}" class="product_panel_container"></div></li>',
link: function (scope, elem, attrs) {
scope.load_productInfo_branch = function(baIndex, productIndex, productId,branchId){
scope.prdouctType = productId;
if(document.querySelector('#test'))
{
document.querySelector('#test').remove()
}
var resp = "<p id='test'>ID : {{prdouctType}} </P>";
var divId = document.getElementById("product_body_" + branchId+"_"+productId);
console.log(divId);
divId.innerHTML=resp;
$compile(divId)(scope);
};
}
};
}]);
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js
"></script>
<body>
<div ng-app = "TabsApp">
<div ng-controller="IndexCtrl">
<support-tab-v3-directive tabdata="tabdata"></support-tab-v3-directive>
</div>
</div>
</body>
</html>
You can run the above snippet
(OR)
HEre is a DEMO for it

isolate scope communication from directive to directive?

I am new to AngularJS and got confused with directive concept.
I am updating scope.markers in my second directive via $scope.delete function but changes are not reflecting on first directives,as I am using two way data binding isolate scope, so it should reflect. any solution will be a great help.
I have my first directive as:
app.directive('damageDiagram', function () {
return {
retrict: 'AE',
scope: {
imgsrc: '=', markers: '=', pointsrc: '=', dtype:'='
},
templateUrl: 'app/components/inspections/damage-diagram.html',
link: function (scope, element, attrs) {
}
}
});
and second directive as:
app.directive('damageMarker', function ($mdDialog,inspectionService,$timeout) {
return {
restrict: 'AE',
require: '?^damageDiagram',
scope: {
marker: '=',
pointsrc: '=',
dtype:'=',
markers: '='
},
template: '<img ng-src="{{pointsrc}}" />',
link: function (scope, elem, attr) {
elem.on("click",function(e){
showDialog();
function showDialog($event) {
var parentEl = angular.element(document.body);
$mdDialog.show ({
parent: parentEl,
targetEvent: $event,
template:
'<form name="clientForm" novalidate >'+
'<md-dialog aria-label="List dialog">' +
' <md-dialog-content>'+
'<md-input-container>'+
'<md-select ng-model="dtypeValue" class="dialog-close" placeholder="Select dtype">'+
'<md-option ng-repeat="opt in dtype">{{opt}}</md-option>'+
'</md-select>'+
'</md-input-container>'+
'<md-input-container class="md-block">'+
'<label>Comment</label>'+
'<input required name="name" ng-model="comment" class="dialog-close">'+
'<div ng-messages="clientForm.name.$error">'+
'<div ng-message="required">This is required.</div>'+
'</div>'+
'</md-input-container>'+
' </md-dialog-content>' +
' <div class="md-actions" layout="row" layout-align="end center">' +
' <md-button ng-click="closeDialog()" class="md-primary">' +
' Close' +
' </md-button>' +
'<md-button ng-disabled="clientForm.$invalid" ng-click = "save()" class="md-primary">'+
'Save'+
'</md-button>'+
'<md-button ng-disabled="clientForm.$invalid" ng-click = "delete()" class="md-primary">'+
'Delete'+
'</md-button>'+
' </div>' +
'</md-dialog>'+
'</form>',
controller: DialogController
});
function DialogController($scope, $mdDialog) {
$scope.dtypeValue = scope.dtype[scope.marker.dtype.toUpperCase()];
$scope.dtype = scope.dtype;
$scope.comment = scope.marker.comment;
$scope.marker = scope.marker;
$scope.closeDialog = function() {
$mdDialog.hide();
}
$scope.save = function(){
console.log($scope.marker.id);
console.log($scope.dtypeValue);
console.log($scope.comment);
var dataSend = {};
dataSend.id = $scope.marker.id;
dataSend.comment = $scope.comment;
for(var key in $scope.dtype) {
if($scope.dtype[key] == $scope.dtypeValue) {
dataSend.dtype = key;
}
}
inspectionService.updateDiagram(dataSend).then(function(response){
console.debug("response ; "+response);
$mdDialog.hide();
scope.marker.id = response.id;
scope.marker.comment = response.comment;
scope.marker.dtype = response.dtype;
});
}
$scope.delete = function(){
var dataSend = {};
dataSend.id = $scope.marker.id;
var param = {};
param.inspection=$scope.marker.inspection;
inspectionService.deleteDiagramMarker(dataSend).then(function(response){
inspectionService.getDiagram(param).then(function(response){
$timeout(function() {
scope.$apply(function(){
scope.markers = response.results;
})
},2000);
console.debug("response ; "+response);
$mdDialog.hide();
});
});
}
}
}
});
console.log(scope.marker.top, scope.marker.left, elem);
}
}
});
My html code for damage-diagram directive is as follows:
<damage-diagram imgsrc="imgsrc" pointsrc="pointsrc" dtype="dtype"
markers="inspection.damage_diagram">
</damage-diagram>
and my html code for damage-directive is as follow:
<div style="position:relative">
<img id="23467129" ng-src="{{imgsrc}}" style="position:relative" width="100%" />
<div ng-repeat="marker in markers"
marker="marker"
markers="markers"
dtype="dtype"
pointsrc="pointsrc"
damage-marker>
</div>
</div>
controller is as follows:
app.controller('InspectionDetailCtrl', ['$scope', 'inspectionService', '$stateParams', 'Restangular','$rootScope',
function ($scope, inspectionService, $stateParams, Restangular, $rootScope) {
$scope.updateDamageImage = {};
$scope.insp_id = $stateParams.inspId;
$scope.damageImagesShow = false;
$scope.comments = [];
$scope.types = [];
$scope.selectTypeDelete = false;
$scope.commentDelete = false;
$scope.selectTypeDeleteBefore = true;
$scope.commentDeleteBefore = true;
init($scope.insp_id);
console.log("Fetching details for scope", $scope.insp_id);
function init(insp_id)
{
inspectionService.inspections.customGET(insp_id, {type: 'full'})
.then(function (data) {
$scope.inspection = data;
}, function (err) {
$scope.inspection = null;
});
}
$scope.pointsrc="app/components/inspections/pointer.png";
$scope.dtype = {
'S': 'Scratch (minor)',
'DS': 'Deep Scratch',
'D': 'Dents',
'WD': 'Wheel Damage',
'CW': 'Cracked Window',
'FT': 'Flat Tire',
'BL': 'Broken (lights)'
};
}]);
First of all two way binding in directive doesn't works like that any change reflected in main controller can be seen in directive but not other way any change in directive won't be reflected in main controller.
But there is a solution you can create an object in main controller
var x={};
x.value='to be passed in directive'
then you use same variable in directive since only once instance of object is created so any change in any directive will be reflected everywhere.

Modifying elements with same directive

I have several elements in a container. One of the rows has two icons in it: zoom in and zoom out. When you click Zoom In, I'd like all the row's widths to grow.
<div id="events">
<year>year 1</year>
<year>year 2</year>
<year>year 3</year>
<year>year 4</year>
<div id="scaling">
<md-icon aria-label="Zoom In" class="material-icons" ng-click="zoomIn()">zoom_in</md-icon>
<md-icon aria-label="Zoom Out" class="material-icons" ng-click="zoomOut()">zoom_out</md-icon>
</div>
</div>
I have a year directive:
angular.module("app").directive("year", ['$rootScope', function ($rootScope) {
return {
link: function($scope, element, attr) {
var events = element;
$scope.zoomIn = function(ev) {
console.log('zoomin');
$scope.zoom = $scope.zoom + $scope.scale;
if($scope.zoom < 100) { $scope.zoom = 100; }
events.html($scope.zoom);
events.css({
'width': $scope.zoom + '%'
});
}
$scope.zoomOut = function(ev) {
$scope.zoom = $scope.zoom - $scope.scale;
if($scope.zoom < 100) { $scope.zoom = 100; }
events.css({
'width': $scope.zoom + '%'
});
}
}
}
}]);
However the width is only applied to the very last year element. Why is that?
You are overwriting the scope every time. So each instance of your year directive is clobbering the zoomIn and zoomOut methods each time it is instantiated.
Normally you could solve this by using a new or isolate scope in your directive definition object:
//new scope
{
scope: true
}
//isolate scope
{
scope: {}
}
However, since you want to bind click handlers outside your individual year directives you will have to do something else.
A better solution would be to pass in the attributes and simply respond to their changes:
return {
scope: {
zoom: '='
},
link: function(scope, elem, attrs){
scope.$watch('zoom', function(){
//Do something with 'scope.zoom'
});
}
};
Now your external zoomIn and zoomOut functions can just modify some zoom property on the parent scope, and you can bind your year components to that.
<year zoom="myZoomNumber"></year>
And just for posterity, here is a working snippet.
function EventsController() {
var $this = this;
var zoom = 1;
$this.zoom = zoom;
$this.zoomIn = function() {
zoom *= 1.1;
$this.zoom = zoom;
console.log({
name: 'zoomIn',
value: zoom
});
};
$this.zoomOut = function() {
zoom *= 0.9;
$this.zoom = zoom;
console.log({
name: 'zoomOut',
value: zoom
});
};
}
function YearDirective() {
return {
restrict: 'E',
template: '<h1 ng-transclude></h1>',
transclude: true,
scope: {
zoom: '='
},
link: function(scope, elem, attr) {
var target = elem.find('h1')[0];
scope.$watch('zoom', function() {
var scaleStr = "scale(" + scope.zoom + "," + scope.zoom + ")";
console.log({
elem: target,
transform: scaleStr
});
target.style.transform = scaleStr;
target.style.transformOrigin = 'left';
});
}
};
}
var mod = angular.module('my-app', []);
mod
.controller('eventsCtrl', EventsController)
.directive('year', YearDirective);
.scaling{
z-index:1000;
position:fixed;
top:10px;
left:10px;
}
.behind{
margin-top:50px;
z-index:-1;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="my-app" ng-controller="eventsCtrl as $ctrl">
<div class="scaling">
<button type="button" aria-label="Zoom In" ng-click="$ctrl.zoomIn()">zoom_in</button>
<button type="button" aria-label="Zoom Out" ng-click="$ctrl.zoomOut()">zoom_out</button>
</div>
<div class="behind">
<year zoom="$ctrl.zoom">year 1</year>
<year zoom="$ctrl.zoom">year 2</year>
<year zoom="$ctrl.zoom">year 3</year>
<year zoom="$ctrl.zoom">year 4</year>
</div>
</div>
The events.css is getting over-ridden, thus making it apply only to last element.
events.css({
'width': $scope.zoom + '%'
}).bind(this);
You have to bind it to current scope.

AngularJS Access Global Variable Under Directive

I have a global variable like following.
var Lightbox = {};
............
Lightbox.openModal = function (newImages, newIndex, modalParams) {
...............
}
I would like to access this variable under directives like the following way.
app.directive('productBuyers', ['Product', function(Product) {
return {
restrict : 'E',
scope : {},
template : '<div>' +
'<p class="product-buyers-f bold" ng-show="photos.length">Others:</p>' +
'<
'<div class="product-buyer square" ng-click="openLightboxModal($index)" ng-repeat="photo in photos | limitTo:3" ng-style="{\'background-image\':\'url(\' + photo.image + \')\'}"></div>' +
'<div class="clear"></div>' +
'</div>' +
'</div>',
link : function($scope, element, attrs) {
$scope.photos = [];
function getImages() {
}
$scope.openLightboxModal = function (index) {
Lightbox.openModal($scope.photos, index);
};
getImages();
}
}
}]);
I have tried by passing "$window" to the directive parameter and also using scope but it's not working. It's showing undefined "Lightbox".
Try window.Lightbox and also window.Lightbox.openModal = function(...)

How to pass a object into a directive

I have a items array which is used by ng-repeat to render the menu,
and on click of Add to cart button addItem() is called.
Currently i pass the name of the selected item as the name attribute in item-container directive.
How shall i pass an entire object through the attribute to the directive
HTML snippet
<p ng-repeat = "item in items">
<item-container
startcounter = 1
resetter = 'reset'
item = 'item'
name = {{item.name}} >
{{item.name}}
</item-container><br><br>
</p>
JS snippet
.directive('itemCounter',function(){
return {
controller: function() {return {}},
restrict:'E',
scope:{
item:"=",
resetter:"="
},
transclude:true,
link:function(scope,elem,attr){
scope.qty = attr.startcounter
scope.add = function(){
scope.qty++;
}
scope.remove = function(){
scope.qty--;
}
scope.addItem = function(){
console.log(attr.item);
scope.$parent.addMsg(scope.qty,attr.name)
console.log("value when submitted:" + scope.qty + "name:"+ attr.name);
scope.qty = attr.startcounter;
scope.$parent.resettrigger();
}
scope.$watch(function(attr){
return attr.resetter
},
function(newValue){
if(newValue === true){
scope.qty = attr.startcounter;
}
});
},
template:"<button ng-click='addItem();'>Add to cart</button>&nbsp&nbsp"+
"<button ng-click='remove();' >-</button>&nbsp"+
"{{qty}}&nbsp" +
"<button ng-click='add();'>+</button>&nbsp&nbsp"+
"<a ng-transclude> </a>"
}
Currently you actually aren't even passing in the name it seems. All the passing in magic happens in this part:
scope:{
resetter:"="
},
As you can see, there is no mention of name. What you need to do is add a field for item and just pass that in:
scope:{
resetter:"=",
item: "="
},
Then you can just do
<p ng-repeat = "item in items">
<item-container
startcounter = 1
resetter = 'reset'
item = item >
{{item.name}}
</item-container><br><br>
</p>
Also I'm fairly sure you dont want to be using transclude here. Look into templateUrl

Resources