ng-repeat wont work in directive template - angularjs

I am trying to use ng-repeat in directive template like this:
JS
var module = angular.module("myModule", []);
module.directive('direct', function ()
{
return {
restrict: 'E',
controller: "directController",
// This HTML will replace the direct directive.
replace: true,
transclude: true,
scope: {title: "#"},
template:
'<div>'
// print: this is a title, which is correct
+ '{{title}}'
+ '<br>'
// print: ["1","2","3"], which is correct
+ '{{list}}'
+ '<br>'
// doesn't print anything, why ?!
+ '<div ng-repeat="l in list">{{l}}</div>'
+ '</div>',
link: function (scope, element, attrs, controller)
{
// scope has list in it
console.log(scope);
}
}
});
module.controller('directController', ["$scope", function ($scope)
{
$scope.list = ["1", "2", "3"];
}]);
angular.bootstrap($('html'),['myModule']);
HTML
<direct title="this is a title"></direct>
result HTML
<div>
this is a title
<br>
["1","2","3"]
<br>
</div>
As shown above, ng-repeat doesn't print 'list',
while printing list or logging it, it works fine, why :( ??
Update:
The problem was using the angular bootstrap function to register the module, since I didn't use the ng-app="myModule" directive.
If I use the ng-app="myModule" directive or inject to the var module = angular.module("ng"); module it should do the trick.

Be sure to wrap the angular bootstrap in the document ready function.
$(document).ready(function() {
angular.bootstrap($('html'),['myModule']);
});
Checkout here: http://jsfiddle.net/CRWAv/

The Documention says that you have to do that on manual bootstrap:
angular.element(document).ready(function() {
angular.bootstrap(document, ['myModule']);
});

Related

Angular $watch not working on controller variable updated by directive

I am trying to place a watch on controller variable which gets updated from a directive using function mapping. variable is getting updated and logged in console but watch on it not working.
Code Snippet :
index.html
<body ng-app="myApp" ng-controller="myCtrl">
<div>
<test on-click="update()"></test>
</div>
app.js
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', function($scope){
$scope.test = {
value: false
};
$scope.update = function() {
$scope.test.value = !$scope.test.value;
console.log("Update: " + $scope.test.value);
};
$scope.$watch('test', function(newVal){
console.log("Watch: " + newVal.value);
}, true);
});
myApp.directive('test', function($compile){
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
onClick: '&'
},
template: '<div ng-transclude=""></div>',
link: function(scope, element, attrs) {
var $buttonElem = $('<button>Test</button>').appendTo(element);
$buttonElem.click(function(){
scope.onClick();
});
}
}
});
Plunker Link is : https://plnkr.co/edit/41WVLTNCE8GdoCdHHuFO?p=preview
The problem is that the directive is raising the event using code that is not apart of AngularJS instead of using an ng-click in its template. If you can't modify the directive, then wrap your event handler in $scope.$apply instead.
$scope.update = function() {
$scope.$apply(function(){
$scope.test.value = !$scope.test.value;
console.log("Update: " + $scope.test.value);
});
};

angular directives inside ng-bind-html is not evluated

This is a subsequent question to this post .
I have an ng-attr-title used in the html injected using ng-bind-html which is not working ie) the title is not formed in the DOM element hence on hovering the tooltip is not formed.here is my code
myApp.controller("MyCtrl",function($scope) {
$scope.tl="this is title";
$scope.level = "<span ng-attr-title='{{tl}}'><b>data</b></span>";
});
Problem is illustrated in the Jsfiddle
You have to use $compile service to achieve this.
JS:
var myApp = angular.module('myApp', ['ngSanitize']);
myApp.controller("MyCtrl", function($scope){
$scope.tl="this is title";
$scope.level = "<span ng-attr-title='{{tl}}'><b>data</b></span>";
});
myApp.directive('compileHtml', compileHtml);
function compileHtml($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.compileHtml);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}
HTML:
<div ng-controller="MyCtrl" id="tableForVxp" class="dataDisplay2">
<span compile-html="level" ></span>
</div>
This compileHtml directive will compile your HTML template against your $scope.
I found that the other answers have problems and that the following directive works, and can be installed with bower.
https://github.com/incuna/angular-bind-html-compile
ng-bind-html will inject html as string. It will not compile it.
check http://plnkr.co/edit/M80zp3o4FIODIXFWVAuM?p=preview
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.val = 'patel';
$scope.myHTML =
'I am an <code>HTML</code>string with ' +
'links! and other <em>stuff</em> {{val}}';
}]);
You need custom directive which compiles your html and injects it into your element.
Use following directive
module.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
This is a normal behaviour of ng-bind-html. Generally, the controller's code should not have HTML markup - move it to template and controls it's visibility with ng-show/ng-hide instead.
However, you still can do that if you want, just use $compile service. See example here: https://docs.angularjs.org/api/ng/service/$compile

Why isn't this field binding in my directive?

I have a directive with 2-way binding on the dataSourceModel scope variable, but for some reason, it is showing as undefined in the directive. Am I doing something wrong here?
Plunker: http://plnkr.co/edit/LxWMbY9qtDSBUPWNqWV7?p=preview
Code:
Html:
<div ng-controller='TestCtrl'>
<test-directive
selected-id='selectedId'
data-source-model='workOrderItems'> <!-- This does not work -->
</test-directive>
{{workOrderItems}} <!-- this works -->
</div>
Script:
var app = angular.module("testApp", []);
app.controller('TestCtrl', ['$scope', function ($scope) {
$scope.workOrderItems = 'abcd';
$scope.selectedId = '123';
}]);
app.directive('testDirective',function () {
return {
restrict: 'E',
scope: {
selectedId: '=',
dataSourceModel: '='
},
replace: true,
template: "<div></div>",
link: function (scope, element, attrs) {
console.log(scope.selectedId, scope.dataSourceModel);
}
}
});
data- is prefix for custom HTML5 attributes, so the data-source-model='workOrderItems' is translated to just sourceModel in your directive.
Try renaming your directive attribute to something that doesn't start with data (or reference it in HTML as data-data-source-model) and it should work.

How to use custom directives.

I have mentioned HTML code below
<div class="panel" ng-controller="myController">
<div myArr="arr" my-Dir="">
</div>
JavaScript is
app.controller('myController',['$scope',function($scope){
// here i have one array of objects arr is here
}])
and Directive is
app.directive("myDir", function() {
scope: {
myArr: "=",
},
templateUrl: "views/myHTML.html"
};
});
Here I am including myHTML.html file. How do I pass the array to this file?. I need this array in myHTML.html file. I want to display the array elements in the myHTML using the ng-repeat.
You are using dayArr in the html and then trying to isolate your scope on something called dayArray. These don't match up. You also need to pass the array into dayArr so that it can actually have a value in your scope
I've updated the fiddle to have a working example http://jsfiddle.net/zpWsz/2/
This is the relevant bits:
Here is where I pass in some dummy array.
<div my-dir="" day-arr="[1,2,3]" >
Here is the updated directive pieces:
scope: {
dayArr : "="
},
template: '<ul>' +
'<li ng-repeat="i in dayArr">{{ i }}</li>' +
'</ul>',
A templateUrl would work too, but how you had it in the fiddle was throwing js errors.
Hope this helped.
custom directive template
app.directive('myDir', function(){
return {
scope: {
myArr: "=",
},
restrict: 'EAC',
templateUrl: 'views/myHTML.html',
link: function(scope, element, attr) { }
};
});
And to use it
<div data-my-dir data-my-arr="arr">

How to create a directive with a dynamic template in AngularJS?

How can I create a directive with a dynamic template?
'use strict';
app.directive('ngFormField', function($compile) {
return {
transclude: true,
scope: {
label: '#'
},
template: '<label for="user_email">{{label}}</label>',
// append
replace: true,
// attribute restriction
restrict: 'E',
// linking method
link: function($scope, element, attrs) {
switch (attrs['type']) {
case "text":
// append input field to "template"
case "select":
// append select dropdown to "template"
}
}
}
});
<ng-form-field label="First Name" type="text"></ng-form-field>
This is what I have right now, and it is displaying the label correctly. However, I'm not sure on how to append additional HTML to the template. Or combining 2 templates into 1.
i've used the $templateCache to accomplish something similar. i put several ng-templates in a single html file, which i reference using the directive's templateUrl. that ensures the html is available to the template cache. then i can simply select by id to get the ng-template i want.
template.html:
<script type="text/ng-template" id=“foo”>
foo
</script>
<script type="text/ng-template" id=“bar”>
bar
</script>
directive:
myapp.directive(‘foobardirective’, ['$compile', '$templateCache', function ($compile, $templateCache) {
var getTemplate = function(data) {
// use data to determine which template to use
var templateid = 'foo';
var template = $templateCache.get(templateid);
return template;
}
return {
templateUrl: 'views/partials/template.html',
scope: {data: '='},
restrict: 'E',
link: function(scope, element) {
var template = getTemplate(scope.data);
element.html(template);
$compile(element.contents())(scope);
}
};
}]);
Had a similar need. $compile does the job. (Not completely sure if this is "THE" way to do it, still working my way through angular)
http://jsbin.com/ebuhuv/7/edit - my exploration test.
One thing to note (per my example), one of my requirements was that the template would change based on a type attribute once you clicked save, and the templates were very different. So though, you get the data binding, if need a new template in there, you will have to recompile.
You should move your switch into the template by using the 'ng-switch' directive:
module.directive('testForm', function() {
return {
restrict: 'E',
controllerAs: 'form',
controller: function ($scope) {
console.log("Form controller initialization");
var self = this;
this.fields = {};
this.addField = function(field) {
console.log("New field: ", field);
self.fields[field.name] = field;
};
}
}
});
module.directive('formField', function () {
return {
require: "^testForm",
template:
'<div ng-switch="field.fieldType">' +
' <span>{{title}}:</span>' +
' <input' +
' ng-switch-when="text"' +
' name="{{field.name}}"' +
' type="text"' +
' ng-model="field.value"' +
' />' +
' <select' +
' ng-switch-when="select"' +
' name="{{field.name}}"' +
' ng-model="field.value"' +
' ng-options="option for option in options">' +
' <option value=""></option>' +
' </select>' +
'</div>',
restrict: 'E',
replace: true,
scope: {
fieldType: "#",
title: "#",
name: "#",
value: "#",
options: "=",
},
link: function($scope, $element, $attrs, form) {
$scope.field = $scope;
form.addField($scope);
}
};
});
It can be use like this:
<test-form>
<div>
User '{{!form.fields.email.value}}' will be a {{!form.fields.role.value}}
</div>
<form-field title="Email" name="email" field-type="text" value="me#example.com"></form-field>
<form-field title="Role" name="role" field-type="select" options="['Cook', 'Eater']"></form-field>
<form-field title="Sex" name="sex" field-type="select" options="['Awesome', 'So-so', 'awful']"></form-field>
</test-form>
One way is using a template function in your directive:
...
template: function(tElem, tAttrs){
return '<div ng-include="' + tAttrs.template + '" />';
}
...
If you want to use AngularJs Directive with dynamic template, you can use those answers,But here is more professional and legal syntax of it.You can use templateUrl not only with single value.You can use it as a function,which returns a value as url.That function has some arguments,which you can use.
http://www.w3docs.com/snippets/angularjs/dynamically-change-template-url-in-angularjs-directives.html
I managed to deal with this problem. Below is the link :
https://github.com/nakosung/ng-dynamic-template-example
with the specific file being:
https://github.com/nakosung/ng-dynamic-template-example/blob/master/src/main.coffee
dynamicTemplate directive hosts dynamic template which is passed within scope and hosted element acts like other native angular elements.
scope.template = '< div ng-controller="SomeUberCtrl">rocks< /div>'
I have been in the same situation, my complete solution has been posted here
Basically I load a template in the directive in this way
var tpl = '' +
<div ng-if="maxLength"
ng-include="\'length.tpl.html\'">
</div>' +
'<div ng-if="required"
ng-include="\'required.tpl.html\'">
</div>';
then according to the value of maxLength and required I can dynamically load one of the 2 templates, only one of them at a time is shown if necessary.
I heope it helps.

Resources