How to update the NgModel of a transcluded element - angularjs

I need to change the model of a transcluded selectbox from inside the directive. Usually I'd do it with require: '?ngModel' but this only works if the directive in attached on the element as an attribute which transclusion cannot do.

Transcluded content is bound to the parent scope of the directive.
One can use the ng-form directive to enclose the transcluded ngModelControllers and put those controller on the isolate scope of the directive.
<ng-form name="$ctrl.form1">
<fieldset>
Transcluded content<br>
<ng-transclude></transclude>
</fieldset>
</ng-form>
Then one can use the $setViewValue method of the ngModelController:
controller: function() {
this.radioChange = function(val) {
console.log(val);
this.form1.sel1.$setViewValue(val);
this.form1.sel1.$render();
}
},
The DEMO on PLNKR

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>

Angular transcluding attributes

I'm looking to pass through most of the attributes on my directive, like so:
<my-directive name='test'
type='select'
ng-required
ng-options="v for (k,v) in types" />
Where name and type are specific to the directive and everything else is passed through to the directive template:
<div class='parent'><input name="hello" ng-transclude /></div> - the input should be given an ng-required and ng-options.
How do I do this?
You can use the link function of the directive, you can inject to it a list of the attributes you put in the directive and apply it the the element you need inside the directive, nite that if you use directive attributes you will have to use $compile to let angular comprehend it is a directive and not just an attribute

AngularJS evaluate $rootScope variable in directive template

I have a directive that creates an input field.
I need to set the ng-model attribute of this input field to the value of a $rootScope
variable.
The reason behind this is that I want the input field to be in the layout, and bind to different models depending on what page is loaded.
I thought I'd set this global variable in each controller and access it in the Directive.
ATM the variable is hard coded
App.run(function($rootScope){
$rootScope.mymodel = 'search.name';
})
And the Directive
Directives.directive('inputFilter', function(){
return{
restrict: 'E',
replace:true,
controller: function($scope, $rootScope){
console.log($scope.mymodel);
console.log($rootScope.mymodel)
},
template: '<input class="filter" type="text" ng-model="mymodel" placeholder="Nach filtern">'
}
});
It gets rendered as
<input class="filter ng-pristine ng-valid" type="text" ng-model="mymodel" placeholder="Filter">
and the text inside the input field is the value of mymodel variable. The console.log shows
search.name
search.name
Could anyone please shed some light on this issue?
What I think you want is
template: '<input class="filter" type="text" ng-model="'
+ $rootScope.mymodel + '" placeholder="Nach filtern">'
Fiddle.
Note that you will need to inject $rootScope into your directive:
Directives.directive('inputFilter', function($rootScope) {

AngularJS passing data from a directive controller to another

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.

What's the difference between ng-model and ng-bind

I'm currently learning AngularJS and am having difficulty understanding the difference between ng-bind and ng-model.
Can anyone tell me how they differ and when one should be used over the other?
ng-bind has one-way data binding ($scope --> view). It has a shortcut {{ val }}
which displays the scope value $scope.val inserted into html where val is a variable name.
ng-model is intended to be put inside of form elements and has two-way data binding ($scope --> view and view --> $scope) e.g. <input ng-model="val"/>.
tosh's answer gets to the heart of the question nicely. Here's some additional information....
Filters & Formatters
ng-bind and ng-model both have the concept of transforming data before outputting it for the user. To that end, ng-bind uses filters, while ng-model uses formatters.
filter (ng-bind)
With ng-bind, you can use a filter to transform your data. For example,
<div ng-bind="mystring | uppercase"></div>,
or more simply:
<div>{{mystring | uppercase}}</div>
Note that uppercase is a built-in angular filter, although you can also build your own filter.
formatter (ng-model)
To create an ng-model formatter, you create a directive that does require: 'ngModel', which allows that directive to gain access to ngModel's controller. For example:
app.directive('myModelFormatter', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, controller) {
controller.$formatters.push(function(value) {
return value.toUpperCase();
});
}
}
}
Then in your partial:
<input ngModel="mystring" my-model-formatter />
This is essentially the ng-model equivalent of what the uppercase filter is doing in the ng-bind example above.
Parsers
Now, what if you plan to allow the user to change the value of mystring? ng-bind only has one way binding, from model-->view. However, ng-model can bind from view-->model which means that you may allow the user to change the model's data, and using a parser you can format the user's data in a streamlined manner. Here's what that looks like:
app.directive('myModelFormatter', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, controller) {
controller.$parsers.push(function(value) {
return value.toLowerCase();
});
}
}
}
Play with a live plunker of the ng-model formatter/parser examples
What Else?
ng-model also has built-in validation. Simply modify your $parsers or $formatters function to call ngModel's controller.$setValidity(validationErrorKey, isValid) function.
Angular 1.3 has a new $validators array which you can use for validation instead of $parsers or $formatters.
Angular 1.3 also has getter/setter support for ngModel
ngModel
The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope.
This directive executes at priority level 1.
Example Plunker
JAVASCRIPT
angular.module('inputExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.val = '1';
}]);
CSS
.my-input {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
.my-input.ng-invalid {
color:white;
background: red;
}
HTML
<p id="inputDescription">
Update input to see transitions when valid/invalid.
Integer is a valid value.
</p>
<form name="testForm" ng-controller="ExampleController">
<input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
aria-describedby="inputDescription" />
</form>
ngModel is responsible for:
Binding the view into the model, which other directives such as
input, textarea or select require.
Providing validation behavior (i.e. required, number, email, url).
Keeping the state of the control (valid/invalid, dirty/pristine,
touched/untouched, validation errors).
Setting related css classes on the element (ng-valid, ng-invalid,
ng-dirty, ng-pristine, ng-touched, ng-untouched) including
animations.
Registering the control with its parent form.
ngBind
The ngBind attribute tells Angular to replace the text content of the specified HTML element with the value of a given expression, and to update the text content when the value of that expression changes.
This directive executes at priority level 0.
Example Plunker
JAVASCRIPT
angular.module('bindExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.name = 'Whirled';
}]);
HTML
<div ng-controller="ExampleController">
<label>Enter name: <input type="text" ng-model="name"></label><br>
Hello <span ng-bind="name"></span>!
</div>
ngBind is responsible for:
Replacing the text content of the specified HTML element with the
value of a given expression.
If you are hesitating between using ng-bind or ng-model, try to answer these questions:
Do you only need to display data?
Yes: ng-bind (one-way binding)
No: ng-model (two-way binding)
Do you need to bind text content (and not value)?
Yes: ng-bind
No: ng-model (you should not use ng-bind where value is required)
Is your tag a HTML <input>?
Yes: ng-model (you cannot use ng-bind with <input> tag)
No: ng-bind
ng-model
ng-model directive in AngularJS is one of the greatest strength to bind the variables used in application with input components. This works as two way data binding. If you want to understand better about the two way bindings, you have an input component and the value updated into that field must be reflected in other part of the application. The better solution is to bind a variable to that field and output that variable whereever you wish to display the updated value throughoput the application.
ng-bind
ng-bind works much different than ng-model. ng-bind is one way data binding used for displaying the value inside html component as inner HTML. This directive can not be used for binding with the variable but only with the HTML elements content. Infact this can be used along with ng-model to bind the component to HTML elements. This directive is very useful for updating the blocks like div, span, etc. with inner HTML content.
This example would help you to understand.
angular.module('testApp',[]).controller('testCTRL',function($scope)
{
$scope.testingModel = "This is ModelData.If you change textbox data it will reflected here..because model is two way binding reflected in both.";
$scope.testingBind = "This is BindData.You can't change this beacause it is binded with html..In above textBox i tried to use bind, but it is not working because it is one way binding.";
});
div input{
width:600px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<head>Diff b/w model and bind</head>
<body data-ng-app="testApp">
<div data-ng-controller="testCTRL">
Model-Data : <input type="text" data-ng-model="testingModel">
<p>{{testingModel}}</p>
<input type="text" data-ng-bind="testingBind">
<p ng-bind="testingBind"></p>
</div>
</body>
ngModel usually use for input tags for bind a variable that we can change variable from controller and html page but ngBind use for display a variable in html page and we can change variable just from controller and html just show variable.
We can use ng-bind with <p> to display, we can use shortcut for ng-bind {{model}}, we cannot use ng-bind with html input controls, but we can use shortcut for ng-bind {{model}} with html input controls.
<input type="text" ng-model="name" placeholder="Enter Something"/>
<input type="text" value="{{name}}" placeholder="Enter Something"/>
Hello {{name}}
<p ng-bind="name"</p>

Resources