VueJs v-Model Binding with Hidden Component not working - vue.js

I am developing a SPA application in Vue. I have a wizard in the wizard I have a component for each step. All the components are adding at the start and their mounted and created method/event is executing when the application starts.
I am using the event bus & when I move to the next step of wizard I emit the event and catch the event on the respective step.
Note: Component is hidden at the start and when it shows it will not have the updated data or even data in input fields.
Code of the component of the 2nd step is
<template>
<section>
<div class="container">
<form class="form-horizontal">
<div class="row form-group">
<label class="col-sm-3 control-label" for="marshaCode">MARSHA Code:</label>
<div class="col-sm-4">
<input type="text" class="form-control field-ml-15" v-model="marshaCode" placeholder="Enter Marsha Code" readonly />
<input type="text" class="form-control field-ml-15":value="marshaCode" placeholder="Enter Marsha Code" readonly />
<p>{{marshaCode}}</p>
</div>
</div>
</form>
</div>
</section>
</template>
<script>
import { EventBus } from "../../shared/eventbus.js";
export default {
data() {
return {
marshaCode:"On Load Code"
}
},
mounted() {
EventBus.$on('showSurvey', () => {
this.marshaCode="On Show Code"
});
},
}
</script>
Now when this component will show the P tag will have "On Load Code" But in both the fields there will be no data.
How can I rebind, re-render or update the data?

Use vue to show/hide the component using v-if or v-show. It can't do it's job if another library is messing with the DOM.
<section v-if='showMyComponent'>
or
<div class="col-sm-4" v-if='showMyComponent'>
<input type="text" class="form-control field-ml-15" v-model="marshaCode" placeholder="Enter Marsha Code" readonly />
<input type="text" class="form-control field-ml-15":value="marshaCode" placeholder="Enter Marsha Code" readonly />
<p>{{marshaCode}}</p>
</div>
And set showMyComponent to a computed or data property. Then remove the logic to show/hide from your other library.

You can simply add this.marshaCode="On Show Code" when mounted.
mounted() {
this.marshaCode="On Show Code"
console.log(EventBus)
EventBus.$on('showSurvey', () => {
this.marshaCode="On Show Code"
});
}
An example: https://codesandbox.io/embed/vue-template-8l2lg.

Related

Laravel + vue: Error message only showing the first letter of the sentence

I'm just starting to learn laravel+vue. I was able to follow a tutorial from this yt: https://www.youtube.com/watch?v=JZDmBWRPWlw. Though it seems outdated, I was still able to follow his steps. I'm using the laravel-mix 6.0.6 and vue 2.6.12.
Using inspect element>network, I can see that I'm throwing the correct error message in array.
{"component":"Users\/Create","props":{"app":{"name":"Laravel"},"errors":{"name":"The name field is required.","email":"The email field is required."}},"url":"\/users\/create","version":"207fd484b7c2ceeff7800b8c8a11b3b6"}
But somehow it is not displaying the complete error message. Right now it just show the first letter of the sentence. LOL. Sample error message is: The email field is required and it will just display the letter "T". Below is my Create.vue. Basically it is just a user create form with simple validation.
Create.vue
<template>
<layout>
<div class="container">
<div class="col-md-6">
<div v-if="Object.keys(errors).length > 0" class="alert alert-danger mt-4">
{{ errors[Object.keys(errors)[0]][0] }}
</div>
<form action="/users" method="POST" class="my-5" #submit.prevent="createUser">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" placeholder="Name" v-model="form.name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" class="form-control" id="email" placeholder="Email" v-model="form.email">
</div>
<div class="form-group">
<label for="name">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password" v-model="form.password">
</div>
<button type="submit" class="btn btn-primary">Create User</button>
</form>
</div>
</div>
</layout>
</template>
<script>
import Layout from '../../Shared/Layout'
export default {
props: ['errors'],
components: {
Layout,
},
data() {
return {
form: {
name: '',
email: '',
password: '',
}
}
},
methods: {
createUser() {
this.$inertia.post('/users', this.form)
.then(() => {
// code
})
}
}
}
</script>
Edit:
I have this error on my console
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property
'then' of undefined"
found in
---> at resources/js/Pages/Users/Create.vue
Your error call is probably getting only the first letter due to [0]. Try to change to:
{{ errors[Object.keys(errors)[0]] }}
Strings can also be read as arrays. If you do this:
$a = "TEST";
echo $a[0];
That would print only T.
That is probably the problem.

Form with radio buttons not submitted

I have a form with two radio buttons which is enabled only when a checkbox is checked.
My problem is when I check the checkbox and click the submit button, the radio value is not getting posted. But after I click on the checkbox and then click on one of the radio buttons then the value is posted.
How to fix this issue?
This is the code I have tried:
HTML
<form [formGroup]="filterProductTargetForm" (ngSubmit)="onSubmitFilterDataList(filterProductTargetForm.value)">
<div class="row">
<div class="col-md-10">
<input type="checkbox" [ngModel]="isProductTypeChecked" formControlName="checkProductType" (change)="onProductTypeChange($event)" />
<label>Select A or B</label>
</div>
</div>
<div class="row">
<label class="col-md-2 uni-label"></label>
<div class="col-md-10 prduct-type-radio">
<fieldset [disabled]="!isProductTypeChecked">
<input type="radio" [checked]="isProductTypeChecked == true" value="A" formControlName="productTypeSelected" [(ngModel)]="productTypeSelected">
<span>B</span>
<br>
<input type="radio" value="B" formControlName="productTypeSelected" [(ngModel)]="productTypeSelected">
<span>B</span>
</fieldset>
</div>
</div>
<button class="uni-button def" [disabled]="!filterProductTargetForm.valid">OK</button>
</form>
TS
ngOnInit() {
this.filterProductTargetForm = this.formBuilder.group({
'checkProductType': '',
'productTypeSelected': ''
});
}
public filterProductTargetForm: FormGroup;
public isProductTypeChecked = false;
onProductTypeChange(event: any) {
this.isProductTypeChecked = !this.isProductTypeChecked;
if(!this.isProductTypeChecked)
this.filterProductTargetForm.controls['productTypeSelected'].reset();
}
First remove all ngModel from your template when using reactive forms.
When the checkbox value changes, in your onProductTypeChange function set the productTypeSelected value
this.filterProductTargetForm.controls['productTypeSelected'].setValue('A');
Working StackBlitz DEMO

Refresh v-model

I'm developing question paper application.
Once I type a question and hit the "+" button, the question goes to the question paper array and counter increased by one.
The problem is after I hit the "+" button, then also the question which I have entered previously is still in the fields of the UI. Because of I use v-model to bind the data fields.
What I want is a method to clear those previous question data in UI.
This is something similar to reset button function.
<template>
<div>
<div class="container" v-if="counter<=5">
<h2>Question {{counter}}</h2><hr>
<textarea rows="7" cols="75" v-model="question"></textarea><br><br>
1. Answer <input type="text" v-model="answer1"> <input type="radio" name="q1answer" value="1" v-model="correctAnswer"><br><br>
2. Answer <input type="text" v-model="answer2"> <input type="radio" name="q1answer" value="2" v-model="correctAnswer"><br><br>
3. Answer <input type="text" v-model="answer3"> <input type="radio" name="q1answer" value="3" v-model="correctAnswer"><br><br>
4. Answer <input type="text" v-model="answer4"> <input type="radio" name="q1answer" value="4" v-model="correctAnswer"><br>
<hr>
Knowledge Area <select v-model="knowledgeArea">
<option value="Maths">Mathematics</option>
<option value="Language">Language Skills</option>
<option value="gk">General Knowledge</option>
<option value="other">Other</option>
</select><br><br>
<button type="button" class="btn" #click="pushToArray" >
<span class="glyphicon glyphicon-plus"></span></button>
</div>
<div v-if="counter>5">
<button type="button" class="btn btn-primary" #click="onSubmit">Save Question Paper</button>
</div>
</div>
</template>
<script>
import axios from 'axios';
var questionPaper = [];
export default {
data () {
return {
question:'',
answer1:'',
answer2:'',
answer3:'',
answer4:'',
correctAnswer:'',
knowledgeArea:'',
counter:1,
show:true
}
},
methods: {
onSubmit () {
},
pushToArray(){
const formData = {
question: this.question,
correctAnswer: this.correctAnswer,
answer1: this.answer1,
answer2: this.answer2,
answer3: this.answer3,
answer4: this.answer4,
knowledgeArea:this.knowledgeArea
}
this.counter++;
questionPaper.push(formData);
}
}
}
</script>
Create a template data variable to use as a reset. For example
const templateData = {
question:'',
answer1:'',
answer2:'',
answer3:'',
answer4:'',
correctAnswer:'',
knowledgeArea:''
}
export default { // ...
use that to set your initial data
data() {
return {
counter: 1,
show: true,
...templateData
}
}
Now you can easily reset your data in the pushToArray method, eg
questionPaper.push(formData);
Object.assign(this, templateData);

Vue 2 - use same form for adding new and editing

I have following template:
<template>
<div>
<form #submit="save">
<div class="field">
<label class="label">Name</label>
<div class="control">
<input class="input" type="text" placeholder="Name" :value="book.title">
</div>
</div>
<div class="field">
<label class="label">Name</label>
<div class="control">
<input class="input" type="text" placeholder="Name" :value="book.author">
</div>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
book : {}
}
},
methods: {
save() {
}
},
created() {
if(this.$store.state.book != 'undefined'){
this.book = this.$store.state.book;
}
},
computed: {}
}
</script>
<style></style>
So far everything works fine if the book is pass with the this.$store.state.book, but if this is not passed the form is failing, with the error message:
** Error in render function: "TypeError: Cannot read property 'title' of undefined"**
I thought that passing the empty object would dynamically bind the book object and auto create the params.
Is it possible to use the same form for both adding new and editing?
I tried your code, and all you have to do is remove the 'quotes' from 'undefined'. Obviously, it's a string as is, and not a js thing.

Vue Validator only after change / blur / submit

I'm using Vue for the first time, with Vue Validator. Here is an example of my code:
<label for="first_name">First name:
<span v-if="$validation1.first_name.required" class="invalid">Enter your first name.</span>
<input id="first_name" placeholder="e.g. Christopher" class="" v-validate:first_name="['required']" v-model="first_name" name="first_name" type="text">
</label>
The only issue at the moment is that when I land on the page with my form, the whole thing is covered in errors. Is there a way I can suppress the errors and only show them on input blur / form submit?
Argh, the Google-able word isn't about blur, or on submit – its about timing and initial:
http://vuejs.github.io/vue-validator/en/timing.html
<input id="first_name" initial="off" placeholder="e.g. Christopher" class="" v-validate:first_name="['required']" v-model="first_name" name="first_name" type="text">
you need to add .dirty or .touched to your validation
<label for="first_name">First name:
<span v-if="$validation1.first_name.required && $validation1.first_name.touched" class="invalid">Enter your first name.</span>
<input id="first_name" placeholder="e.g. Christopher" class="" v-validate:first_name="['required']" v-model="first_name" name="first_name" type="text">
</label>
I was dealing with a similar problem. I had to have an initialized variable for the input name: "" but I also wanted to have a required attribute in element.
So I add required when the event onblur occurs.
<input name="name" type="number" v-model="name" #blur="addRequired" />
const app = Vue.createApp({
data() {
return {
name: ""
}
},
methods:{
addRequired: function(event){
event.target.setAttribute("required", true);
}
}
});