I'm actually not sure how to ask this question, and I'm probably using incorrect terms, so bear with me.
Angular sets up a 2-way data binding so that it makes it easy to work with the data on both sides. But what if I want to change how that data is represented?
Let me give a concrete example.
I want a form with a checkbox, which if bound directly to a model, would be stored as true or false by Angular. However, in another part of the webpage I want that value to show up as 0 or 1, not true or false.
Now sure I could go and make them use two different variables, and use ng-change or something like that to update one based on the other, but that seems overkill and convoluted.
Is there some special meta function or something I can define that lets me essentially translate the data as it goes back and forth?
Use the ngTrueValue and ngFalseValue directives. They define what should be treated as true and false on a checkbox: https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D
Example:
<input type="checkbox" ng-model="foo"
ng-true-value="OK"
ng-false-value="BOO-HOO">
The model will either have a value of "OK" or "BOO-HOO" instead of the default true and false in the above example.
Alternatively, if you want the original model to retain its default values and only draw the custom ones from another variable, you could also use the ngChange directive:
<input type="checkbox" ng-model="foo"
ng-change="bar=foo&&'OK'||'BOO-HOO'">
Now, whenever foo changes, bar will have the corresponding alternative value. Just remember to assign bar an initial value (it will start out with no value at all).
in your controller...
$scope.getvalue(data)
{
if(data===true)
return 1; // write what ever you want...
return 0;
}
in your html page..
bind the normal one as {{$scope.data1}} and other as {{getvalue($scope.data1)}}
You can do some nice things with ngBind:
Check this plunker: http://plnkr.co/edit/cRhLN2p5N4PmI65ps6Gp?p=preview
<input type="checkbox" ng-model="ok"> OK?
<h2>true or false: {{ ok }}</h2>
<h2>0 or 1: {{ ok ? 1 : 0 }}</h2>
Related
I am writing a page using Vue.js and am attempting some custom components. I've started with a very simple one:
Vue.component('single-field', {
template: '<b>{{ key }}:</b> {{ value }}',
props: ['key', 'value']
});
This works fine on its own: it is intended to take two values and return them in a key: value format. Initially, when I was passing the value as an element from the Vue data object (specifically, pickup.dateTime it wasn't working. It's easier to show than explain:
<div id="app">
<single-field key="Date & Time" value="pickup.dateTime"></single-field>
</div>
I was able to fix this by binding the value (notice the colon by value):
<single-field key="Date & Time" :value="pickup.dateTime"></single-field>
I then wrote a second component which is designed to take an object and a title and return each element of the object as a set of <single-field> components, with a title above them. Here is what I wrote:
Vue.component('field-block', {
template: '<h2>{{title}}</h2>\
<div v-for="(p-key, p-value) in parent">\
<single-field key="p-key" value="p-value"></single-field>\
</div>',
props: ['parent', 'title']
});
Now, I think there must be something wrong with the way I am binding (or not binding?) which is causing the data to be displayed in a strange way. Here is my HTML:
<div id="app">
<single-field key="DateTime" :value="pickup.dateTime"></single-field>
<single-field key="Name" :value="pickup.name"></single-field>
<field-block title="Address" :parent="pickup.address"></field-block>
</div>
The field pickup.address is an object, so I was hoping that the component would iterate through the elements and return them as single fields, but what it's currently doing is just returning the variable names, like p-key: p-value. I tried doing a bind on both of them in the component definition (on both because they are now both variables being passed whereas previously I was using a static string for the key field):
<div v-for="(p-key, p-value) in parent">
<single-field :key="p-key" :value="p-value"></single-field>
</div>
But what this returns is NaN: NaN, and I can't figure out why it would do that. I don't fully understand the bind operation, but I expect that it only binds to data in the data option when defining the view, which is not where p-key and p-value are defined. How would I get the variables to 'carry through' from the outer component to the inner ones? Thanks.
Okay this one was tricky.
Several problems with your code:
Your components should have only one root element. So pack your components into a <div>.
Then, - is not a valid character to put into a javascript variable name. So you can't write (p-key, p-value) in parent, write something else, like (pKey, pValue) in parent.
On the other hand, html properties are not case-sensitive, so you should actually write key-name (for example) instead of keyName, but only for the property names. This limitation does apply only in your html files (not in your string templates).
Finally, here is a working js-fiddle.
https://jsfiddle.net/6juwLd3b/
Additionally, I would advise you that you look at your console while developing. Many error where displayed that could have lead you to a working version.
I am quiet recent to angular 2 and was bumping my head around two way data binding. Here is the confusion:
<input (input)="username = $event.target.value" id="testing">
<p>{{username}}</p>
This serves the purpose well. It already seems to be two way data binded. I can access username inside the component and the value property of the input element is updated as well. Why do i need [ ] at all then? What is the two way binding here? what goes inside the component and what comes out of the component.
Disclaimer: I know it's a very naive and seemingly stupid question :|
As i understand write like you did:
(input)="username = $event.target.value"
is the same as:
[(input)]="username"
So they're both 2 way data binding
if you want only one way:
(input)="username"
https://angular.io/docs/ts/latest/guide/template-syntax.html
Angular 2 data binding is explained here.
When you write in the input you execute: "username = $event.target.value", wich assigns to the variable user the value you enter.
That means that the following html does the same:
[(ngModel)]="username"
[username]="username" (input)="updateUserName()"
(input)="username = $event.target.value" and {{userName}}
I am using the following angular binding on an input:
<li data-ng-repeat="value in model.Values">
<input type="number"
data-required
data-min="-10"
data-max="10"
data-ng-model="value.Percentage"
ng-disabled="model.IsDisabled" />
In the case that the model.IsDisabled is true, I need to disabled the input and set the value of the input to 0, if it changes to false I need to change it back to the original value.Percentage value.
This is as far as I have got as I am new to Angular, originally I had it as a directive but seemed overkill, can I achieve this with the standard bindings?
What you can possibly do is create another array in your controller like:
$scope.newValues = []
model.Values.forEach(function(index, value){
if($scope.model.IsDisabled)
$scope.newValues.push(0)
else
$scope.newValues.push(value)
})
and now use new array for binding in HTML.
If you do not want to create a new array, you can add new property to models like calculatesPercentage and use that for binding.
And you can keep a watch on model.IsDisabled to recalculate the array whenever it changes.
you do not need watch anything just clone your array and save and take values from it when checkbox's value changes...
Here I created a PLUNKER for you...
CHANGES
track by $index I add this to ng-repeat otherwise angular will throw dupes error when we set all values to 0.
I cut your object a little for this example using Values instead Values.Percentage
$scope.model = {'Values' : [2,5,8,3,7]};
If you add any of the angular directives for validation (ng-minlength, ng-maxlength, ng-pattern, etc.) to an input that is bound to a breeze entity it blocks any user input if found to be invalid.
If the value from ng-model is initially valid it shows up, but if you change the value to something invalid the input field is cleared, the model is set to null, and you can't input anything that may be initially invalid. However if you copy a valid value into the field it shows.
I would be fine with the fact that the model value is set to null when invalid if it didn't clear the input then prevent changes.
Also I have a feeling that whatever is causing this issue is also messing up ui-mask. The same thing happens there just without the angular validation directives.
here is a Plunker I found from a similar question that I modified to show my issue:
http://plnkr.co/edit/dVsF7GFY65a30soLL5W8?p=preview
Edit
After many many hours of research I did find a solution that works although I am not sure of any ill side effects.
It has to do with how angular does validation in the first place by setting the $modelValue to 'undefined' if it fails any validators as it makes it's way through $parsers and $formatters.
I found this code in Angular (line 16331) that gets called by each angular validator:
function validate(ctrl, validatorName, validity, value){
ctrl.$setValidity(validatorName, validity);
return validity ? value : undefined;
}
I changed it to return 'value' instead of 'undefined':
function validate(ctrl, validatorName, validity, value){
ctrl.$setValidity(validatorName, validity);
return value;
}
Angular still sets validation correctly. Although I am sure this isn't the best solution or even a good one.
I suspect the problem arises when Angular sets $modelValue to 'undefined' then Breeze sees that the model has changed and updates the entity which then updates the model which then clears the input and so forth... Or something like that...
I found this to be helpful in my quest. Maybe it will be helpful to one of you that knows much more than I https://github.com/angular/angular.js/issues/1412
Angular 1.3.0-rc.1 introduced the allowInvalid option for use with the ngModelOptions directive. It is essentially a formalization of the OP's hack at line 16331. The option instructs Angular to allow invalid form inputs to be written to $scope, and solves the problem neatly.
Usage:
<input type="email" ng-model-options="{allowInvalid: true}" ng-model="my_breeze_model.email"/>
See this feature request for more information: https://github.com/angular/angular.js/issues/8290.
I'm happy to look at your plunker and see if there is something Breeze can do about this.
I'm not terribly surprised. Ng also struggles when you combine it with HTML 5 validation as I recall. You really should only use one scheme I think.
Do you disagree?
Also, have you considered the zValidate directive in the Breeze Labs breeze.directives.js? We think that is the best way to expose Breeze entity property validation errors in the view.
Another solution to consider is to use the ng-model-options attribute made available with Angular 1.3+.
In this way, you can prevent the Angular digest occurring after every keypress, and instead defer it to, for example, the 'blur' event so that the use has a chance to actually enter the valid data.
It would look like this:
<input type="email" ng-model="customer.email" ng-model-options="{ updateOn: 'blur' }">
However, this still has the limitation that if you enter invalid input, on blur the input will be cleared out and the use will have to enter it all again. Not very user friendly in my opinion, so I'll be trying out the breeze-only approach to circumvent this issue.
However, I thought this solution was also worth mentioning here.
https://docs.angularjs.org/error/ngModel/numfmt describes how Angular considers it a programming error, not a user input error, if programmatic model changes don't respect the input's validation rules.
If your model does not contain actual numbers then it is up to the application developer to use a directive that will do the conversion in the ngModel $formatters and $parsers pipeline.
Their example describes a String model value for an <input type='number'> but the same logic applies here, I think. If your input contains a minLength attribute, the scope should not get updated with strings that are too short.
So to fix it, add a custom directive to your input that pushes a custom parser into the $parsers pipeline.
For instance, the following directive will hold off writing the value of an <input type='text' minLength='4' min4> to the scope until a long enough string has been typed into it:
.directive('min4', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$parsers.push(function(value) {
return value && value.length >= 4 ? value : "";
});
}
};
});
This prevents the nasty interactions that otherwise occur when Breeze writes updated values back to the scope and ends up overwriting not-yet-legal state in the input.
See Plunker demo
item.imposed is either 1, or 0.
In order for a checkbox to mark as checked, the value must be true or false.
<input type="checkbox" ng-model="item.imposed">
You cannot use a filter inside of ng-model, how can this be accomplished simply and correctly?
You can use ngChecked, but you won't get any binding back to your model:
http://jsfiddle.net/fMBQj/
Or you can use ngTrueValue and ngFalseValue, but you HAVE to use a string (not an int):
http://jsfiddle.net/fMBQj/1/
Or you can use a custom directive... it's pretty lame right now.
You can use ng-value-true to tell angular that your ng-model is a string.
I could only get ng-true-value working if I added the extra quotes like so (as shown in the official Angular docs - https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D)
ng-true-value="'1'"