Unable to compile ng-repeat from template in directive - javascript

I am trying to add form elements dynamically using JS and will need a directive. I am able to add form elements but when I have ng-options or ng-repeat it does not get compiled. I have an example directive I am using for demo.
http://plnkr.co/edit/JOzTWB6tuyilCJ8Rj37Q
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller("fCtrl",function($scope){
$scope.xx = ['x','c','y','z','a'];
});
app.directive('datanType', function ($compile) {
var testTemplate1 = '<h1 ng-repeat="x in xx">Test</h1>';
var testTemplate2 = '<h1>Test2</h1>';
var testTemplate3 = '<h1>Test3</h1>';
var getTemplate = function(contentType){
var template = '';
switch(contentType){
case 'test1':
template = testTemplate1;
break;
case 'test2':
template = testTemplate2;
break;
case 'test3':
template = testTemplate3;
break;
}
return template;
};
var linker = function(scope, element, attrs){
element.html(getTemplate(attrs.content));
$compile(element.contents())(scope);
};
return {
restrict: "E",
replace: true,
link: linker,
scope: {
content:'=',
con:'#'
}
};
});
</script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller="fCtrl">
<p>Result:</p>
<datan-type content="test1" con="{{xx}}"></datan-type>
</body>
</html>

Try this method, its working http://plnkr.co/edit/NTG0LBa1dIPWcGGupJgt?p=preview
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller("fCtrl",function($scope){
$scope.xx = ['x','c','y','z','a'];
});
app.directive('datanType', function ($compile) {
return {
restrict: 'E',
replace: true,
link: function (scope, ele, attrs) {
var testTemplate1 = '<h1 ng-repeat="x in arr">Test</h1>';
var testTemplate2 = '<h1>Test2</h1>';
var testTemplate3 = '<h1>Test3</h1>';
var template = '';
scope.arr = eval(attrs.con);
switch(attrs.content){
case 'test1':
template = testTemplate1;
break;
case 'test2':
template = testTemplate2;
break;
case 'test3':
template = testTemplate3;
break;
}
ele.html(template);
$compile(ele.contents())(scope);
}
};
});
</script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller="fCtrl">
<p>Result:</p>
<datan-type content="test1" con="{{xx}}"></datan-type>
</body>
</html>

This should work. Here are the following changes I did:
con is already binded to scope. no need to use attrs
var testTemplate1 = '<h1 ng-repeat="x in con">Test {{x}}</h1>';
Changed # to = to bind scope property to parent scope (# bounds as string, see this
scope: {
content: '=',
con: '='
}
Changed {{xx}} to xx
<datan-type content="test1" con="xx"></datan-type>
Working plunker http://plnkr.co/edit/P54mZhXWE7AnjfCWbQRb

Related

How to pass $scope variable from controller to directive using angularjs

I made a simple directive that print an object:
var app = angular.module("myApp", []);
app.controller('myCtrl', function($scope) {
$scope.users=[
{name:'davidi',age:'23'},
{name:'karen',age:'43'},
{name:'lior',age:'22'}
];
});
app.directive("appCustomer", function() {
var htmlcode="<p ng-repeat='user in customerInfo'>{{user.name}}</p>\
";
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
template : htmlcode
};
});
my html code:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<app-customer info="users"></app-customer>
</div>
</body>
</html>
<script src="angularapp.js"></script>
i want simply access the $scope.users in my directive, to print the users object without the ng-repeat,
so i made it like that:
var app = angular.module("myApp", []);
app.controller('myCtrl', function($scope) {
$scope.users=[
{name:'davidi',age:'23'},
{name:'karen',age:'43'},
{name:'lior',age:'22'}
];
});
app.directive("appCustomer", function() {
var htmlcode="";
var userslength=3;
for(var i=0;i<userslength;i++)
{
htmlcode=htmlcode+"<p>{{users["+i+"]['name']}}</p>";
}
return {
restrict: 'E',
template : htmlcode
};
});
thats works fine, but when i replace the userlength to $scope.users.length , the app fail.
so my question is how can i access a $scope from the controller in the directive?
JSFiddle
You need to specify the link function
app.directive("appCustomer", function() {
function link(scope, element, attrs) {
var htmlcode = "";
for (var i = 0; i < scope.users.length; i++) {
element.text(Put your text here);
// OR
element.html(Put your HTML here);
}
}
return {
restrict: 'E',
users: '=',
link: link
};
});
In the scope you will receive users array
The second is you can you element argument to put your data to template. Or create html template separately

AngularJS dynamic custom directive issue

My custom directive runs fine onload page load but when added using append it does not run properly. It does not show its content when at in runtime.
HTML:
<!doctype html>
<html lang="en" ng-app="module1">
<head>
<meta charset="UTF-8">
<title>Angular Demo</title>
<link rel="stylesheet" href="css/bootstrap.css">
<script src="js/angular.js"></script>
<script src="js/ui-bootstrap-tpls-0.13.0.js"></script>
<script src="js/app.js"></script>
</head>
<body>
<div id="divContainer" style="border-style: solid;border-color:red;" ng-controller = "Controller1 as cnt1" >
<button ng-click="clicked();">Click Me!!</button>
<wlaccordion></wlaccordion>
</div>
</body>
</html>
app.js:
var app = angular.module('module1',[]);
app.controller('Controller1', ['$scope', function($scope) {
$scope.authorData = authorInfo;
$scope.clicked = function () {
alert('Clicked');
//angular.element(document.getElementById('divContainer')).append('<wlaccordion1></wlaccordion1>' (scope));
angular.element(document.getElementById('divContainer')).append('<wlaccordion></wlaccordion>');
}
}]);//controller1
var authorInfo = [
{
'name': 'Ray',
'rate': '10',
'show': 'true'
},
{
'name': 'Mahesh',
'rate': '12',
'show': 'true'
}
]
app.directive("wlaccordion", function($compile) {
var template = '<div ng-controller = "Controller1 as cnt1">' +
'<div ng-repeat="aData in authorData" ng-init="tab = 1">' +
'<ul>' +
'<li>' +
'<h1 ng-show={{aData.show}} ng-class="{active: tab === 1}"> {{aData.name}} </h1>' +
'<h1 ng-show={{aData.show}} ng-class="{active: tab === 2}"> {{aData.rate | currency}} </h1>' +
'</li>' +
'</ul>' +
'</div>' +
'</div>';
return{
link: function(scope, element){
var content = $compile(template)(scope);
element.append(content);
}
}
});
I would like the directive to function same as onload.
-Thanks
Mahesh
AngularJS intended for separation of presentation logic and business one. So I think you should do this in Angular way, your current approach is more jQuery one.
I would suggest you to add accordions collection to controller:
app.controller('Controller1', ['$scope', function($scope) {
$scope.accordions = [0]; // Replace 0 with some actual data
$scope.clicked = function() {
$scope.accordions.push($scope.accordions.length); // Same here
};
// Your other code
}
And in HTML add ng-repeat:
<div id="divContainer" style="border-style: solid;border-color:red;" ng-controller = "Controller1 as cnt1" >
<button ng-click="clicked();">Click Me!!</button>
<wlaccordion ng-repeat="accordion in accordions"></wlaccordion>
</div>
Edit: Also don't forget to remove compilation from wlaccordion's link.
Edit #2: I suppose that authorInfo global var is used simply for example, however if it doesn't then consider usage of module.constant or module.value
app.controller('Controller1', ['$scope','$compile', function($scope, $compile) {
$scope.authorData = authorInfo;
$scope.clicked = function () {
alert('Clicked');
//angular.element(document.getElementById('divContainer')).append('<wlaccordion1></wlaccordion1>' (scope));
angular.element(document.getElementById('divContainer')).append($compile('<wlaccordion></wlaccordion>')($scope));
}
}]);//controller1

Add ng-if attribute to a element from within a directive

I have a AngularJS directive which takes an ID and makes a lookup on this ID to get col-width, hide-state and order for a given flexbox element.
What I d like to do is to add a ng-if=false attribute to the element if its hide-state is true. Is there any way, how I can add ng-if attribute from within a directive to a element?
My current code:
.directive("responsiveBehaviour", ['ResponsiveService', function(ResponsiveService){
var linkFunction = function(scope, element, attributes){
var id = attributes["responsiveBehaviour"];
var updateResponsiveProperties = function(){
element.attr("column-width", ResponsiveService.getWidth(id));
if(ResponsiveService.getOrder(id)){
element.attr("order", ResponsiveService.getOrder(id));
}
if(ResponsiveService.getHidden(id) == true){
element.attr("hidden", "");
} else {
element.removeAttr("hidden");
}
};
if(id) {
scope.$watch('device', function () {
updateResponsiveProperties();
});
}
};
If I add
element.attr("ng-if", false);
instead of
element.attr("hidden", "");
it just renders out the ng-if attribute to the element but there is no action happening, so the element is still rendered and visible although ng-if is false.
Do you have any idea on how to achieve what I am looking for?
Thanks in advance.
Greets
something like below:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive( 'test', function ( $compile ) {
return {
restrict: 'E',
scope: { text: '#' },
template: '<p ng-click="add()">Jenish</p>',
controller: function ( $scope, $element ) {
$scope.add = function () {
var el = $compile( "<test text='n'></test>" )( $scope );
$element.parent().append( el );
};
}
};
});
working plunk is here
Update
Here is simple example as you requested.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.add = function () {
alert('Jenish');
$scope.cond = false;
}
$scope.cond = true;
});
app.directive( 'test', function ( $compile ) {
return {
restrict: 'E',
template: '<p ng-click="add()">Click me to hide</p>',
link: function ( $scope, $element, attr ) {
var child = $element.children().attr('ng-if', 'cond')
console.log($element)
$compile(child)($scope);
}
};
});
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.13/angular.js" data-semver="1.3.13"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<test></test>
</body>
</html>
I hope this would help you.

A validation directive that reacts to another value works in 1.0.x but not in 1.2.x

In angular js, I want to create a validator that will cause the ng-model value to become invalid when another value is specified. Now I have something that works fine for angular js 1.1.4 (which I was using because I was using an old plunkr), but when I switch to 1.1.5, it stops working.
I am sure I am doing something wrong with the scope, but I am not sure what.
Here is my code (plunkr here: http://plnkr.co/edit/Ug9oM1LNqPpTsONhRTnG?p=preview)
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.doSomething = function () {
alert('Submitted!');
}
$scope.data = {};
$scope.data.value = new String('blah');
$scope.data.value.$$error = 'My Error';
$scope.data.toggleError = function() {
if ($scope.data.value.$$error) {
$scope.data.value.$$error = null;
}
else {
$scope.data.value.$$error = "SOME ERROR";
}
};
console.log($scope.data.value instanceof String);
});
app.directive('serverError', function (){
return {
require: 'ngModel',
scope:true,
link: function(scope, elem, attr, ngModel) {
scope.$watch('attr.errorValue', function() {
console.log("The error value is " + scope.errorValue);
ngModel.$setValidity('serverError', scope.errorValue == null);
});
}
};
});
Here is my HTML:-
<!DOCTYPE html>
<html ng-app="angularjs-starter">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<form name="myForm" ng-submit="doSomething()">
<input type="text" name="fruitName" ng-model="data.value" serverError errorValue="data.value.$$error" />
<div>{{ data.value.$$error }}</div>
<span class="invalid" ng-if="myForm.fruitName.$error.serverError">
{{data.value.$$error}}
</span>
<br/>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
<input type="button" ng-click="data.toggleError()" value="Toggle Error"/>
</form>
</body>
</html>
As soon as I change from 1.1.3 to 1.2.0, my directive stops working.
I worked it out.
http://plnkr.co/edit/Ug9oM1LNqPpTsONhRTnG?p=preview
The directive is simple enough:-
app.directive('serverError', function($parse) {
return {
// restrict to an attribute type.
restrict: 'A',
// element must have ng-model attribute.
require: 'ngModel',
// scope = the parent scope
// elem = the element the directive is on
// attr = a dictionary of attributes on the element
// ctrl = the controller for ngModel.
link: function(scope, elem, attr, ctrl) {
scope.$watch(attr.serverError, function(newValue, oldValue) {
if (newValue != null) {
ctrl.$setValidity('serverError', false);
}
else {
ctrl.$setValidity('serverError', true);
}
});
}
};
});

How can I use AngularJS controller-specific scope values in document head?

I am trying to use my scope values in my head title tag. This is external to the body element that has my controller. IS there a way to do this?
Here is my HTML:
<!DOCTYPE html>
<html ng-app="soCLean">
<head>
<title>{{locale}} {{type}} {{service}}</title>
</head>
<body ng-controller="soCleanLandingPage">
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script>
<script>
var soClean = angular.module('soClean', []);
soClean.controller('soCleanLandingPage', ['$scope', function ($scope) {
$scope.locale = 'vancouver';
$scope.type = 'residential';
$scope.service = 'pressure washing';
$scope.serviceSingle = 'pressure wash';
}]);
</script>
</body>
</html>
Thanks for your help.
Just move your ng-controller to the html element.
http://plnkr.co/edit/xImv48BvoW2Y9Ibb9JTq?p=preview
(You can see the values in head in the debugger)
<!DOCTYPE html>
<html ng-app="soClean" ng-controller="soCleanLandingPage">
<head>
<title>{{locale}} {{type}} {{service}}</title>
</head>
<body >
<div>
<p>{{locale}}</p>
<p>{{type}}</p>
<p>{{service}}</p>
<p>{{serviceSingle}}</p>
</div>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script>
<script>
var soClean = angular.module('soClean', []);
soClean.controller('soCleanLandingPage', ['$scope', function ($scope) {
$scope.locale = 'vancouver';
$scope.type = 'residential';
$scope.service = 'pressure washing';
$scope.serviceSingle = 'pressure wash';
}]);
</script>
</body>
</html>
You can use the $rootScope:
app.run(function($rootScope){
$rootScope.locale = "en";
$rootScope.type = "something";
$rootScope.service = "another";
});
You can also use a fancy directive:
app.directive('title', function(){
return {
restrict: 'E',
scope:{},
template: "{{locale}} {{type}} {{service}}",
link: function(scope){
scope.locale = "en";
scope.type = "something";
scope.service = "another";
}
}
});
Check this plunker: http://plnkr.co/edit/JRblEkKaOCLNC2O7r9s4?p=preview

Categories

Resources