AngularJS passing data from a directive controller to another - angularjs

I have a form, which with you can edit an image gallery, of course I've created a directive for it, like this:
galleryEdit.html
<div>
<form ng-submit="submit()">
<label>Headline:</label>
<input type="text" ng-model="gallery.headline" placeholder="Enter headline here" value=""/>
<label>channels:</label>
<channelselect></channelselect>
<input type="submit"/>
</form>
</div>
So, galleryEdit has another directive channelSelect, which with you can select a channel (not only for galleries)
channelselect.html
<div>
<select>
<option value="{{channel.shortCode}}">{{channel.name}}</option>
</select>
</div>
GalleryEdit has a controller, that passes data (called "gallery") for its directive, so its $scope has $scope.gallery property, which contains channel id: $scope.gallery.channel.
Since channelselect has different controller, that has its scope, this gallery.channel cannot be seen from that scope.
Is there any way to pass data from gallery to channel controller/directive ?
Using $scope.$parent is not a solution, since channelSelect should not know anything where the data is coming from.

You can set up bi-directional binding between your galleryEdit directive's scope and your channelselect directive's scope.
In your channelselect directive's definition, you can do something like the following:
directive('channelselect', [function () {
...
scope: {channel: '='}
...
}])
This will create an isolated scope for your channelselect directive, and will let you use the channel attribute on your <channelselect> tag to set up a bi-directional binding to its parent scope.
So now you can do this in galleryEdit.html:
<channelselect channel="gallery.channel"></channelselect>
Now channelselect's $scope.channel will be bound to galleryEdit's $scope.gallery.channel.
See the Directive Definition Object section of AngularJS's Directives guide for more details on isolated scopes.

Related

Data binding with text input doesn't work when using `ng-if` in a directive

I created an Angular directive (see plunkr) as follows:
JS:
angular.module('plunker', []);
angular.module('plunker').controller('MainCtrl', function($scope) {
$scope.myInputs = {
email: "test#test.org"
}
$scope.logToConsole = function() {
console.log($scope.myInputs.email);
}
});
angular.module('plunker').directive('myEmail', function(){
return {
restrict: 'E',
scope: {
myngmodel: '='
},
template: '<input type="email" ng-if="true" ng-model="myngmodel" />'
};
});
It's called from HTML like this:
<body ng-controller="MainCtrl">
<my-email myngmodel="myInputs.email"></my-email>
<input type="button" value="log to console!" ng-click="logToConsole()">
</body>
The issue is as follows:
When I don't put ng-if="true", in the template's textinput, or I use ng-show instead, the binding works correctly.
But when ng-if="true" is present, the binding no longer works; when I edit the field and click button, the old value is always is written to console.
Is it a bug or works as designed?
Is the issue due to ng-model="myngmodel" not following "the dot rule"?
If so, how could I rewrite the directive so that I can pass the data model entry from outside world in the same spirit?
ng-if creates a child scope - that's the reason why it happens. Here the quote from the documentation:
The scope created within ngIf inherits from its parent scope using prototypal inheritance. An important implication of this is if ngModel is used within ngIf to bind to a javascript primitive defined in the parent scope. In this case any modifications made to the variable within the child scope will override (hide) the value in the parent scope.
So, when you modify the value in your input box - the new value is written to the child scope that inherits the directive's isolated scope:
<my-email myngmodel="myInputs.email" class="ng-isolate-scope">
<input type="email" ng-if="true" ng-model="myngmodel" class="ng-scope">
</my-email>
The MainCtrl scope var is not updated because it is bound to the directive's isolated scope var - not to its child scope.
The workaround is to use the dot notation - to make sure ng-model reads from and writes to the parent isolated scope of the directive:
directive's template:
<input type="email" ng-if="true" ng-model="myngmodel.email" />
binding to the outer scope:
<my-email myngmodel="myInputs"></my-email>

Isolate and shared scope same time in directive AngularJS

New to Angular so not sure if I ask my question the right way.
So I have form.
<form ng-controller="myController" action="" method="get">
<div myDirective>
<input ng-model="question.sex" value="male" type="radio">
<input ng-model="question.sex" value="female" type="radio">
<button ng-click="log(LogThisQuestionAnsware())"></button>
</div>
<div myDirective>
<input ng-model="question.agree" value="no" type="radio">
<input ng-model="question.agree" value="yes" type="radio">\
<button ng-click="log(LogThisQuestionAnsware())"></button>
</div>
</form>
So my goal is to log current "question" answer. on button click.
How can I access local question in myDirective separate from my second directive and have in controller scope too.
--[ Edit: ]--
Ok this is pretty much my scenario. http://jsfiddle.net/y5esnm09/5
Each button have to log its own directive value not both radio values if they are selected.
If I correctly understood your question, you want both directive instances to bind to the same question object, but have their scopes separate from eachother:
Set up a two way data binding between your directive and your controller, e.g.:
<div my-directive question="question">
<!-- the rest -->
angular.module('your.module').directive('myDirective', function() {
return {
scope: question: '=',
//other props
}
This will ensure that both directives bind to the same question object but have their own separate scopes.
An alternative would be to set the scope property to true, that way they will both create a child scope of your controller.
EDIT: Fiddle demonstrating two way binding:
http://jsfiddle.net/L0eqf4qe/
PS: I converted myDirective to my-directive, angular will translate the snake-case to camelCase for you.

How we set to ng-model variable to parent controller in nested controllers scope

I have one ng-controller nested in another controllers scope.
I want to set scope variable in nested controller scope, to parent controller.
I have view:
<div ng-controller="MyCtrl">
<input type="text" ng-model="name"/>
<div ng-controller="CountryDataController">
<angucomplete
(...)
selectedObject="country"/>
</div>
</div>
which is part of the form.
Then on form submit i want to send ng-models from MyCtrl ( name,country) doing:
fields: {name: $scope.name,
country: $scope.country,
},
How can i tell angular, that selectedObject model belongs to MyCtrl, and not CountryDataController.
I tried
selectedObject="MyCtrl.country"
selectedObject="country[MyCtrl]"
but without effects.
selectedObject in angucomplete works like ng-model.
Also I don't want to rewrite logic from CountryDataController to MyCtrl, because in first i have fields for autocomplete and in second file uploading.
Is there any convention for this?
The answer is:
selectedobject="$parent.country"
You can use $parent, but if you move your HTML or eventually add another controller it between it will break.
The correct way to do that is to use the controller as syntax, as shown below:
<!-- use topCtrl to access this controller scope -->
<div ng-controller="MyCtrl as topCtrl">
<input type="text" ng-model="name"/>
<!-- use countryCtrl to access this controller scope -->
<div ng-controller="CountryDataController as countryCtrl">
<angucomplete
(...)
selectedObject="topCtrl.country"/>
</div>
</div>

ng-focus doesn't work with ng-include

I have a problem with ng-focus on ng-include elements. I use this part of code
<div ng-include="'template.tpl.html'" ng-controller="Ctrl"
ng-class="{'highlight':highlight==true,'nofocus':highlight==false}"></div>
and ng focus works fine when I have inside template.tpl.html regular DOM elements like:
<div class="form-group">
<label class="control-label">My label</label>
<div class="input-wrap">
<input type="text" name="Name" class="form-control"
ng-model="client.firstName" ng-disabled="displayMode != 'edit'"
ng-focus="highlight=true" ng-blur="highlight=false">
</div>
</div>
but, if I include another file in template.tpl.html it doesn't work anymore. For example if I apply in template.tpl.html the structure like below it won't work:
<div ng-include="'contacts/contacts-list.tpl.html'"></div>
Why ng-focus doesn't work on ng-include included in ng-include? Any tips on how to resolve this?
(Now that you have clarified your actual issue) As a continuation of the comment, ngInclude created a child scope of the provided scope, in your case child scope of child scope of Ctrl and well that does not matter since overall your wrapper is the Ctrl controller. But here you need to be careful while setting the scope property and expect the changes done from grandchild scope to be reflected in its grandparent scope. You have not even defined the properties in your controller, also even if you define them directly as scope.highlight = false the changes made to the property in the child or its child will not be reflected in the parent (though it will propagate down as a part of inheritance). You should remember scopes (except isolate scopes) are prototypically inherited, so in order to resolve this, you can use a property that holds the object reference which contains the property that needs to be modified, so that even if you change the value of property on that object from grandchild it will still reflect while accessing the same on the parent because they are all looking at the property on the same object reference
So in your controller initialize and object:-
.controller('MainCtrl', function($scope) {
$scope.settings = {};
});
Set the bindings accordingly in the main html:-
<div ng-include="'tmpl.html'" ng-controller="MainCtrl"
ng-class="{'highlight':settings.highlight,'nofocus':settings.highlight}"></div>
And in its grand child as well:-
<input type="text" name="Name" class="form-control"
ng-model="client.firstName"
ng-focus="settings.highlight=true" ng-blur="settings.highlight=false">
Plnkr
You wouldn't get a better explanation on angular scopes than this answer.

AngularJs multiple instances and nested controller

I got confused a bit about whether can we create multiple instance of controller and that to in nested form for eg -
<div ng-controller="abc">
<form ng-submit="call()">
<input type=text ng-model="content"/>
</form>
<div ng-controller = "abc">
<form ng-submit="call()">
<input type=text ng-model="content"/>
</form>
</div>
</div>
i just want to know that if i use the same model with other instance of controller, so model value would be same or different. Similar to static variable ?
i just want to know that if i use the same model with other instance
of controller, so model value would be same or different. Similar to
static variable ?
All declarations of ng-controller create a new instance of the controller. So, if you had two instances side by side, like this:
<div ng-controller="abc">
<input type=text ng-model="content"/>
</div>
<div ng-controller="abc">
<input type=text ng-model="content"/>
</div>
plunker
then, all of the $scope properties of each would be completely independent.
When a ng-controller is nested, then its scope inherits the parent controller's scope. So for this you'd expect that content refers to the same scope property:
<div ng-controller="abc">
<input type=text ng-model="content"/>
<div ng-controller="abc">
<input type=text ng-model="content"/>
</div>
</div>
plunker
However, since content is not defined directly in the controller something strange happens. If you fill in the parent input first. Both, inputs become bound to the same scope property. However, if you fill in the child input first, they are independent!
This can be confusing until you understand that Angular is being lazy when it creates the property on the scope. content is null at first on both scopes. It is only when it has a value that it will inherit.
So, what do you do if you want to keep things separate? Add an initial value to a $scope property inside the controller:
app.controller('abc', function($scope) {
$scope.content = '';
});
plunker
This way, each separate controller instance is initialized with its own content property.
Hope this helps.

Resources