directive twoway data binding inside ng-repeat is not working - javascript

Here i want to update controller scope value as per change in directive scope but its only working outside the ng-repeat and its not working inside ng-repeat..
HTML
<div ng-app="app">
<div ng-controller="MainCtrl">
<div>
<h3>
outside repeat
</h3>
<br> Name <strong>{{name}}</strong>
<div class="directive" my-directive name="name"></div>
<button ng-click="run()">See changes</button>
<br>
<br>
<h3>
Inside repeat
</h3>
<br>
<div ng-repeat="(k,v) in rawdata">
{{k}} {{v.key}} Name <strong>{{name}}</strong>
<div class="directive" my-directive name="name"></div>
<button ng-click="run()">See changes</button>
<br>
</div>
</div>
</div>
JS
var app = angular.module("app", []);
app.controller("MainCtrl", function($scope) {
$scope.name = "HappyNewYear2016";
$scope.run = function() {
alert($scope.name);
}
$scope.rawdata = [{
key: "event1",
}, {
key: "event2",
}];
});
app.directive("myDirective", function() {
return {
restrict: "EA",
scope: {
name: "="
},
template: [
"<div>",
"Name : <strong>{{name}}</strong>; Change name:<input type='text' ng-model='name' /><br/>",
].join("")
};
});
JSFiddle Link
Please help me in updating controller value from directive inside this ngrepeat..

Basically the problem is the $scope.name inside the controller is not the same as you are passing to directive inside the ng-repeat element, because ng-repeat does create a child scope which is prototypically inherited from the parent scope while looping through rawdata object.
There are several ways to solve this problem.
If you wanted to solved this child and parent scope related issue just by using $parent annotation before name which will refers to parent scope.
Plunkr With $parent in directive
Cons:-
But after certain point $parent will make you crazy. Like suppose if you have two or three hierarchy of child scope it will become like $parent.$parent.$parent.name which looks very wiered.
In your case name is of primitive datatype, so while creating child scope the primitive datatypes values of parent scope aren't accessible inside the child scope. That is the reason why you were using $parent to indicates that name belongs to parent scope. You could overcome this problem just by following do annotation while declaring object. Which will help you to make parent scope property available inside the child by following prototypal inheritance.
HTML
<div class="directive" my-directive name="model.name"></div>
Code
$scope.model = {
name: 'HappyNewYear2016'
};
Plunkr with Dot Notation
You could solve this problem just by passing name value from the run function on ng-click="run(name)"
Html
<div ng-repeat="(k,v) in rawdata">
{{k}} {{v.key}} Name <strong>{{name}}</strong>
<div class="directive" my-directive name="name"></div>
<button ng-click="run(name)">See changes</button>
<br>
</div>
Code
$scope.run = function(name) {
alert(name);
}
You could use controllerAs syntax while declaring controller with its alias and the pass the controller name property by its alias.
Working Plunkr

It's all because of how prototypical inheritance works as both scopes directive and controller have same name for model. Child directive shadows the controller model.
Instead of storing controller model directly as variable use it with object.
$scope.data = {
name : 'HappyNewYear2016'
}
Then use it asdata.name to setup in ng-repeat. It will reflect it in parent as well.

Related

isolated scope two way binding is not working in angularjs

I am new to angularjs. I am working with isolated scope. The two way binding using isolated scope is not working. Please check my code. If i remove age : '=' then my code is working fine.
**HTML**
<div ng-controller="homeCtrl">
<my-dir name="{{namee}}" age="{{age}}"></my-dir>
</div>
**JS**
var app = angular.module("home")
app.controller("homeCtrl",["$scope",function($scope){
$scope.namee = "John";
$scope.age= 30;
}]);
app.directive("myDir",function(){
return{
restrict :'E',
scope: {
name : '#',
age : '=',
},
template: ['Directive name is: {{name}}',
'<p>{{age}}</p>'
]
}
})
**Output**
John 30
Directive name is: {{name}} {{age}}
It should be:
<div ng-controller="homeCtrl">
<my-dir name="name" age="age"></my-dir>
</div>
remove the {{}} use just age="age"

Define custom directive with isolated scope depanding object value

I have list of Object of person type in my controller when I loop through them it works fine and display name of all person from list.
I have define a controller to display data in structured why but its not allowing to bind scope of custom directive to value of person name
this piece of works fine
<div ng-app="myapp">
<div ng-controller="MyController">
<div ng-repeat="per in person">
<!-- <test user=" -->
{{per.firstName}}
<!-- "></test> -->
</div>
But When I try to bind scope of custom directive to object first name ,AngularJS throw error
Error: [$parse:syntax] Syntax Error: Token '{' invalid key at column 2 of the expression [{{per.firstName}}] starting at [{per.firstName}}].
<div ng-repeat="per in person">
<test user=" {{per.firstName}}"></test>
</div>
</div>
</div>
AngularJs cond:
var myapp=angular.module('myapp',[]);
myapp.controller('MyController',function($scope){
$scope.person=[
{firstName:"ali1",
lastName:"ahmad"
},
{firstName:"ali2",
lastName:"ahmad3"
},
{firstName:"ali4",
lastName:"ahmad"
},
{firstName:"ali5",
lastName:"ahmad"
},
];
});
myapp.directive('test',function(){
var directive={}
directive.restrict="E";
directive.template="Name : {{user.firstName}}";
directive.scope={
user:"=user"
}
return directive;
});
How to bind custom directive with value of object directive.?
Try this:
<test user="per"></test>
html:
myapp.directive('test', function () {
return {
restrict: 'E',
scope: {
user: '='
},
template: 'Name : {{user.firstName}}'
};
});
when you bind to a scope you just pass the object, no need for {{}}, so
user="per.firstName"
Change this line
<test user=" {{per.firstName}}"></test>
To
<test user="person"></test>
And use ng-repeat inside the directive.
First send the data from your controller to your directive like this
<test user="person"></test>// Here you are sending whole array
Then receive your user which is containing refrence of person
Then inside your directive receive user in some variable as you have done this inside scope of directive user: "=user"

Angular + Typescript: Use Directive Class Scope Variable in Controller Class

Having another problem with figuring out Angular Typescript; this time when it comes to directives with controllers. Trying to pass an object from a page to a directive and then use that object in the controller of the directive. Maybe this isn't the correct approach, but it seems to make sense to me; just can't figure out how to access the object in the controller.
HTML from page:
<div>
<section>
On View: {{ee.obj.name}}
<image-upload obj="ee.obj"></image-upload>
</section>
</div>
Directive template:
<div>
On directive: {{obj.name}}
On controller: {{iuc.obj.name}}
<div class="row">
<div class="col-md-4 text-primary h4">
Add Images
</div>
</div>
<div class="row">
<div class="col-md-3">
<input type="file" multiple ng-model="iuc.imageUploads" ng-change="iuc.uploadImages()" />
</div>
</div>
<div class="row">
<div class="col-md-3 dropzone"></div>
</div>
</div>
Directive typescript:
/// <reference path="../../../scripts/_all.ts" />
module ObjConfig {
'use strict'
export class ImageUpload implements ng.IDirective {
static instance(): ng.IDirective {
return new ImageUpload();
}
restrict = 'E';
replace = true;
templateUrl = '../../../../App/AppConfig/Views/Directives/ImageUpload.html';
scope = {
obj: '='
};
controller = imageUploadCtrl;
controllerAs = 'iuc';
}
export class imageUploadCtrl {
obj: OBJBase;
imageUploads: OBJImage[];
constructor() {
}
uploadImages() {
//THIS IS WHERE I WANT TO ACCESS OBJ
//this.imageUploads.forEach((iu) => { this.obj.images.push(iu); });
}
}
angular.module('ObjConfig').directive('imageUpload', ImageUpload.instance);
}
When I use "this.obj" in the method, it comes back as undefined so obviously the controller "obj" doesn't get automatically wired up to the directive "obj". The ee.obj.name on the page shows value, the obj.name at the top of the directive template shows value, but the iuc.obj.name does not show value. So I've been trying to find a way to link the directive obj to the controller obj and I've tried using a link function to use ng.IAttributes, but that doesn't give me the object; it gives me ee.obj.
Any help would be greatly appreciated.
You should be able to accomplish what you are trying to do using bindToController: true.
This feature is documented in the $compile documentation, so it's not easy to locate, but it does what you want.
When an isolate scope is used for a component (see above), and controllerAs is used, bindToController: true will allow a component to have its properties bound to the controller, rather than to scope. When the controller is instantiated, the initial values of the isolate scope bindings are already available.

dynamically create directive angularjs and obtain variable input from new directive

Say I have the following directive called foo:
<div>
<label>Enter foo: </label>
<input ng-model="myModel"/>
</div>
And I use it like so:
<foo></foo>
<button>Add foo: </button>
What I am trying to do is dynamically add the foo directive on each button click and be able to have access to the new model variable that was created in the corresponding controller for this page.
Can this be done using angularJS?
First of all, if you plan to re-use <foo>, then you should create an isolated scope:
.directive("foo", function(){
return {
restrict: "E",
scope: {
data: "="
},
template: "<div><label>Enter foo: </label><input ng-model='data'/></div>"
}
});
There is no difference between creating a custom directive or other tags. Without knowing too much about what you're doing, I could suggest the following:
app.controller("MainCtrl", function($scope)){
$scope.fooModels = [];
$scope.addFoo = function(){
$scope.fooModels.push(new FooModel());
};
}
FooModel() here is just a placehold for whatever the data model you need to represent foo data. You could also just do: $scope.fooModels.push({});.
Then in the view, simply ng-repeat your fooModels:
<div ng-repeat="fooModel in fooModels">
<foo data="fooModel.data"></foo>
</div>
<button ng-click="addFoo()">Add Foo</button>
Here's a plunker to play with.

Angular JS breaking two way binding on isolate scope when binding to a primitive and using ng-include to dynamically load a template

I have a bit of a strange scenario that is a little different to the other childscope and two way binding issues I have seen on Stackoverflow.
I have a field generation directive that receives a configuration object and some data and dynamically creates the correct type of field on screen and populates the data.
directive.js
.directive('myField', function () {
var stringTemplate = "scripts/directives/templates/my-string.tpl.html";
var textTemplate = "scripts/directives/templates/my-text.tpl.html";
var selectTemplate = "scripts/directives/templates/my-select.tpl.html";
var linker = function ($scope, elem, attrs) {
// Function to dynamically select the correct template
$scope.getTemplateUrl = function () {
var template = '';
if ($scope.options) {
if ($scope.options.optionList) {
template = selectTemplate;
} else {
switch ($scope.options.type) {
case 'String':
template = stringTemplate;
break;
case 'Text':
template = textTemplate;
break;
}
}
return template;
}
};
return {
restrict: 'E',
replace: true,
scope: {
options: '=',
data: '=',
fieldName: '#',
fieldWidth: '#',
labelWidth: '#',
},
link: linker,
template: '<ng-include src="getTemplateUrl()"/>'
}
});
I then have the corresponding template... I'm showing just the string template in this case.
my-string.tpl.html
<div class="form-group col-md-12">
<label for="{{fieldName}}" class="{{labelWidth}}">
{{options.label}}
</label>
<div class="{{fieldWidth}}">
<input type="text" class="form-control input-sm" id="{{fieldName}}" placeholder="{{options.watermark}}" ng-model="data" tooltip="{{options.tipText}}" ng-disabled="options.editable === false">
</div>
</div>
An example of how this might then be used would be
controller.js
$scope.person.firstName = "John";
$scope.person.lastName = "Doe";
$scope.options.person.firstName.type = "String";
index.html
<div class="row">
<my-field options="options.person.firstName" data="person.firstName" field-name="firstName" label-width="small" field-width="medium"></my-field>
The problem is the usual one, my-field directive has an isolated scope with a "data" property that is two-way bound to the controller. Because I am then using ng-include to dynamically load the correct template I am creating a further child scope that due to prototypical inheritance still populates correctly as it doesn't have its own data property so reaches to the parent. However when I modify the field, a shadow property is created on my child scope called data that doesn't propagate upwards the way that two way binding should.
I hope you are still with me
controller > my-field
ng-include causes the following scopes to exist
controller > my-field > ng-include
From reading around I understand that what I need to do to rectify this is pass an object rather than a primitive, however as there is effectively an intermediate layer between my controller and my final directive this is not straightforward.
I thought about changing the isolate scope in my-field to look like this
scope: {
....
data: {value: '=data'}
....
}
and then updating the template to refer to the object
my-string.tpl.html
<div class="form-group col-md-12">
<label for="{{fieldName}}" class="{{labelWidth}}">
{{options.label}}
</label>
<div class="{{fieldWidth}}">
<input type="text" class="form-control input-sm" id="{{fieldName}}" placeholder="{{options.watermark}}" **ng-model="data.value"** tooltip="{{options.tipText}}" ng-disabled="options.editable === false">
</div>
</div>
but this kills angular.
I have successfully got it to work by reaching back to the controller scope for binding by using
ng-model="$parent.$parent.data"
but I am not really happy with this as a solution as A it is ugly and B it involves knowing the depth of scope you are at which could vary.
Really stumped with this. Any help would be appreciated.

Categories

Resources