Using conditional operators in v-model? - javascript

I have a vue component that shows a form populated with items from a selected item to edit. Now I don't want to have to use a second form for creating a new item. At the moment I auto populate and update the item with v-model which obviously updates the object. Am I not able to use conditional operators in this like so?
<form #submit.prevent>
<div class="field">
<label class="label">Job Title</label>
<p class="control">
<input type="text" class="input" placeholder="Job title" v-model="experiences[editIndex].title ? experiences[editIndex].title : ''" />
</p>
</div>
</form>

You can use conditional operators with v-model, but you can't give v-model a string like you're attempting in your example.
I wouldn't use the same form for editing and creating (might be preference). I would make the form its own component and then make two additional form components for editing and creating.
However, if you really want to handle the logic in each input's v-model directive, you would need to give it a variable in the last part of the ternary operator. Something like this:
v-model="experiences[i].title ? experiences[i].title : newExperience.title"

If you use eslint-plugin-vue it will complain about ternary in v-model.
ESLint: 'v-model' directives require the attribute value which is
valid as LHS. (vue/valid-v-model)
So I'd rather explicitly use a pair of :value and #input props.
Like that:
<input
type="text"
class="input"
placeholder="Job title"
:value="experiences[editIndex].title ? experiences[editIndex].title : ''"
#input="experiences[editIndex].title = $event.target.value"
/>
Also, you can use some function for #input, which will check property existence and add it if necessary.

Related

Textarea with ngModel get overwritten in a ngFor loop

I just started working on an Angular project.
In the following code, I would like to use ngFor and ngModel to iterate an array and put the item value as a default value for the text area. And I have an example with two values in the array, all the textareas are always get overwritten by one of them instead of showing the different values on the website. Does anyone know what could be possible reason for that?
<mat-form-field *ngFor = "let item of model.answers; let index = index;" class="example-full-width" appearance="fill">
<mat-label>Cluster Answer</mat-label>
<textarea
matInput
id="{{item['id']}}"
rows="7"
placeholder="Cluster answer"
[(ngModel)]="model.answers[index]['answer']"
name="clusterAnswer"
formControlName="clusterAnswer"
></textarea>
<mat-hint>Define the cluster answer to be suggested</mat-hint>
</mat-form-field>
you must define the value in the form builder in the ts non in the html:
exampe.formBuilder{
test:[defaultValue]
}
you already have ngModel so no need to specify formControlName, use of [(ngModel)] with formControlName has been deprecated
<mat-form-field *ngFor = "let item of model.answers; let index = index;" class="example-full-width" appearance="fill">
<mat-label>Cluster Answer</mat-label>
<textarea
matInput
[id]="item['id']"
rows="7"
placeholder="Cluster answer"
[(ngModel)]="model.answer"
></textarea>
<mat-hint>Define the cluster answer to be suggested</mat-hint>
</mat-form-field>
You need to provide different name attribute value for each individual iteration because the ngModel value uses the value of the name attribute to connect the new control with the provided name to the form.
In your case it's not even necessary to use two way data binding (the bananna in a box [(ngModel)]) because you already have the state of the form inside the form itself so you can just use the [ngModel] to bind the value if there is one and whenever you want to get the new values you can either use #ViewChild(NgForm) form: NgFrom in order to get the form from your view (probably its going to be better to query it using a template variable name. It will look something like <form #f="ngForm"> where f is your template variable. Also check out the View Child Decorator) or inside the ngSubmit event of your form you can provide the value of the form using a template variable. <form #f="ngForm" (ngSubmit)="submitHandler(f.value)">.
I don't recommend using the two way data binding because what happens is that we store the same values at two different locations:
the state of each individual ngModel control that we have
inside the state your component.
Also formControlName directive comes from the Reactive-Forms and it's not good to mix both types of forms inside a single form.
<mat-form-field *ngFor="let item of model.answers; let i = index;" class="example-full-width" appearance="fill">
<mat-label>Cluster Answer</mat-label>
<textarea
matInput
[id]="item['id']"
rows="7"
placeholder="Cluster answer"
[ngModel]="model?.answers?[i]?['answer']"
[name]="'clusterAnswer' + i"
></textarea>
<mat-hint>Define the cluster answer to be suggested</mat-hint>
</mat-form-field>

how can I validate with vuejs fields that are prepopulated from database?

I want to validate the inputs from a form in vuejs, and when the data is pre-populated I want the inputs to convert to readonly.
<input type="number" name="cf_962" class="form-control" v-model="fillProfile.cf_962" step="0.1" :readonly="(fillProfile.cf_962>0.00) ? true : false">
the problem with this now is that always that i writed on the input if the value is higher than 0 the input is readonly and i dont want that.. how can do that with vuejs 2?.. thank you.
What you are trying achieve can be done easily using v-once directive
<input type="number" name="cf_962" class="form-control" v-model="fillProfile.cf_962" step="0.1" :readonly="(fillProfile.cf_962>0.00) ? true : false" v-once>
As mentioned in docs
You can also perform one-time interpolations that do not update on
data change by using the v-once directive, but keep in mind this will
also affect any other bindings on the same node
v-once will let you set readonly for the inputs having pre populated data, and not readonly for the inputs which are not pre populated. This thing will happen only once so next time you write on the input it will won't affect the readonly anymore.

How to bind index to value attribute in vuejs

I want to bind an index to the value attribute of the hidden field.
I am trying this:
<div id="panel" class="panel panel-default mt" v-for="(question,index) in questions.slice().reverse()">
<input type="hidden" v-model="question.question_no" :value="index">
</div>
it is showing me this error:
what is the correct way to bind value attribute with an index?
V-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.
So
<input type="hidden" v-model="question.question_no">
is same as
<input type="hidden" :value="index" #input="question.question_no=$event.target.question.question_no">
So you can't use both the syntax together.
Either use first one or second one according to your use case.
Edit
As input type is hidden
<input type="hidden" :value="index">
should be enough.
Reference

Angular1: How to reflect ng-model errors on another element?

I have a directive with the following template
<div>
<span class="label">My Label</span>
<input ng-model="name" required>
</div>
I want the label to be painted red when the input field is invalid.
How can I do that?
Currently I have another directive to sync all the errors from ngModelCtrl to the wrapping div
<div add-all-errors>
...
</div>
And the directive's link function does something like this:
const ngmodel = $element.find('[ng-model]').controller('ngModel');
$scope.$watch(()=>ngmodel.$error, addAllClasses, true);
Where addAllClasses simply makes sure the correct classes appear on the element..
I also tried just adding the same ng-model
<div ng-model="name">
...
</div>
But did not see the classes there..
any better way to do this?
This is why we use the angularjs form... I'm really not sure why people are against using a very handy feature.
I've made a plunker for you.
https://plnkr.co/edit/bGOcQjWzlRq2aTYZUYNm?p=preview
<form name="form">
<span ng-class="{red: form.name.$invalid}">Name:</span>
<input name="name" ng-model="name" required>
</form>
A little more insight of what's going on. form is added to the scope auto magically by angularjs by it's name. In this case, I named it form, however it can be any name.
Now form is an ngForm Object and adds all input field into it by their name attributes. This way we can do form.name to get another object similar to the ngForm Object. We can then use $invalid or $valid properties with ng-class.
ngForm is pretty powerful and is loaded with many cool properties and methods. Just call console.log(scope.form); You will need to put in a method and add it to ng-change to see updates.

Angular validators and ng-maxlength use

I've got the following div, which I want to add the bootstrap's class "has-error" if the input length is over 50 characters. This is the HTML:
<div class="form-group" ng-class="{has-error:[formData.titulo.$error]}">
<label for="inputTitulo">Título</label>
<input type="titulo" class="form-control" id="inputTitulo"
maxlength="50" ng-maxlength="50" ng-model="formData.titulo">
</div>
How can I make this work? I guess when you reach 50 characters, ng-maxlength throws a error, like the $error object, but I have no clue on what object is, how to access it, and if I have to do some more work in the controller or directive.
Any help here? I can't find any "easy" info regarding this issue and Angular validators.
edit 1:
I've seen all your responses, learned something new thanks to you, but this is still somehow not working. It currently is this way:
<div class="form-group" ng-class="{'has-error': formData.titulo.$error.maxlength}">
<label for="inputTitulo">Título</label>
<input type="titulo" class="form-control" id="inputTitulo" maxlength="50" ng-maxlength="50" ng-model="formData.titulo">
</div>
Also tested checking the length directly, as one of you suggested. But none of these solutions seem to work: it never adds the has-error class. Why?
To have the errors published on the scope, a form directive is required.
<div ng-form="form1" ng-class="{'has-error': form1.text1.$error.maxlength}">
<input name="text1" ng-model="formData.foo" ng-maxlength="50">
</div>
(Notice that the above uses the name attribute of the input to publish the form data - really, the ngModelController - on the scope)
So, the above works, and it's preferable if you do form validation. But, if you just need to check the length of some input, you don't have to use form validation - you could just check the model directly:
<div ng-class="{'has-error': formData.foo.length > 50}>
<input ng-model="formData.foo">
</div>
as you are using ng-model to make validations ,, this class ng-invalid will be added to your input
docs : https://docs.angularjs.org/api/ng/directive/ngModel
to use $error you need to access it using forms and names not ng-model ,, and the ng-class should be bound to the $error.maxlength not $error only
tutorial : https://scotch.io/tutorials/angularjs-form-validation
If you use the maxlength, a user will never be able to enter more characters than that, so you will never get the ng-maxlength error. It doesn't make sense to use maxlength and ngMaxlength together IMHO.
See the example on docs.angularjs.org/api/ng/directive/ngMaxlength (open the example in plunker and add maxlength attribute)

Categories

Resources