Angular add dynamic html(directive) with object as parameter - angularjs

Doesn't sound like it would be a big deal but I don't know what to google for to solve this. I want to be able to fetch an object with $http and then render that info with the help of a directive.
JS:
angular.module("test", []);
angular.module("test").directive('myTest', function() {
return {
templateUrl: 'myTest.html'
};
});
angular.module("test").controller("myCtrl", function($http, $compile){
var vm = this;
vm.name = "Viktor";
vm.country = "Sweden";
vm.origin = "controller";
vm.click = function(){
$http.get("data"+Math.floor((Math.random() * 2) + 1)+".json").success(function(data){
$("body").append($compile("<my-test></my-test>")(data));
})
}
})
Template:
<div>
<div>My name is: {{vm.name}}</div>
<div>I live in : {{vm.country}}</div>
<div>Source origin : {{vm.origin}}</div>
</div>
index.html
<!DOCTYPE html>
<html ng-app="test">
<head>
<script data-require="jquery#*" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script data-require="angular.js#*" data-semver="1.4.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl as vm">
<button ng-click="vm.click()">Add panel</button>
<my-test></my-test>
</body>
</html>
Plunker: http://embed.plnkr.co/YcG9ZFxuR3PYBYASjzm2/preview

I just can't edit your plnkr file.
But bindToController should help you. It should be somethind like this, I create a controller for the directive :
angular.module("test").directive('myTest', function() {
return {
restrict: 'E',
scope: {},
templateUrl: 'myTest.html',
controller: function(){},
controllerAs: 'ctrl',
bindToController: {
name: '=',
country: '=',
origin: '='}
}
});
Also I change the alias of the controller in the template :
<div>
<div>My name is: {{ctrl.name}}</div>
<div>I live in : {{ctrl.country}}</div>
<div>Source origin : {{ctrl.origin}}</div>
<input ng-model="ctrl.name"/>
</div>
Here is a working example modified on your plunkr : plunkr.

I am still stuck in the jQuery way of thinking, I guess I found out how to do it the Angular way. Just to add the newly fetched item to a list and then render the list. Not what I was originally aiming for but that is probably the prettiest way.
And if one now HAVE to add new items instead of redrawing the whole list, I guess one could send in the object as a json-string or set scope variables on the directive for each parameter in the object.
Plunker: http://plnkr.co/edit/5ElTIx?p=preview
Html:
<body ng-controller="myCtrl as vm">
<button ng-click="vm.click()">Add panel</button>
<my-test ng-repeat="panel in vm.panels" obj="panel"></my-test>
</body>
Js:
angular.module("test").controller("myCtrl", function($http, $compile){
var vm = this;
vm.name = "Viktor";
vm.country = "Sweden";
vm.origin = "controller";
vm.panels = [{
name:"Viktor2",
origin:"Controller array",
country:"Sweden"
}];
vm.click = function(){
$http.get("data"+Math.floor((Math.random() * 2) + 1)+".json").success(function(data){
vm.panels.push({
name:data.name,
origin:data.origin,
country:data.country
})
})
}
})

Related

angularjs - access transclude html scope from hosting directive

I have a simple directive with transcluded html.
I want to be able to inject directive scope params to the transclude.
I wrote a simple example in plunker :
https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
I know in angular 4 it can be done, but I can't find a good way to do it in angularjs.
// Code goes here
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope) {
$scope.users = ["tal", "oren", "orel", "shluki"];
$scope.deleteUser = (user) => {alert("trying to delete", user);}
});
app.directive('myList', function myList() {
return {
restrict: 'E',
transclude: true,
template: "<div><table><tr ng-repeat='item in collection'><td> This is inside myList - user name: {{item}} <ng-transclude></ng-transclude></td></tr></table></div>",
scope: {
collection: "="
},
replace: true
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<my-list collection="users">
<h2>This is transclude</h2>
<button ng-click="deleteUser(user)">Delete user: {{user ? user : "User name should be here"}}</button>
</my-list>
</body>
</html>
Will really appreicate some help.
plunker: https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
Here's a working plunker with your example.
http://plnkr.co/edit/BjSowyQdLXd0xoCZFqZ6?p=preview
The idea is to pass it as contents and not html as string. $compile is here because the link is done after ng-repeats already has transcluded its own template.
var template = '<h1>I am foo</h1>\
<div ng-repeat="item in users">\
<placeholder></placeholder>\
<hr>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent) {
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate) {
element.append(clonedTemplate);
});
});
If you want a proper explanation of what the problem was you should check the detailed answer here : Pass data to transcluded element
Hope this helped you out

Angular JS. What the order of actions in HTML when creating a directive?

I have just a problem rightly ask a question).
I created angularJS app 'myApp'. And app get list of two groups. And app should to represent on browser two checkboxes: first - checked, second - empty.
But maybe I didn't count the order of action of building of angular's directives.
This is example
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="group in groups">
<mygroups info='group'></mygroups>
</div>
</div>
<script type="text/javascript">
var groups = [
{
"id":"769", "spam_status":"checked"},
{
"id":"1262", "spam_status":""}
];
var app = angular.module("myApp", []);
app.controller("MainController", function($scope){
$scope.groups = groups;
});
app.directive("mygroups", function(){
return{
scope: {info: "="},
template: "<input type='checkbox' id='{{group.id}}'> {{info.id}}",
//WORK FOR FIRST CHECKBOX IF TYPE: //BUT IT IS NO SENSE//
// template: "<input type='checkbox' id='769'> {{info.id}}",
controller: function(){
for (var i = 0; i < groups.length; i++){
$('#' + groups[i].id).attr(groups[i].spam_status,'');
}
}
};
});
</script>
</body>
</html>
I get a right 'id' of element on front-end when I inspected code on browser. If I put on template number '769' instead {{info.id}}, the first checkbox will checked.
But I think that controller of directive acts before assignment 'id' of elements.
But I need realize this by that method. Because I try to explain on scratches the situation which I don't understand.
Thanks a lot.
Two issues :
First point
Your template is :
<input type='checkbox' id='{{group.id}}'> {{info.id}}
It should be :
<input type='checkbox' id='{{info.id}}'> {{info.id}}
Second point
You must call your dom modification in a $timeout function. On the first execution of your controller, your html does not exists yet.
$timeout will trigger the code at the next digest. So, your html will exists.
Of course, do not modify your html in your controller (even in a directive). The link function is the place to modify the html.
Corrected snippet
Here is the snippet with the correction. There is a jquery error, I didn't watch what it is. Do not hesitate to edit to throw it away.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="group in groups">
<mygroups info='group'></mygroups>
</div>
</div>
<script type="text/javascript">
var groups = [
{
"id":"769", "spam_status":"checked"},
{
"id":"1262", "spam_status":""}
];
var app = angular.module("myApp", []);
app.controller("MainController", function($scope){
$scope.groups = groups;
});
app.directive("mygroups", function(){
return{
scope: {info: "="},
template: "<input type='checkbox' id='{{info.id}}'> {{info.id}}",
//WORK FOR FIRST CHECKBOX IF TYPE: //BUT IT IS NO SENSE//
// template: "<input type='checkbox' id='769'> {{info.id}}",
controller: function($timeout){
$timeout(function() {
for (var i = 0; i < groups.length; i++){
$('#' + groups[i].id).attr(groups[i].spam_status,'');
}
});
}
};
});
</script>
</body>
</html>
Looks like you want to hook the checkbox up
template: "<input type='checkbox' ng-model='info.spam_status' ng-true-value='\'checked\'' id='{{group.id}}'> {{info.id}}"

AngularJS : how to get directive input field model value in controller?

I created a custom directive name kid. In that I have one input field with having usermodel object. I need to get its value in my controller. Can we get user model object in my controller. Actually I used to same directive in my view. I need to get both directive input values in my controller .
Here is my Plnkr
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function(){
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{},
template:"<input type='text' ng-model='usermodel'/>{{usermodel}}",
}
})
I updated your plunkr: updatedMyPlunker
I am passing the usermodel to the kid directive via its isolated scope.
The = sign makes sure that the two models will update through two way data binding
<body ng-app="Testappp">
<div ng-controller="testcontroller">
<kid usermodel="usermodel"></kid>
<kid usermodel="usermodelSecondKid"></kid>
</div>
</body>
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function($scope){
$scope.usermodel = '';
$scope.usermodelSecondKid = '';
$scope.$watch("usermodel", function(newvalue,oldvalue){
console.log(newvalue);
})
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{ usermodel: "=usermodel"
},
template:"<input type='text' ng-model='usermodel'/>{{usermodel}}",
}
})
Forked your plnkr. Passed two way data model from controller to directive. kid1 and kid2 are controller variable. Which will value you enter in text box.
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#*" data-semver="2.0.0" src="scruipt"></script>
<link rel="stylesheet" href="style.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
</head>
<body ng-app="Testappp">
<div ng-controller="testcontroller">
<kid ng-model="kid1"></kid>
<kid ng-model="kid2"></kid>
</div>
</body>
<script>
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function(){
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{
ngModel: '=ngModel'
},
template:"<input type='text' ng-model='ngModel'/>{{ngModel}}",
}
})
</script>
</html>

Using bindToController without a template

I'm trying to learn how isolated scope, bindToController and controllerAs work.
If I make a directive with an isolated scope and a template, it works as expected:
<!DOCTYPE html>
<html ng-app="app">
<head>
<style>
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
function directiveTest() {
function CtrlTest() {
this.foo = "honk";
this.clearFoo = function() {
this.foo='';
};
}
return {
restrict : 'E',
scope: {
label : '#',
lg : '#'
},
bindToController : true,
controller: CtrlTest,
controllerAs: 'ctrlTest',
template: '<label>\n {{ ctrlTest.label }}\n <input type="text" ng-model="ctrlTest.foo"/>\n</label>\n<button ng-click="ctrlTest.clearFoo()">Clear</button>\n\n<div>{{ ctrlTest.foo }}</div>\n\n<div ng-show="ctrlTest.foo.length> ctrlTest.lg">\n Long string !\n</div>\n'
};
}
var app = angular.module('app',[]);
app.directive('dirTest',[directiveTest]);
</script>
</head>
<body>
<dir-test label="Type something now:" lg="7">
</dir-test>
</body>
</html>
I was trying to do the same without the template, but I can't make it work:
<!DOCTYPE html>
<html ng-app="app">
<head>
<style>
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
function directiveTest() {
function CtrlTest() {
this.foo = "honk";
this.clearFoo = function() {
this.foo='';
};
}
return {
restrict : 'E',
scope: {
},
bindToController : true,
controller: CtrlTest,
controllerAs: 'ctrlTest'
};
}
var app = angular.module('app',[]);
app.directive('dirTest',[directiveTest]);
</script>
</head>
<body>
<dir-test>
<label>
Type something again :
<input type="text" ng-model="ctrlTest.foo"/>
</label>
<button ng-click="ctrlTest.clearFoo()">Clear</button>
<div>{{ ctrlTest.foo }}</div>
<div ng-show="ctrlTest.foo.length>7">
Long string !
</div>
</dir-test>
</body>
</html>
However that works if I set scope to true instead of an isolate scope.
Can someone explain to me how to make the 2nd example work, or if it's not possible, why ?
Flip the values of bindToController and scope around.
{
....
scope: true,
bindToController: {}
...
}
I have just hit the same issue over the weekend, and made a simple complete example here: bindToController Not Working? Here’s the right way to use it! (Angular 1.4+)

Angular js directive issue

Below is my code
I created a simple page and include a directive in it. And on ng-click in directive i want to call parent scope's method.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Directive Page</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"> </script>
</head>
<body>
<div ng-app="myapp" ng-controller="myController">
<ul-dir on-item-click="itemClick(obj)"></ul-dir>
</div>
</body>
</html>
<script>
var myapp = angular.module("myapp", []);
myapp.directive('ulDir', function() {
return {
restrict: 'E',
replace: 'true',
template: '<div id="container"><ul><li ng-repeat="content in contents">{{content.name}}</li></ul></div>',
controller: function ($scope) {
$scope.contents = [{'name':'Nishu', 'age':'20'},{'name':'Nidhi', 'age':'21'},{'name':'Kirti', 'age':'24'}];
},
scope: {
onItemClick: '&' // Pass a reference to the method
}
}
});
myapp.controller('myController', function($scope) {
$scope.itemClick = function(content){
console.log("Success : "+content);
};
});
</script>
So my console log print as "success : undefined"
So content object not passing from directive scope to parentscope.
Please help me.
I believe your call inside template should either be:
<a href="#" ng-click="onItemClick({'content':content})">
or
<a href="#" ng-click="onItemClick({'obj':content})">
Can you try. For more details see this SO post Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?

Resources