Insert directive into the DOM using jqlite - angularjs

I need to use jqlite to insert the HTML for the directive, but for some reason the directive does not insert the template.
<div ng-app="docsSimpleDirective">
<div ng-controller="Controller">
<button ng-click="showCustomer($event)">click to see the customer</button>
<div>
</div>
And my app.js looks like:
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function ($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.showCustomer = function($event) {
angular.element($event.currentTarget).next().html("<div my-customer></div>");
};
}])
.directive('myCustomer', function () {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
http://jsfiddle.net/4nad43gn/
NOTE: This is just to try and recreate the situation i'm in, but the directive has to be inserted to the DOM in a similar way to the above - otherwise it will not work for my situation.

As Michelem mention the best way to do DOM manipulation is using directive.
If you still want to do this by using controller you can take a look at my example: http://jsfiddle.net/4nad43gn/3/
$scope.showCustomer = function($event) {
var element = document.querySelectorAll('[ng-controller=Controller] div');
var tpl = $compile( "<div my-customer=''></div>" )( $scope );
element[0].appendChild(tpl[0]);
};
You need to add $compile in your application. It's possible?

Don't know why you did that but this is more simple:
JSFiddle
HTML:
<div ng-app="docsSimpleDirective">
<div ng-controller="Controller">
<button ng-click="showCustomer = true">click to see the customer</button>
<div my-customer ng-show="showCustomer"></div>
</div>
</div>
JS:
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function ($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.showCustomer = false;
}])
.directive('myCustomer', function () {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
PS
you can also use ng-ifinstead ng-show if you don't want to have the element (instead only hidden) before the click.

Related

angularjs click event inside the ngBindHtml directive

I have an angularjs sample code snippet here where i can bind the html tags using ng-bind-html directive. But how can I include some other tags like angularjs ng-click, id tag etc inside ngBindHtml directive like
Test
My sample code is here:
var app = angular.module("myApp", ['ngSanitize']);
app.controller("myCtrl", function($scope) {
$scope.myText = "<a href='#' ng-click='someFunction()'>Test</a>";
$scope.someFunction = function(){
alert("Link Clicked");
};
});
FYI, the data is loaded dynamically from server side script and i have to use ng-bind-html inside ng-repeat directive and i have to pass respective id's to click events something like ng-click="myFunction(x.id)" as in sample 2.
As suggested #Dr Jones, you need use $compile directive.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.myText = "<button ng-click='someFunction(1)'>{{text}}</button>";
$scope.text = "Test";
$scope.someFunction = function(val) {
console.log(val);
};
})
.directive('bindHtmlCompile', function($compile) {
return {
restrict: "A",
scope: {
bindHtmlCompile: "="
},
link: function(scope, elem) {
scope.$watch("bindHtmlCompile", function(newVal) {
elem.html('');
var newElem = angular.element(newVal);
var compileNewElem = $compile(newElem)(scope.$parent);
elem.append(compileNewElem);
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<h3>
Write code for test button
</h3>
<textarea cols="100" ng-model="myText"></textarea>
<div bind-html-compile="myText">
</div>
</div>
</div>

Dynamically creating directive template

I am trying to create template dynamically. When I inject the hard coded value through directives attribute it works fine. But when I assign it using angular variable it does not seem to work. Below is the js code
(function(angular) {
'use strict';
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.nameTempl = 'customer-name.html';
$scope.addressTempl = 'customer-address.html';
}])
.directive('myCustomer', function() {
return {
templateUrl: function(elem, attr){
return attr.type;
}
};
});
})(window.angular);
this is html part
<body ng-app="docsTemplateUrlDirective">
<div ng-controller="Controller">
<div my-customer type="{{nameTempl}}"></div>
<div my-customer type="{{addressTempl}}"></div>
</div>
</body>
Instead of using variables if i directly use values it seems to be working fine.
I dont understand why is this happening?
Plunker code
Your problem is that angular first process the template and then the DOM. So when he gets to the attr.type, it's still {{nameTemp1}}, angular has yet manipulated the DOM. My suggestion is, try to pass the address in another way. This plunker shows how you can try creating a service that would hold an object, and then bind the url to the object. Then, inject the service to the directive and use the url. Just make sure to put an "ng-if" on the directive the checks if the scope had created that object and bound it to the service's object
Here is the service
service('service', [function() {
return {
template: {}
};
}])
And this is the controller:
controller('Controller', ['$scope', 'service', function($scope, service) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.addressTempl = 'customer-address.html';
service.template.url = $scope.addressTempl;
}])
and the directive looks like this:
directive('myCustomer', function(service) {
return {
templateUrl: function(elem, attr){
return service.template.url;
}
};
})
You can use different templates as follows:
Live example on plunker.
(function(angular) {
'use strict';
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.nameTempl = 'customer-name.html';
$scope.addressTempl = 'customer-address.html';
}])
.directive('myCustomer', function() {
return {
scope: {
type: "#",
customer:"=myCustomer",
},
template: '<div ng-include="type"></div>'
};
});
})(window.angular);
And html part
<body ng-app="docsTemplateUrlDirective">
<div ng-controller="Controller">
<div my-customer="customer" type="{{nameTempl}}"></div>
<div my-customer="customer" type="{{addressTempl}}"></div>
</div>
</body>
You cannot access scope in templateURL function however there is a way to load template at run time. Plunker - http://plnkr.co/edit/n20Sxq?p=preview
app.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
restrict: 'A',
link: function(scope , element, attrs) {
scope.$watch(attrs.template, function (value) {
if (value) {
loadTemplate(value);
}
});
function loadTemplate(template) {
$http.get(template, { cache: $templateCache })
.success(function(templateContent) {
element.replaceWith($compile(templateContent)(scope));
});
}
}
}
}]);
Hope this solve your issue.

AngularJS - custom filter, model doesn't update in view

I have a custom filter set up that triggers when the user clicks on a checkbox. I use a directive to render the DOM elements, and attach a listener on the checkbox which when clicked, triggers the filter function that's exposed on the $scope.
The $scope.model which is used in the view should get overwritten by the result of the filter function, and the return object looks ok (e.g. the console.log()) but the view doesn't update. What am I doing wrong?
http://jsfiddle.net/r3pXc/1/
The view:
<body ng-app="app">
<div ng-controller="mainCtrl">
<div list-directive />
</div>
The template:
<script type="text/ng-template" id="list.html">
<input type="checkbox" class="control">
<div ng-repeat="player in model">
Name: {{player.firstName}}, Price: {{player.price}}
</div>
</script>
The module and controller:
var app = angular.module('app', []);
app.controller('mainCtrl', ['$scope', '$filter', function($scope, $filter){
$scope.model = [{ firstName: 'foo', price: 100 }, { firstName: 'bar', price: 50 }, { firstName: 'foobar', price: 0}];
$scope.filter = function() {
$scope.model = $filter('listFilter')($scope.model);
console.log($scope.model);
}
}]);
The directive:
app.directive('listDirective', function(){
return {
restrict: 'AE',
templateUrl: 'list.html',
link: function($scope, iElm, iAttrs, controller) {
iElm.bind('click', function(e){
var el = angular.element(e.target);
if (el.hasClass('control')) {
$scope.filter();
};
});
}
};
});
And the filter:
app.filter('listFilter', function(){
return function(input) {
var results = [];
angular.forEach(input, function(val, key){
if (val.price != 0) {
results.push(val);
}
});
return results;
}
});
Need to manually call the digest cycle with $apply(), because I don't listen on the elements with ng- event handlers:
$scope.filter = function() {
$scope.model = $filter('listFilter')($scope.model);
$scope.$apply();
}

Angular directive how to add an attribute to the element?

I'm wondering what's the way to do work this snippet:
//html
<div ng-app="app">
<div ng-controller="AppCtrl">
<a my-dir ng-repeat="user in users">{{user.name}}</a>
</div>
</div>
//js
var app = angular.module('app', []);
app.controller("AppCtrl", function ($scope) {
$scope.users = [{name:'John',id:1},{name:'anonymous'}];
$scope.fxn = function() {
alert('It works');
};
})
app.directive("myDir", function ($compile) {
return {
link:function(scope,el){
el.attr('ng-click','fxn()');
//$compile(el)(scope); with this the script go mad
}
};
});
I know it's about the compile phase
but I don't get the point so a short explanation would be
very appreciate.
A directive which adds another directive to the same element:
Similar answers:
How to get ng-class with $dirty working in a directive?
creating a new directive with angularjs
Here is a plunker: http://plnkr.co/edit/ziU8d826WF6SwQllHHQq?p=preview
app.directive("myDir", function($compile) {
return {
priority:1001, // compiles first
terminal:true, // prevent lower priority directives to compile after it
compile: function(el) {
el.removeAttr('my-dir'); // necessary to avoid infinite compile loop
el.attr('ng-click', 'fxn()');
var fn = $compile(el);
return function(scope){
fn(scope);
};
}
};
});
Much cleaner solution - not to use ngClick at all:
A plunker: http://plnkr.co/edit/jY10enUVm31BwvLkDIAO?p=preview
app.directive("myDir", function($parse) {
return {
compile: function(tElm,tAttrs){
var exp = $parse('fxn()');
return function (scope,elm){
elm.bind('click',function(){
exp(scope);
});
};
}
};
});
You can try this:
<div ng-app="app">
<div ng-controller="AppCtrl">
<a my-dir ng-repeat="user in users" ng-click="fxn()">{{user.name}}</a>
</div>
</div>
<script>
var app = angular.module('app', []);
function AppCtrl($scope) {
$scope.users = [{ name: 'John', id: 1 }, { name: 'anonymous' }];
$scope.fxn = function () {
alert('It works');
};
}
app.directive("myDir", function ($compile) {
return {
scope: {ngClick: '='}
};
});
</script>

How to modify the controller scope from an ng-repeat in a directive, without using a function?

I would like the ng-click to change the value of the controller scope variable 'controllerLabel'. What's the best way of achieving this without using a controller scope function?
HTML:
<div ng-app="app">
<div ng-controller="Ctrl">
<p>{{controllerLabel}}</p>
<my-template></my-template>
</div>
<!-- my-template.html -->
<script type="text/ng-template" id="my-template.html">
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="controllerLabel = {{clickLabel.text}}">{{clickLabel.text}}</label>
</div>
</script>
</div>
JavaScript:
angular.module('app', [])
.controller('Ctrl', function Ctrl1($scope) {
$scope.controllerLabel = 'Default text';
$scope.clickLabels = [
{'text':'Hello'},
{'text':'World'},
];
})
.directive('myTemplate', function() {
return {
restrict: 'E',
templateUrl: 'my-template.html'
};
});
JSFiddle
You can add link to directive and write like:
.directive('myTemplate', function() {
return {
restrict: 'E',
link: function (scope) {
scope.onClick = function (clickLabel) {
scope.controllerLabel = clickLabel.text;
}
},
templateUrl: 'my-template.html'
};
});
HTML
<script type="text/ng-template" id="my-template.html">
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="onClick(clickLabel)">{{clickLabel.text}}</label>
</div>
</script>
Actually you can write like #Alborz posted but I think to add method into link and call from HTML will be clearer and easy to debug.
Demo Fiddle
I updated your fiddle;
Updated fiddle
You need to use controllerLabel as an object property to have a shared object with controller.
angular.module('app', [])
.controller('Ctrl', function Ctrl1($scope) {
$scope.label = {};
$scope.label.controllerLabel = 'Default text';
$scope.clickLabels = [
{'text':'Hello'},
{'text':'World'},
];
})
.directive('myTemplate', function() {
return {
restrict: 'E',
templateUrl: 'my-template.html'
};
});
Template:
Note to label.controllerLabel = clickLabel.text
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="label.controllerLabel = clickLabel.text">{{clickLabel.text}}</label>
</div>

Resources