Here is the mock data array i am looping over in my app.component.html
questions: [
{question: 'Question-1'},
{question: 'Question-2'},
{{question: 'Question-3'}
]
In my app.component.ts as follows, Since i am using reactive form approach i have instantiated the Form Group and declare a formControl named 'note' which is declared as required:
export class example {
regPresentationForm: FormGroup;
this.regPresentationForm = new FormGroup ({
note: new FormControl(null, Validators.required),
});
}
In my component HTML i am looping over the array and displaying the text field, therefore will have three text boxes each with note as my formControl:
<ng-container *ngFor = "let question of questions">
<textarea type="text" id="textArea" placeholder="Enter the Note" class="form-control" formControlName = "note">
</textarea>
</ng-container>
Since the note formcontrol is required i am disabling the submit button like this:
<button class="btn btn-primary" type="submit"[disabled] = "!regPresentationForm.valid">
Save & Continue
</button>
PROBLEM:
The problem is the form get valid if i enter a text on the first text box therefore angular is considering it as a single box repeated thrice, which should not happen it should consider each note field seperately (3 instance of note formcontrol since the iteration is done thrice)
No idea how to figure this out need helps.
I hope i am clear.
You can loop formControl like this :
On ts side:
form2: FormGroup;
testData:any;
formDataInfo: any;
constructor() { }
ngOnInit() {
this.testData = [];
this.formDataInfo = {};
for(var i=0; i<2; i++){
var currentNote = 'note' + i;
this.formDataInfo[currentNote] = new FormControl(null,
Validators.required);
this.testData.push({ "value": currentNote, "key": "firstName1",
"label": "First name",
"required": true, "order": 1, "controlType": "textbox",
"type": "text"
});
}
this.form2 = new FormGroup (
this.formDataInfo
);
}
On HTML side:
<div [formGroup]="form2">
<ng-container *ngFor="let question of testData" class="form-row">
{{question | json}} {{question.key}}
<textarea type="text" placeholder="Enter the Note"
class="form-control" [formControlName]="question.value"
[required]="question.required">
</textarea>
</ng-container>
</div>
Related
i want to show delete button for each comment that a particular user made, but i am unable to do it, i am using v-bind to disable delete buttton for others comment, so that user cant delete others comment, but its still visible for all the comments i.e (for other users as well ). suppose i am a user i comment 3 times then delete button should show on my comments only not on others comment. can some one please help me to achieve this?
My code snippet is below
<div class="comment-list" v-for="comment in comments" :key="comment._id">
<div class="paragraph" :class="[name, onCaretButtonClick ? 'paragraph' : 'hide']">
<span class="names" id="comment-desc">
<label class="comm-name">{{ comment.name }}</label>
<button class="like-btn" id="like-comment"><i class="fas fa-heart"></i></button>
<label> {{ comment.likes + ' likes' }} </label>
<button v-bind:disabled="isCommentIdMatched" v-on:click="deleteComment(comment.userId)"
class="del-btn">delete</button>
</span>
<p>{{ comment.comment }}</p>
</div>
</div>
below is deleteComment function and computed properties that i am using
computed: {
comments() {
return store.state.comments
},
getUserId() {
return store.state.user._id
},
isCommentIdMatched() {
let comments = store.state.comments
let value = false
if(comments) {
comments.forEach((comment) => {
if(comment.userId.match(this.getUserId)) {
value = true
}
})
return value
}
else {
return value
}
}
},
methods: {
deleteComment: function(commentUserId) {
let userId = store.state.user._id
if(commentUserId.match(userId)) {
console.log('yes matched')
}
},
}
there is no need to write isCommentIdMatched property, instead you can use change some lines.
add v-bind="comments" in below line
<div v-bind="comments" class="comment-list" v-for="comment in comments" :key="comment._id">
and add v-if="comment.userId && comment.userId.match(getUserId)" in below button
<button v-if="comment.userId && comment.userId.match(getUserId)" v-on:click="deleteComment(comment.userId)" class="del-btn">delete</button>
JSBin and Stackoverflow snippet are below.
I am trying to have a list of input components. If all input components are filled with a value (not blank), then there should be a "new blank field" visible at the end for the user to type into. When he types into it, it should make this field apart of the list above it, maintaining focus in it.
However the problem I'm having is, focus maintains in the new field, and never moves into the array. Here is my code:
JSBIN and stackoverflow snippet - https://jsbin.com/cudabicese/1/edit?html,js,output
const app = new Vue({
el: '#app',
data: {
inputs: [
{ id:'foo', value:'foo' },
{ id:'bar', value:'bar' }
]
},
methods: {
addRow(e) {
this.inputs.push({
id: Date.now(),
value: e.target.value
})
},
deleteRow(index) {
this.inputs.splice(index, 1)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<div id="app">
<ul>
<li v-for="(input, index) of inputs">
<input type="text" v-model="input.value">
</li>
<li v-if="inputs.filter(input => !!input.value).length">
<input type="text" #input="addRow">
</li>
</ul>
</div>
I'd recommend you put the input for the list within a computed function vs directly using the data. The examples at https://v2.vuejs.org/v2/examples/ are a good place to start.
I am using v-for with a custom component
Vue.component('lineitem', {
template: '#lineitem-template',
props: {
item: {
required: false,
default: null
},
},
computed: {
local_line_item() {
console.log("Re computing line item")
return _.clone(this.item)
},
},
methods: {
set_taxable(e) {
e.preventDefault();
if (this.local_line_item.taxable == false) {
this.local_line_item.taxable = true;
} else {
this.local_line_item.taxable = false;
}
console.log("Changing taxable to ", this.local_line_item);
},
}
});
with this template:
<template id="lineitem-template">
<tr>
<td>
<input type="text" class="form-control" v-model="local_line_item.cost">
</td>
<td>
<div class="option" #click="set_taxable">
<input type="checkbox" v-model="local_line_item.taxable" v-bind:true-value="true" v-bind:false-value="false">
<label for="check1"></label>
</div>
</td>
</tr>
</template>
see this JSFiddle: https://jsfiddle.net/shaunc869/1Ly7mL6n/7/
When I click the checkbox I am changing the value of the internal variable, but the checkbox doesn't change, what am I doing wrong? Thanks!
e.preventDefault() inside set_taxable is the cause of problem. Removing that part works just fine. You can see the updated fiddle here: https://jsfiddle.net/1Ly7mL6n/9/
Also, I don't understand your rationale behind using the click binding to trigger set_taxable. Since you have already used v-model binding to toggle local_line_item.taxable in accordance with the checked state of the checkbox, you do not need a click handler for it. So unless you plan to do some more operations in the click handler, you could remove that as well.
I'm trying to build
An application that renders a form, where the default input values are equal to the data from the store.
When the save button is clicked, the state will be updated according to the new data added to the view by the user.
Currently the inputs are bound to the store data, and so I have no reference to the "live" value of the inputs. When the user clicks save, how do I grab the "live" values?
Component Template
<input type="text" class="form-control" :value="item.name">
<input type="text" class="form-control" :value="item.price">
<button class="btn btn-primary" v-on:click="updateItem(item)">Save</button>
Component
data: function() {
return {}
},
methods: {
updateItem(item) {
this.$store.commit('updateItem', item);
},
},
computed: {
items() {
return this.$store.getters.getItem;
}
}
Potential Solutions
I thought I could perhaps create a "clone" of the store, and bind the inputs to the cloned item data. Then this object will be updated as the view changes, and so I can grab those "live" values, and commit the data from the view to the store. Is this a good solution?
If you wanted to update without the user having to click the button, then I would suggest one of the methods explained in the docs.
But since you want to do it wen they click the button, try something like this:
<template>
<form>
<input type="text" class="form-control" v-model="item.name">
<input type="text" class="form-control" v-model="item.price">
<button class="btn btn-primary" #click.prevent="updateItem">Save</button>
</form>
</template>
<script>
export default {
data() {
return {
item: {
name: null,
price: null,
}
}
},
mounted() {
// Set the default value of this.item based on what's in the store
this.item = this.$store.getters.getItem
},
methods: {
updateItem() {
this.$store.commit('updateItem', this.item);
}
}
}
</script>
I want to do a classic form submission from my Vue page, from a method. I don't want to use an <input type="submit">. How do I reference the form element in the page from my method? Surely I don't have to do document.getElementById() ?
markup
<div id="vueRoot">
<form>
<input name="vitalInformation" v-model="store.vital">
SUBMIT
</form>
</div>
code
var store = {vital:''};
vm = new Vue({
el : "#vueRoot",
data : {store : store},
methods : {
submit : function(){
//I'm ready, how do I do it ??
}
}
});
jsfiddle
The answer would be: ref: https://v2.vuejs.org/v2/api/#ref
Markup
<div id="vueRoot">
<form ref="form">
<input name="vitalInformation" v-model="store.vital">
SUBMIT
</form>
</div>
code
var store = {vital:''};
vm = new Vue({
el : "#vueRoot",
data : {store : store},
methods : {
submit : function(){
this.$refs.form.$el.submit()
}
}
});
Sorry for the late reply, but I am confused why you need ref when submitting a form.
data: function(){
return {
name: "",
price: "",
}
},
methods: {
addProduct(e){
e.preventDefault() // Prevent page from reloading.
// console.log(this.name, this.price);
}
}
<form v-on:submit="addProduct">
<input type="text" v-model="name" placeholder="Product Name" >
<input type="number" v-model="price" placeholder="Price">
<button type="submit">Add</button>
</form>
Another option is trigger the event click().
<button ref="submit" style="display:none;">Submit</button>
In your function call it as follows:
this.$refs.submit.click();
From above answers I used following statements
this.$refs.form.submit()
this.$refs.submit.click();
both throw following error
TypeError: Cannot read property '$refs' of null
I'm able to fix this issue by storing $refs in a variable then access the relevant method
if you want to use ref for form submit event
const form = this.$refs.formRef
form.submit()
if you want to use ref for other element's event like click
const btn = this.$refs.btnRef
btn.click()
In the case where it says that submit is not a function
It looks like if a form control has a name or id of submit it will mask the form's submit method.
In my case I had name="submit" on my submit button, I removed it and it worked fine.