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

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>

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}}"

Angular add dynamic html(directive) with object as parameter

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
})
})
}
})

Editing resolved object is not updating parent/child controller data

I am sharing data between parent and child controllers using resolve option available in ui-router.
PFB the code,
var app = angular.module('app',['ngRoute','ui.router']);
app.config(function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise('/first/second');
$stateProvider
.state('first', {
url: '/first',
template: "<div>First Page :: Name - <input type='text' ng-model='parentName'/></div> <div ui-view=''></div>",
resolve: {
name : function(){return 'Dheepan Raju';}
},
controller : function($scope,name){
$scope.parentName = name;
}
})
.state('first.second', {
url: '/second',
template: "<div>Second Page :: Name - <input type='text' ng-model='childName'/></div>",
controller : function($scope,name){
$scope.childName = name;
}
});
});
index.html
<!DOCTYPE html>
<html ng-app='app'>
<head>
<script src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2" data-require="angular.js#*"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-route.js" data-semver="1.3.15" data-require="ngRoute#1.3.15"></script>
<script data-require="ui-router#*" data-semver="0.2.15" src="//rawgit.com/angular-ui/ui-router/0.2.15/release/angular-ui-router.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ui-view=""></div>
</body>
</html>
Here is the plunker. When the page is loaded both child and parent input fields are having same name. But if I edit any of those values, it is not updating the other property. Both looks individual. How can I make both the controllers to use same data and if edit that in one controller, other controller is also updated(without any $broadcast or $watch)
This can be accomplished by using a shared service with the controllers. By setting the ng-model of both to use a shared object, you can keep both controllers in sync through the service. The service handles the shared object and the controllers just reference it. No watch or broadcast required.
Updated script.js
app.config(function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise('/first/second');
$stateProvider
.state('first', {
url: '/first',
template: "<div>First Page :: Name - <input type='text' ng-model='parentName.name''/></div> <div ui-view=''>Hell</div>",
resolve: {
name : function(){return 'Dheepan Raju';}
},
controller : function($scope,name, SharedData){
// Init
SharedData.name = name;
$scope.parentName = SharedData;
}
})
.state('first.second', {
url: '/second',
template: "<div>Second Page :: Name - <input type='text' ng-model='childName.name'/></div>",
controller : function($scope, SharedData){
$scope.childName = SharedData;
}
});
}).service("SharedData", function() {
return { name: "" }
});
I have updated your plunker showing this: http://plnkr.co/edit/XIvHXSy6oDuuuKsmE3AF?p=preview

ng-model doesn't work when I create new DOM elements

When I create a new DOM element (out of angular), angular doesn't take it under control. Why?
Exemple:
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button onclick="changeDom();">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<div id='placeNewElement'>
</div>
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function(){
this.name = 'diamont';
this.bright = 3;
});
var changeDom = function(){
document.getElementById('placeNewElement').innerHTML = "<input type='text' ng-model='store.name'/>";
};
</script>
</body>
</html>
Here, initial DOM is created with an input that have databinding with scope.bright data model.
When you click on the button, a new element is created, with ng-model to make a databinding with scope.name.
But when new DOM element is created, angular doesn't 'managed' its element (doesn't show scope.name value, doesn't put typical angular classes (ng-valid, etc) to the element).
I searched a lot and I can't solve it :-(
I tryed to make scope.apply() to upload model, but this appears not to be the problem (because angular doesn't know this new element).
Tnaks for help! ;-)
Jordi
Use the idiomatic way of managing DOM tree (that is, don't modify it from under angular)
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button ng-click="store.showNameField = true">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<input type='text' ng-model='store.name' ng-if='store.showNameField' />
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function(){
this.name = 'diamont';
this.bright = 3;
this.showNameField = false;
});
</script>
</body>
</html>
I founded an answer by Kevin Shay at angularJs google group
The solution for the example code was:
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button ng-click="recognizeNewElements();" onclick="changeDom();">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<div id='placeNewElement'>
</div>
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function($scope, $compile){
this.name = 'diamont';
this.bright = 3;
$scope.recognizeNewElements = function(){
var placeNewElement = angular.element(document.getElementById('placeNewElement'));
$compile(placeNewElement.contents())($scope);
};
});
var changeDom = function(){
document.getElementById('placeNewElement').innerHTML = "<input type='text' ng-model='store.name'/>";
};
</script>
</body>
</html>
Thanks a lot for all you that waste time to help me!
Your approach towards handling new element is wrong. You can achieve this using angular directives and sharing the scope of the same angular controller with the directive. That would update the DOM at run time.
Please have a look at this fiddle example. In this I have added a textbox and dynamically added a new text box which shares the same scope as what was there for first text box and its coming under the scope of angular.
<section ng-app="myApp" ng-controller="MainCtrl">
<addbuttonsbutton></addbuttonsbutton>
<input type='text' ng-model='store'/>
<div id="space-for-buttons"></section>
</section>
var myApp = angular.module('myApp', []);
function MainCtrl($scope) {
$scope.count = 0;
$scope.store = "";
}
//Directive that returns an element which adds buttons on click which show an alert on click
myApp.directive("addbuttonsbutton", function(){
return {
restrict: "E",
template: "<button addbuttons>Click to add buttons</button>"
}
});
//Directive for adding buttons on click that show an alert on click
myApp.directive("addbuttons", function($compile){
return{
link: function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-buttons')).append($compile("<input type='text' ng-model='store' ng-init='scope.store'/>")(scope));
});
}
};
});
Hope this helps.
Thanks Lalit.
I paste your code with some changes for working (may be it will be useful for somebody searching this soluction).
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body>
<section ng-app="myApp" ng-controller="MainCtrl">
<addbuttonsbutton></addbuttonsbutton>
<input type='text' ng-model='store'/>
<div id="space-for-buttons"></div>
</section>
<script type="text/javascript">
var myApp = angular.module('myApp', []).controller('MainCtrl', function($scope) {
$scope.store = "something";
});
//Directive that returns an element which adds buttons on click which show an alert on click
myApp.directive("addbuttonsbutton", function(){
return {
restrict: "E",
template: "<button addbuttons>Click to add buttons</button>"
}
});
//Directive for adding buttons on click that show an alert on click
myApp.directive("addbuttons", function($compile){
return{
link: function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-buttons')).append($compile("<input type='text' ng-model='store'/>")(scope));
});
}
};
});
</script>
</body>
</html>
For me it was not :-(, because I'm looking for something to use witout changing how new DOM elements are created (may be it's not possible :-S ).

Resources