VueJS: input with dynamic value + v-model - javascript

I have a problem with VueJS when setting the value of an input radio AND v-model. I cant understand why I can not setting dynamically a value to an input and use a model to retrive what input the user selected.
In code is easier to understand:
export default {
props: ["question", "currentQuestion"],
data() {
return {
answer: undefined
}
},
computed: {
isCurrent() {
return this.currentQuestion && this.currentQuestion.id == this.question.id;
}
},
methods: {
groupName(question) {
return 'question_id_' + question.id
},
inputType(question) {
if (question.question_type_id == 2) return "checkbox";
return "radio";
}
},
mounted() {
console.log('Component mounted.')
}
}
<template>
<ul v-if="question.question_type_id != 4">
<li v-for="option in question.options" :key="option.id">
<div class="form-group">
<label>
<input v-model="answer" :value="option.id" :type="inputType(question)"
:name="groupName(question)" />
{{option.option}}
</label>
</div>
</li>
</ul>
</template>
So, in case there are 4 options, the user would see 4 radio buttons, each one with the "option.id" as a value and, WHEN the user clicks the radio button, the model "answer" would be populated with that value.
Now, when I try to compile this file, I get this error message:
:value="option.id" conflicts with v-model on the same element because the latter already expands to a value binding internally
So, could anyone help me understand how do I dynamically set a value to an input AND asssociate a model to retrieve the value when the input is selected?
By the way, VueJS documentation at this page says exactly what I am trying to do:
https://v2.vuejs.org/v2/guide/forms.html
Any help is very appreciated.

The main issue here is that you have a dynamic type on the input element, so I expect Vue is getting a little confused. value is not valid in combination with v-model for a checkbox input, unless you are binding to an array.
You can solve that by using a v-if/v-else.
<label>
<input v-if="question.question_type_id == 2"
v-model="answer"
type="checkbox"
:name="groupName(question)" />
<input v-else
v-model="answer"
:value="option.id"
type="radio"
:name="groupName(question)" />
{{option.option}}
</label>
Working example.
There are other issues, but this addresses the error you mention in the question. For one, it doesn't make much sense for more than one checkbox to be bound to a single v-model, unless you are binding to an array. In that case, you would have to swap what type answer is based on whether is a radio, a checkbox with a single value or a checkbox with multiple values. Seems like it would get complicated.

the proper way is to use placeholder.
<input :placeholder="option.id" v-model="answer" #input="functionToChangeValue($event)"/>
! DO NOT USE VALUE AND V-MODEL TOGETHER
it is because v-model create two way biding and your code brake

Related

Angular Reactive form: Unable to bind checkbox checked property on page load

In Angular Reactive forms i want Two-way data binding of Checkbox, after checking it i am dispatching check variable it to the store and after page load i am getting the new updated value also. But that checkbox isdateChkd variable even though its coming TRUE checkbox is not getting auto checked on page load.
Below are both HTML and ts files..
HTML File
<form [formGroup]="form">
<div class="abc" formControlName="inputDate">
<div class="xyz">
<p-calendar formControlName="inputDate"
[(ngModel)]="inputDateValue"
class="abc"/>
<input type="checkbox" id="checkbox-1" class="abc" name="dateChk"
name="dateIsChecked" [(ngModel)]="isdateChkd" (change)="chkFunction($event.target.value)" />
</div>
</div>
</form>
Component.ts file
public isdateChkd: boolean = false;
ngOnInit() {
....Other service calls
// Call to get isdateChkd value
this.store.select(getValues).takeUntil(this.ngUnsubscribe).subscribe((data) => {
this.isdateChkd = data.defaultUpdate // this value holds boolean to be used
});
}
chkFunction(valule) {
this.isdateChkd = value; // i get update when checkbox is checked
this.store.dispatch(new updateCheckboxCheckedService(value)); // after its checked dispatching it to the store
}
Thanks in advance for help...
You can use checked attribute along with NgModel.
It's working here with checked along with the value isdateChkd. You can also check here in this stackBlitz
Please try to make changes like below code
...
<input type="checkbox" id="checkbox-1" class="abc" name="dateIsChecked"
[(ngModel)]="isdateChkd" (change)="chkFunction($event)" checked="isdateChkd" />
...
Happy Coding.. :)

Dynamically created input of type checkbox doesn't properly bind value through NgModel

I've got a dynamically created form, wrapped in a table, that appears when I click in an "edit" button of that same row. There are a lot of conditionals inside that dynamic table that when the row is being edited, they show some inputs.
The type, and the binding of those inputs, are all dynamic. Let's check this one:
<td *ngIf="table?.getInputType(column.key) && table?.isInputActive(column.key, rowIndex) && column.key != table?.buttonsColumn">
<input *ngIf="table?.getInputType(column.key) != 'select' && column.key != table?.buttonsColumn"
[type]="table?.getInputType(column.key)"
[(ngModel)]="sortedValue.referenceObject[column.key]">
This binding works perfect for both selects (which are not included in this snippet), and text fields. But it doesn't bind correctly for checkbox inputs. It doesn't really get the actual value inside the given object, therefore the checkbox is always as "false" (though the value is at "true" sometimes). Consequently, ticking the box and saving the result won't generate any change.
The reference you can see inside the NgModel is perfectly done; I already checked it and the names involved in this key-value object are correctly put. The problem is somewhere else, but I don't know where.
Any help?
This case is discussed in the issue 7329 on GitHub, and is considered "a limitation of the framework" (the issue is closed). One workaround mentioned in the discussion is to use conditional directives. See this stackblitz for a demo.
<ng-container [ngSwitch]="inputType">
<input *ngSwitchCase="'checkbox'" type="checkbox" [(ngModel)]="value">
<input *ngSwitchCase="'password'" type="password" [(ngModel)]="value">
<input *ngSwitchCase="'email'" type="email" [(ngModel)]="value">
<input *ngSwitchDefault type="text" [(ngModel)]="value">
</ng-container>
or
<input *ngIf="inputType === 'checkbox'" type="checkbox" [(ngModel)]="value">
<input *ngIf="inputType !== 'checkbox'" type="text" [(ngModel)]="value">
You can try giving an 'id' to each checkbox. I have used rowIndexas id in the below example. You can use it or give some other unique value as id.
<td *ngIf="table?.getInputType(column.key) && table?.isInputActive(column.key, rowIndex) && column.key != table?.buttonsColumn">
<input *ngIf="table?.getInputType(column.key) != 'select' && column.key != table?.buttonsColumn"
[id]="rowIndex" [type]="table?.getInputType(column.key)"
[(ngModel)]="sortedValue.referenceObject[column.key]">

Angular 4 - How to calculate price based on change in quantity and option

Requirement
There are a list of items in the shopping cart. Each item has option, quantity, price. Need to calculate the price based on change in quantity and option of a item.
Research
I had added [(ngModel)] as below, so as to use 2 way data-binding, however, no idea why the page simply keeps on loading and than crashes without any error.
When i remove the ngModel attribute the page loads successfully.
<span class="price col-1 pull-right" >{{ orderType*price}}</span>
<div class="qty" style="margin:0;">
<span class="spinner">
<span class="sub">-</span>
<input type="number" id="qty" name="qty" min="1" max="100"
value="1" class="qty-spinner" [(ngModel)]="quantity" />
<span class="add">+</span>
</span>
</div>
<div class="type">
<label class="radio inline" *ngFor="let option of item.options; let k = index">
<input id="{{'toggle-half-'+item.id+'-'+j}}" type="radio"
name="{{'option-toggle-'+item.id+'-'+j}}" [value]="option.value"
[checked]='k===0' [(ngModel)]="orderType" />
<span>{{option.title}} </span>
</label>
</div>
TypeScript Code
quantity:number;
orderType:number;
price:number;
constructor() {
this.quantity = 1;
this.price = 0;
}
Controls used
1) Number Control input of type number for quantity.
2) Radio Control for Options [Half/Full]
3) span element for printing calculated price.
Expected Output
Price which is currently set to 0 should get updated when we change the quantity or the option.
Eg. Value set for option [Half] = 10
Eg. Value set for option [Full] = 20
Please advise how can i implement this, is the above logic and implmentation correct ?
Thanks in advance
The variable option is already in use in the ngFor directive. See line:
<label class="radio inline" *ngFor="let option of item.options; let k = index">
Use a different variable instead for the ngModel, example:
<input id="{{'toggle-half-'+item.id+'-'+j}}"
type="radio"
name="{{'option-toggle-'+item.id+'-'+j}}"
[value]="option.value"
[(ngModel)]="selectedOption" />
Look at the examples in the documentation;
EDIT
Also you have you're binding the checked attribute while imposing a ngModel. Instead of doing that, simply init the ngModel with the value you want, in your case,
selectedOption = item.options[0].value
Edit: Reviewing the documentation sample shared in jkris' answer, value makes sense for radio inputs. I still suspect checked creates an update loop when combined with [(ngModel)].
I notice you're binding checked in addition to the NgModel binding. If you remove it, does it keep the correct behavior and prevent crashing?
<input type="number" class="qty-spinner" min="1" max="100"
name="quantity" [(ngModel)]="quantity" />
<input type="radio"
[value]="option.value" name="option" [(ngModel)]="option" />
Otherwise, another option is to use [checked] and (click)(or similar) bindings in place of NgModel.
Not sure why it crashed but if i were you. The constructor
constructor() {
this.quantity = 1;
this.price = 0;
}
I will use ngInit instead since its a two way binding.
import { OnInit } from '#angular/core';
export class myClas implements OnInit {
ngOnInit() {
this.quantity = 1;
this.price = 0;
}
}
Secondly, you have value in your radio button and input type number. Are you sure they return value of type number ? If i were you i will use or try string first and cast it to my desired number type afterward. Or use a pipe. Wrong type conversion can be expensive sometimes . So can you try
quantity:string;
orderType:string;
price:string;
first and do your number conversion afterward ?
the issue is resolved now.
The code snippet in the description was implemented between a div tag which had a *ngFor directive, that iterated through the list of items.
Issue
For dividing the list of items into 2 columns "div.col-md-6", i had implemented a a function that returned me an array of 2 arrays each containing set of items, so as to iterate through each array and display the items in each column.
Could not find the issue, why there was an issue only when i implemented the ngModel tag, which should have not been the case. However, i removed that splitting function and everything worked fine for me.
Thanks to all for your efforts and help.

Angular2 ngModel binding in the third property level gets undefined

A weird thing is happening on my form or maybe i am not doing it right, let me explain to you by presenting my code.
i have defined a form object inside my component
form = {};
There is a button on each row with data that when you click it opens a modal widow and also passes the item as argument.
<a class="btn btn-warning" (click)="open(item)"><i class="glyphicon glyphicon-pencil"></i></a>
This is the method that fires and opens a modal window but also assign the item object to form object above:
open = (item: any) => {
this.inventoryEditModal.open(); //Opens a modal window
this.form = item; // the assignment
}
The item object is available on the view by printing it out like this:
{{ form | json }} // i can see all item properties
The modal window contains a form where user will edit the item, so basically the input form fields should get filled with item properties values but for some reason the third level is undefined and i don't understand why, let me show you an screenshot of the second level
<input type="text" class="form-control" [(ngModel)]="form.alarmSystem" name="wireless">
The third level gets undefined:
<input type="text" class="form-control" [(ngModel)]="form.alarmSystem.wireless" name="wireless">
This issue is happening only for the third level "object.object.property". I am only showing one input field but the form contains more than 8 fields they all have same issue.
Not sure what i am missing here, but logically it should work. Do you have see this issue happening here and there or experienced your self?
Thank you in advance.
I am not sure if it helps your case, but I was in a very similar situation.
What helped me out was using the "safe-navigation-operator".
I assume that what you need to do is just add the ? after form:
<input type="text" class="form-control" [(ngModel)]="form?.alarmSystem.wireless" name="wireless">
The docs can be found here: https://angular.io/docs/ts/latest/guide/template-syntax.html#!#safe-navigation-operator
There can be 3 possible solutions with Angular 5
Don't assign ngForm to the template variable of the form (in form HTML element)
Don't do this -
<form #newItem="ngForm" (ngSubmit)="saveItem(newItem.value);">
Instead, do this -
<form (ngSubmit)="saveItem();">
<input type="text" class="form-control" name="wireless [(ngModel)]="form.alarmSystem.wireless">
<submit type="submit">Save</submit>
</form>
By this, you will be able to assign a 3 level nested property to
[(ngModel)] without any elvis or safe navigation operator (?).
If you are assigning ngForm to the form template variable then [(ngModel)] will give undefined for a 3 level nested property even if it already has some value.
So, use the name attribute instead -
<form #newItem="ngForm" (ngSubmit)="saveItem(newItem.value);">
<input type="text" name="alarmSystem.wireless" ngModel class="form-control">
</form>
Now, in this case, the nested property alarmSystem.wireless assigned
to the name attribute will be bound 2-way using ngModel directive.
And you can easily access it in the Event emitter function.
And the last solution by using elvis operator (?) is -
Here, again we will not assign ngForm in the form template variable, although there will not be any error but it won't store the value entered in input correctly.
So, split 2-way data binding with [ngModel] and (ngModelChange) like this
<form (ngSubmit)="saveItem();">
<input type="text" name="wireless"
[ngModel]="form?.alarmSystem?.wireless"
(ngModelChange)="form.alarmSystem.wireless=$event" class="form-control">
</form>
Also you can refer to this answer - Correct use of Elvis operator in Angular2 for Dart component's view
But, I don't recommend this solution as it adds up a lot of code.

JQuery selecting radio button using a variable

I'm trying to select a radio button using a variable but, although I can print the selected value the button does not stay selected.
This button is defined in an angular template as follows:
<a ng-click="selectNode($event, node)">
<input type="radio" name="library" style="float:left;" id="id-{{n}}" />
</a>
Where id is a GUID so is definitely unique.
Then, because it is the angular click event that is called when a button is selected I call this code in selectNode:
context.selectedNodes = [node];
$scope.selectedNode = node.$model.name;
var id = '#id-' + node.$model.id;
//console.log(typeof id);
//console.log(id);
$(id).prop("checked", true);
alert($("[name=library]:checked").val())
Which should theoretically check the radio button. However, it checks for a moment (it is checked when I call the alert) and then seems to disappear. I am however able to hardcode in an id and it stays checked.
I recommend diving more into the tools angular gives you to handle this things simply. If you need to track the value on this input, tie it to a model. If you want to process something once the checkbox has changed, use ng-change and tie it to a function.
<input type="radio" name="library" style="float:left;" ng-model=libraryModel ng-change="doSomething()" />
Keep in mind that doSomething() needs to be in scope, so in your controller, you need to define the function as $scope.doSomething(). Your model will also be attached to the $scope object, so you'd reference it as $scope.libraryModel. You can initialize the model as true or false as needed for a default value in the controller, then any user changes will auto update this value.
You're mixing jQuery and Angularjs, and it is not really worth. You can do the following just by using AngularJS ngModel, if you use AngularJs in your project.
Controller
function Controller($scope) {
//Init data input to value 1
$scope.data = ['1'];
}
HTML
<input type="radio" name="name" value="1" ng-model="data">
<input type="radio" name="name" value="2" ng-model="data">
Here, our $scope.data is set to 1 in our Controller, so it will match with the first input.

Categories