input not beign validated by form - angular2-template

I created an input component to reuse it between a few forms. In one of then, it's working perfectly, but in the other, it's not.
It doesn't throw any erros. I even receive the input value after submit.
code.component.html
<div [ngClass]="aplicaCssErro(ag)">
<label for="code">Code</label>
<input id="code" name="code" type="text" class="form-control" [(ngModel)]="value" required #ag="ngModel"
maxlength="4" minlength="4" (blur)="formatCode(ag)">
<div *ngIf="verificaValidTouched(ag)" class="msgErroText">
<gce-campo-control-erro [mostrarErro]="ag?.errors?.required" msgErro="the code is required">
</gce-campo-control-erro>
</div>
code.component.ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'gce-input-code',
templateUrl: './input-code.component.html',
styleUrls: ['./input-code.component.scss']
})
export class InputCodeComponent implements OnInit {
#Input() value: string = "";
constructor() { }
ngOnInit() {
}
//some functions
}
form.component.html
The problem is that the form is not validating it, just the first input.
I think the form is not recognizing it as one of it's inputs.
<form (ngSubmit)="onSubmitForm2(f)" #f="ngForm">
<div class="row">
<div class="col-sm-6" [ngClass]="aplicaCssErro(apelido)">
<label for="apelido">Apelido da conta</label>
<input id="apelido" name="apelido" type="text" class="form-control" alt="Apelido" [(ngModel)]="conta.apelido" required #apelido="ngModel">
<div *ngIf="verificaValidTouched(apelido)" class="msgErroText">
<gce-campo-control-erro [mostrarErro]="apelido?.errors?.required" msgErro="O Apelido é obrigatório.">
</gce-campo-control-erro>
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-2">
<gce-input-code name="code" [(ngModel)]="user.code" #code="ngModel" ngDefaultControl></gce-input-code>
</div>
</div>
</div>
<div class="row">
<button class="btn btn-default" name="btn2" type="submit" alt="Continuar" [disabled]="!f.valid">Continue</button>
</div>
Any help?

If I understand your question correctly. You are trying to make it so the form(ngForm) can validate the custom component that wraps around the input(gce-input-code).
A normal form does not have any way to know what is going in/out of the component as it is Angular component. You would have to enhance your code.component.ts to include all the connectors (ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS) into it.
Checkout this blog
https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html#custom-form-control-considerations
and its plnkr(exerpt code below)
https://plnkr.co/edit/6xVdppNQoLcsXGMf7tph?p=info
import { Component, OnInit, forwardRef, Input, OnChanges } from '#angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS }
from '#angular/forms';
export function createCounterRangeValidator(maxValue, minValue) {
return (c: FormControl) => {
let err = {
rangeError: {
given: c.value,
max: maxValue || 10,
min: minValue || 0
}
};
return (c.value > +maxValue || c.value < +minValue) ? err: null;
}
}
#Component({
selector: 'counter-input',
template: `
<button (click)="increase()">+</button> {{counterValue}} <button (click)="decrease()">-</button>
`,
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CounterInputComponent), multi: true },
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => CounterInputComponent), multi: true }
]
})
export class CounterInputComponent implements ControlValueAccessor, OnChanges {
propagateChange:any = () => {};
validateFn:any = () => {};
#Input('counterValue') _counterValue = 0;
#Input() counterRangeMax;
#Input() counterRangeMin;
get counterValue() {
return this._counterValue;
}
set counterValue(val) {
this._counterValue = val;
this.propagateChange(val);
}
ngOnChanges(inputs) {
if (inputs.counterRangeMax || inputs.counterRangeMin) {
this.validateFn = createCounterRangeValidator(this.counterRangeMax, this.counterRangeMin);
this.propagateChange(this.counterValue);
}
}
writeValue(value) {
if (value) {
this.counterValue = value;
}
}
registerOnChange(fn) {
this.propagateChange = fn;
}
registerOnTouched() {}
increase() {
this.counterValue++;
}
decrease() {
this.counterValue--;
}
validate(c: FormControl) {
return this.validateFn(c);
}
}

Related

V-Model is not Reactive (Vue3)

I have an string input Component, which passes the inputted data back to the parent component, and stores it in a Form Object.
This was working fine, until I began to use a Validation Library (Vee-Validate), I'm trying to retrace my changes, but cannot seem to solve this issue without breaking the validator. I've copied the relevant code below.
Parent Component:
<template>
<form class="form" #submit.prevent="submitFormData">
<BaseInput
v-model="form.salutation.firstName"
type="text"
class="text-class"
label="case_file.table.first_name"
:required="true"
/>
</form>
</template>
const form = reactive({
salutation: {
firstName: ""
},
});
Child Component:
<template>
<div class="form-control">
<label class="label">
<span class="label-text">{{ $t(`${labelName}`) }}</span>
</label>
<input
type="text"
class="input input-bordered"
:value="value"
#input="$emit('update:modelValue', $event.target.value)"
v-on="validationListeners"
/>
<span>{{ errorMessage }}</span>
</div>
</template>
<script setup>
import { useField } from "vee-validate";
import { reactive, computed } from "vue";
import { toRefs } from "#vue/reactivity";
import { ref, watch } from "vue";
import { useParticipantStore } from "#/store/participant";
const participantStore = useParticipantStore();
const props = defineProps({
label: {
type: [String, Boolean],
default: false,
},
modelValue: {
type: String,
default: "",
},
required: {
type: Boolean,
default: false,
},
});
const labelName = ref(props.label);
const localInputValue = ref(props.modelValue);
function validateField(value) {
if (!value && props.required) {
return "This is required";
}
return true;
}
const { errorMessage, value, handleChange } = useField(
"fieldName",
validateField,
{
validateOnValueUpdate: false,
}
);
const validationListeners = computed(() => {
// If the field is valid or have not been validated yet
// lazy
if (!errorMessage.value) {
return {
blur: handleChange,
change: handleChange,
input: (e) => handleChange(e, false),
};
}
// Aggressive
return {
blur: handleChange,
change: handleChange,
input: handleChange, // only switched this
};
});
</script>
With this TextInput component:
value does not need to be a prop
Just emit the update:modelValue event to the parent (this should
be enough)
For the required validation, the browser will help us if we manage the form submit and input attributes properly.
IMHO, other validations should go in the form component rather than the input component. the input should only open the gate for errorMessages to render.
Also, if we have the error messages object (reactive) in the form component, we can determine the submit button state easily.
This does not include vee-validate code but I think you can embed it easily. I feel vee validation should go inside the form component.
<template>
<div class="form-control">
<label class="label">
<span class="label-text">{{ labelName }}</span>
</label>
<input
type="text"
class="input input-bordered"
:value="tv"
:required="required"
#input="updateModelValue"
#change="$emit('change', $event.target.value)"
#focus="$emit('focus', $event.target.value)"
#blur="$emit('blur', $event.target.value)"
#keyup="$emit('keyup', $event.target.value)"
>
<span>{{ errorMessage }}</span>
</div>
</template>
<script setup>
import {ref, defineProps, defineEmits} from "vue"
const emit = defineEmits([
"update:modelValue",
"change",
"blur",
"focus",
"keyup",
])
const tv = ref("")
defineProps({
labelName: {
type: String,
default: ""
},
errorMessage: {
type: String,
default: ""
},
required: {
type: Boolean,
default: false
}
})
const updateModelValue = ($event) => {
tv.value = $event.target.value
emit("update:modelValue", $event.target.value)
}
</script>
And this is in the Parent component:
<template>
<form #submit.prevent="submitForm">
<TextInput
v-model="form.salutation.firstName"
label-name="First Name"
:error-message="formErrors.salutation.firstName"
required
#keyup="validateFirstName"
/>
<button
type="submit"
:disabled="formErrors.salutation.firstName"
>
Submit
</button>
</form>
</template>
<script setup>
import {reactive, watch} from "vue"
const form = reactive({
salutation: {
firstName: "",
}
})
const formErrors = reactive({
salutation: {
firstName: null,
}
})
const validateFirstName = () => {
if (form.salutation.firstName && form.salutation.firstName.length < 3) {
formErrors.salutation.firstName = "First name must be at least 3 characters"
return false
} else {
formErrors.salutation.firstName = null
return true
}
}
const submitForm = () => {
console.log(form.salutation.firstName)
}
</script>
This should make your form:
not submittable when empty (required validation)
show|hide error messages on keydown

Vue 3 Vuelidate not working with component

I am using vue 3 / typescript . I have 2 forms one is for patient registration and other is for patient visits both forms required validation on some fields. I am using vuelidate to validate both forms. I am have applied validation on both forms patient and visit . patient form is a component and i injected it inside the visit page .The problem is when i add patient component inside visit page vailidation on visit page get fails. but then i comment out the patient compoment on visit page it works fine. both forms are independent form each other.
Here is the patient registration component
<template>
<Dialog
id="previewReceiptDailog"
v-model:visible="productDialog"
:style="{ width: '60vw' }"
position="top"
class="p-fluid"
:modal="true"
:closable="true"
#hide="closeDialog"
>
<template #header>
<h6 class="p-dialog-titlebar p-dialog-titlebar-icon">
<i class="pi pi-plus-circle"></i> {{ dialogTitle }}
</h6>
</template>
<div class="p-grid">
<div class="p-col-6">
<div class="p-field">
<label
for="firstName"
:class="{
'p-error': v$.firstName.$invalid && submitted
}"
>First Name</label
>
<InputText
id="firstName"
v-model="v$.firstName.$model"
:class="{
'p-invalid': v$.firstName.$invalid && submitted
}"
/>
<small
v-if="
(v$.firstName.$invalid && submitted) ||
v$.firstName.$pending.$response
"
class="p-error"
>{{
v$.firstName.required.$message.replace(
"Value",
"First Name"
)
}}</small
>
</div>
</div>
</div>
<template #footer>
<Button
type="button"
label="Save"
icon="pi pi-check-circle"
class="p-button-primary pull-left"
#click.prevent="saveItem(!v$.$invalid)"
/>
</template>
</Dialog>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { reactive } from "vue";
import { required } from "#vuelidate/validators";
import useVuelidate from "#vuelidate/core";
#Options({
props: {
PatientRegistration: Object
},
watch: {
PatientRegistration(obj) {
this.openDialog();
this.dialogTitle = obj.dialogTitle;
this.productDialog = obj.status;
if (obj.patientID != 0) {
this.loadPatient(obj.patientID);
}
}
},
emits: ["updatePatientStatus"]
})
export default class PatientDialog extends Vue {
private submitted = false;
private state = reactive({
firstName: "",
});
private validationRules = {
firstName: {
required
},
};
private v$ = useVuelidate(this.validationRules, this.state);
//DEFAULT METHOD OF TYPE SCRIPT
//CALLING WHENEVER COMPONENT LOADS
created() {
this.toast = new Toaster();
this.patientService = new PatientService();
}
saveItem(isFormValid) {
this.submitted = true;
if (isFormValid) {
//DO SOMETHING
}
}
}
</script>
Here is the visit page where i injeceted the component of patient
<template>
<section>
<div class="app-container">
<Dialog
v-model:visible="productDialog"
:style="{ width: '50vw' }"
:maximizable="true"
position="top"
class="p-fluid"
>
<template #header>
<h5 class="p-dialog-titlebar p-dialog-titlebar-icon">
{{ dialogTitle }}
</h5>
</template>
<form #submit.prevent="saveVisitItem(!v1$.$invalid)">
<div class="p-field">
<label for="notes">Notes</label>
<InputText
id="notes"
v-model="v1$.notes.$model"
:class="{
'p-invalid': v1$.notes.$invalid && submitted
}"
/>
<small
v-if="
(v1$.notes.$invalid && submitted) ||
v1$.notes.$pending.$response
"
class="p-error"
>{{
v1$.notes.required.$message.replace(
"Value",
"Notes"
)
}}</small
>
</div>
<Button
type="submit"
label="Save"
icon="pi pi-check"
class="p-button-primary"
/>
</form>
</Dialog>
</div>
</section>
<PatientDialog
:PatientRegistration="{
patientID: this.patientID,
status: this.patientDialogStatus,
dialogTitle: this.dialogTitle
}"
v-on:updatePatientStatus="updatePatientStatus"
/>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import VisitService from "../../service/VisitService.js";
import { reactive } from "vue";
import useVuelidate from "#vuelidate/core";
import { required } from "#vuelidate/validators";
import PatientDialog from "../../components/PatientDialog.vue";
import AutoComplete from "primevue/autocomplete";
#Options({
components: {
PatientDialog,
AutoComplete
}
})
export default class EncounterVisit extends Vue {
private validationRules = {
notes: {
required
}
};
private visitFormState = reactive({
notes: "",
});
private v1$ = useVuelidate(this.validationRules, this.visitFormState);
//ADD OR UPDATE THE ITEM VIA HTTP
saveVisitItem(isFormValid) {
this.submitted = true;
console.log(this.v1$);
console.log(isFormValid);
//this.submitted = false;
}
}
</script>
The response i get from the console log is
$anyDirty: true
$clearExternalResults: ƒ $clearExternalResults()
$dirty: false
$error: false
$errors: []
$getResultsForChild: ƒ $getResultsForChild(key)
$invalid: true
$model: null
$path: "__root"
$pending: false
$reset: () => {…}
$silentErrors: (5) [Proxy, Proxy, Proxy, Proxy, Proxy]
$touch: () => {…}
$validate: ƒ ()
notes: Proxy {$dirty: ComputedRefImpl, $path: 'notes', required: {…}, $touch: ƒ, $reset: ƒ, …}
_vuelidate_72: Proxy {$dirty: ComputedRefImpl, $path: '__root', $model: null, $touch: ƒ, $reset: ƒ, …}
[[Prototype]]: Object
false

How to use "this" in Vue 3

I'm working on a component in Vue3/TypeScript. I'm relatively new to Vue.
I'm dynamically creating a div. I want to be able to delete it when a user clicks on the "x" button. This is the "closeStatus" method.
In JavaScript I can pass this, but Vue hates it.
If anyone can help.
Thanks.
Here is the code:
<template>
<div>
<button #click="addStatus()" >Add Status</button>
<div id="slide" class="slide-modal">
<div class="clear-notifications" #click='clearAllNotifications()'>Clear all notifications</div>
<div class="status-div">
<div>11:00 Booking requested</div>
<div #click="closeStatus(this)"><img src="../assets/cross.svg" /></div>
</div>
<div class="status-div">
<div>11:30 Booking requested</div>
<div #click="closeStatus(this)"><img src="../assets/cross.svg" /></div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'SlideModal',
methods: {
clearAllNotifications() {
const allNotifications = document.querySelectorAll(".status-div");
for(let x=0;x<allNotifications.length;x++) {
allNotifications[x].remove(); //removes all the child elements.
}
document.getElementById('slide').style.visibility='hidden'; //This should be Vue reactive.
},
addStatus() {
document.getElementById('slide').style.visibility='visible'; //This should be Vue reactive.
const statusMessage = "Another Message"
var div = document.createElement('div');
div.setAttribute('class', 'status-div');
div.innerHTML = '<div>' + statusMessage + '</div><div onclick="closeStatus(this)"><img src="/src/assets/cross.svg" /></div>'
document.getElementById('slide').appendChild(div);
},
closeStatus(elm: any) {
elm.parentNode.remove();
const allNotifications = document.querySelectorAll(".status-div");
if(allNotifications.length == 0 ) {
document.getElementById('slide').style.visibility='hidden'; //This should be Vue reactive.
}
}
}
}
</script>
Code is javascript DOM management stuff and far from Vue style.
Vue manages DOM with variables and that is what makes javascript frameworks awesome.
Let me first share the modified code.
<template>
<div>
<button #click="addStatus()" >Add Status</button>
<div id="slide" class="slide-modal" v-show="visibleSlide">
<div class="clear-notifications" #click='clearAllNotifications()'>Clear all notifications</div>
<div class="status-div">
<div>11:00 Booking requested</div>
<div #click="closeStatus(this)"><img src="../assets/cross.svg" /></div>
</div>
<div class="status-div">
<div>11:30 Booking requested</div>
<div #click="closeStatus(this)"><img src="../assets/cross.svg" /></div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'SlideModal',
data() {
return {
visibleSlide: true
}
},
methods: {
clearAllNotifications() {
const allNotifications = document.querySelectorAll(".status-div");
for(let x = 0; x < allNotifications.length; x++) {
allNotifications[x].remove(); //removes all the child elements.
}
this.visibleSlide = false
},
addStatus() {
this.visibleSlide = true;
const statusMessage = "Another Message";
var div = document.createElement('div');
div.setAttribute('class', 'status-div');
div.innerHTML = '<div>' + statusMessage + '</div><div onclick="closeStatus(this)"><img src="/src/assets/cross.svg" /></div>'
document.getElementById('slide').appendChild(div);
},
closeStatus(elm: any) {
elm.parentNode.remove();
const allNotifications = document.querySelectorAll(".status-div");
if(allNotifications.length == 0 ) {
this.visibleSlide = false
}
}
}
}
</script>
I have just updated what is commented: "//This should be Vue reactive."
But it is still not benefiting from Vue framework.
Here is my whole idea of Vue code
<template>
<div>
<button #click="addStatus()" >Add Status</button>
<div id="slide" class="slide-modal" v-if="statuses.length">
<div class="clear-notifications" #click='clearAllNotifications()'>Clear all notifications</div>
<div v-for="status in statuses" :key="status.id" class="status-div">
<div>{{ status.text }}</div>
<div #click="closeStatus(status)"><img src="../assets/cross.svg" /></div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'SlideModal',
data() {
return {
statuses: [
{
id: 1,
text: '11:00 Booking requested'
}, {
id: 2,
text: '11:30 Booking requested'
}
]
}
},
methods: {
clearAllNotifications() {
this.statuses = []
},
addStatus() {
const statusMessage = "Another Message";
this.statuses.push({
id: this.statuses.length + 1,
text: statusMessage
})
},
closeStatus(elm: any) {
const index = this.statuses.map((status) => status.id).indexOf(elm.id);
this.statuses.splice(index, 1);
}
}
}
</script>
As you can see, DOM elements are managed by variables and their operations instead of DOM management methods.
Hope this is helpful

Vue parent component not re-rendering when computed properties are changing

I have a User-Select component that wraps a v-select. This components job is to fetch list of users as user types and allow the user to select one or more users.
Here is the code for this component.
<template>
<div>
<v-select
id="recipients"
name="recipients"
:options="options"
label="name"
multiple
#search="onSearch"
v-model="selectedVal"
/>
</div>
</template>
<script>
import vSelect from 'vue-select'
import axios from 'axios'
import _ from 'lodash'
export default {
name: 'UserSelect',
components: {
'v-select': vSelect
},
props: {
value: {
type: Array
}
},
data() {
return {
options: []
}
},
computed: {
selectedVal: {
get() {
return this.value
},
set(val) {
//this.value = val
this.$emit('input', val)
}
}
},
methods: {
onSearch(search, loading) {
loading(true)
this.search(loading, search, this)
},
setSelected: function(val) {
this.$emit('input', val)
},
search: _.debounce((loading, search, vm) => {
axios
.post('searchPeople', { search }, { useCredentails: true })
.then(res => {
vm.options = res.data
loading(false)
})
}, 350)
}
}
</script>
<style lang="scss" scoped>
#import url('https://unpkg.com/vue-select#latest/dist/vue-select.css');
</style>
As you can see I have a v-model linked to a computed property , which emits input event. Also my property name is value. Hence, I expect the parent component,that is using this UserEvent component to be able to v-model.
In the parent component , i have a computed property to which I have v-modelled the selected value. Here is the code.
<template>
<div>
<b-modal id="editMessage" :title="title" :static="true">
<form id="newMessageForm" class="row">
<div class="col-md-12">
<div class="form-group row">
<label for="to" class="col-sm-3 col-form-label">To:</label>
<user-select
class="col-sm-7"
style="padding-left: 0px; padding-right: 0px"
v-model="editedMessage.recipients"
/>
</div>
<div class="form-group row">
<label for="subject" class="col-sm-3 col-form-label"
>Subject:</label
>
<input
id="subject"
name="subject"
type="text"
class="form-control col-sm-7"
:value="editedMessage.messageSubject"
/>
</div>
<div class="form-group row">
<label for="date" class="col-sm-3 col-form-label"
>Schedule for later :
</label>
<input
type="checkbox"
class="form-control col-sm-7"
v-model="scheduleForLater"
id="scheduleCheckBox"
/>
</div>
<div class="form-group row" v-if="scheduleForLater">
<label for="date" class="col-sm-3 col-form-label"
>Scheduled Date:</label
>
<datetime
v-model="editedMessage.sentDate"
type="datetime"
class="col-sm-17"
input-class="form-control col-sm-15"
input-id="date"
/>
</div>
<div class="form-group row">
<label for="body" class="col-sm-3 col-form-label">Message:</label>
<textarea
id="body"
name="body"
type="text"
rows="10"
class="form-control col-sm-7"
:value="editedMessage.messageBody"
></textarea>
</div>
</div>
</form>
<template v-slot:modal-footer="{ hide }">
<!-- Emulate built in modal footer ok and cancel button actions -->
<b-button size="sm" variant="light" #click="hide()">
Cancel
</b-button>
<b-button
size="sm"
variant="secondary"
#click="
sendMessage(true)
hide()
"
>
Save Draft
</b-button>
<b-button
size="sm"
variant="primary"
#click="
sendMessage(false)
hide()
"
>
Send
</b-button>
</template>
</b-modal>
</div>
</template>
<script>
import { mapState } from 'vuex'
import UserSelect from '#/components/UserSelect.vue'
import axios from 'axios'
export default {
name: 'NewMessage',
components: {
'user-select': UserSelect
},
data() {
return {
options: [],
scheduleForLater: false
}
},
mounted() {},
computed: {
...mapState({
openMessage: state => state.message.openMessage,
messageAction: state => state.message.messageAction
}),
editedMessage: {
get() {
if (this.messageAction === 'new') {
return this.newMessage()
} else if (this.messageAction === 'reply') {
let openMessageClone = Object.assign({}, this.openMessage)
// make sender as the recipient.
return Object.assign(openMessageClone, {
messageSubject: 'RE: ' + this.openMessage.messageSubject,
recipients: [
{
name: this.openMessage.sender.name,
id: this.openMessage.sender.id
}
]
})
} else {
let openMessageClone = Object.assign({}, this.openMessage)
return Object.assign(openMessageClone, {
messageSubject: 'FW: ' + this.openMessage.messageSubject
})
}
},
set(val) {
this.$emit('input', val)
}
},
title() {
if (this.messageAction === 'new') {
return 'New'
} else if (this.messageAction === 'reply') {
return 'Reply'
} else {
return 'Forward'
}
}
},
methods: {
newMessage() {
return {
messageBody: '',
messageSubject: '',
recipients: [],
sender: {}
}
},
sendMessage(saveOrUpdateDraft) {
var url = ''
var message = {
recipients: this.editedMessage.recipients.map(x => x.id),
subject: this.editedMessage.messageSubject,
body: this.editedMessage.messageBody,
sentDate: this.scheduleForLater ? this.editedMessage.sentDate : null,
id: ['editDraft', 'viewScheduled'].includes(this.messageAction)
? this.editedMessage.messageId
: null
}
// id indiciates message id of draft message if one was opened.
if (saveOrUpdateDraft) {
// if no changes have been made to an opened draft, or the draft is completely empty for a new or existing draft , just go back.
if (message.id) {
url = `updateDraft`
} else {
url = 'saveNewDraft'
}
} else {
if (message.id) {
url = `sendSavedMessage`
} else {
url = 'sendMessage'
}
}
axios
.post(`eventdirect/${url}`, message, {
withCredentials: true
})
.then(response => {
if (url.includes('Draft') && this.messageAction === 'viewScheduled') {
this.$store.dispatch('sent/moveToDraft', response.data)
} else if (url.includes('Draft')) {
this.$store.dispatch('drafts/updateDraft', response.data)
} else {
// else part is sending a message or scheduling a draft.
if (this.messageAction === 'editDraft') {
this.$store.dispatch('drafts/deleteDraft', response.data)
}
// if we are sending an existing scheduled messsage , just update the sent vuex store , so that the message moves from scheduled to sent bucket.
if (this.messageAction === 'viewScheduled') {
this.$store.dispatch('sent/updateMessage', response.data)
} else {
this.$store.dispatch('sent/addSentItem', response.data)
}
}
})
.catch(() => {
// TODO , add a qtip here to notify user , this message should be sent later.
// messages in draft store with target , should be posted to the target
this.$store.dispatch('drafts/updateDraft', {
...message,
target: url
})
})
}
}
}
</script>
Now i can see in the vue dev tools the computed values in this NewMessage component gets changed. However this component does not re-render and the selected values are not passed down to UserSelect component until I toggle , schedule for later checkbox , that causes the components data to change and this triggers the Vue component to suddenly start showing the selected values.
Whats going on here. There is something about Vue's reactivity that I am not able to understand here.
Thanks in anticipation. You can try it here, or click on the edit sandbox button above to view and edit the code.
To try it, click on the Vue Message link , then hit reply , then type ‘Test’ and then select ‘Test User’ from the drop down. The selected user wont show until you click the checkbox - Schedule later.
PS: Within the component , UserSelect, In the setter of the computed property selectedVal , if I manually set the value of property value (by simply uncommenting line 39 - which is commented right now) , everything works fine. However , I get a warning that property should not be directly set. Since I am emitting the input event , parent component should change its v-model and re-render , thereby causing child component to re-render. Please correct me , if my understanding is wrong. Problem once again is that the parent component’s v-model changes , but it doesn’t re-render.

Validate Child component Vue vee-validate

App (parent)
Hi I have these component (Child)
TextComponent
InfoErrorForm
When I press submit from the parent component App is not validate this form. So I tried to validate with inject $validator in the child component (TextComponent), and provide but not show message error .
If you can help me to validate children form inisde parent component.
This is my code
AppComponent
<template>
<div>
<!-- Form validar numero max input -->
<form :class="{'was-validated': error_in_form_save_progress}" >
<card-shadow v-for="(texto,key) in sections_template.texts" :key="key" >
<texto-component
:orden="key+2"
v-model="sections_template.texts[key].content"
:tituloComponente="texto.title"
:inputName="texto.title" >
<template slot="section_show_error_validate_input">
<info-error-form
:listErrors='errors'
:name_field = "texto.title"
v-show = "error_in_form_save_progress" >
</info-error-form>
</template>
</texto-component>
</card-shadow>
</form>
<div class="row foot_button" >
<div class="offset-md-3 col-md-3">
<button class="btn" #click.prevent="save_progrees"> Guardar Cambios</button>
</div>
</div>
</div>
</template>
<script>
export default {
provide() {
return {
$validator: this.$validator,
};
},
data: function(){
return {
sections_template: {
texts:[
{
section_template_id: 1,
type: "texto",
title: "fundamentacion",
content: ""
},
{
section_template_id: 2,
type: "texto",
title: "sumilla",
content: ""
}
] },
error_in_form_save_progress: true
}
},
methods:{
save_progrees(){
this.$validator.validateAll().then((result) => {
if (result) {
this.error_in_form_save_progress = false;
alert("se guardaran cambios");
return
}
this.error_in_form_save_progress = true;
});
}
}
}
</script>
I found solution with this code. In my parent component i add provide and i send the $validator
export default {
components:{
...
},
provide() {
return {
$validator: this.$validator,
}
},
In my child component i received this
inject: ['$validator'],
In my parent component i add this method to invoque validation
methods:{
save_progrees(){
var validationArray = this.$children.map(function(child){
return child.$validator.validateAll();
});
window.Promise.all(validationArray).then((v) => {
v.some( element => { if ( element == false ) { throw "exists error in child component";} });
this.error_in_form_save_progress = false;
alert("datos guardados");
return
}).catch(() => {
this.show_message_error_validation();
});
},
show_message_error_validation(){
this.error_in_form_save_progress = true;
}
}
Finally to show error in component info-error I use this code
<template>
<div class="row" v-if="errors.items">
<div class="col-md-12">
<template v-for="(e,key) in errors.items" >
<div class="text-danger" v-if="e.field ==name_field" :key="key"> {{e.msg}} </div>
</template>
</div>
</div>
</template>
In your child component do watch for this.errors and in the watch, put this.$emit
Something like this below :
watch: {
errors: function (value) {
this.$emit('TextComponent', value)
}
}
And then catch it on your parent component see it here https://v2.vuejs.org/v2/api/#vm-emit