Passing an Object as a prop and syncing nested form values with Vuetify text fields - vue.js

I am trying to pass the inputted form data back to the parent.
Parent:
<AddForm
:value.sync="field"
></AddForm>
data: () => ({
field: {
id: "",
name: "",
},
})
Child AddForm:
<v-text-field :value="value.id" #input="addId"></v-text-field>
<v-text-field :value="value.name" #input="addName"></v-text-field>
props: {
value: { type: Object, required: true },
},
methods: {
addId(e) {
this.$emit("update:field.id", e);
},
addName(e) {
this.$emit("update:field.name", e);
},
}
Not sure what I'm missing ?

I think you should only update field without specifying the nested fields by spreading this.value and add the updated field and emitting it as payload :
<v-text-field :value="value.id" #input="addId"></v-text-field>
<v-text-field :value="value.name" #input="addName"></v-text-field>
props: {
value: { type: Object, required: true },
},
methods: {
addId(e) {
this.$emit("update:field",{...this.value, id: e});
},
addName(e) {
this.$emit("update:field", {...this.value,name:e});
},
}

Even though you can do like #BoussadjraBrahim said, I think if that component is a form, it should act like a form, and then you only pass the data back to parent when the user try to submit the form.
Something like this, the code below is not valid, is just to demonstrate my point
FormComponent.vue
<template>
<v-form :valid="valid" #submit.prevent="sendData">
<v-text-field :value="value.id" :rules="[someValidation]" #input="id" />
<v-text-field :value="value.name" #input="name" />
<v-btn type="submit">Save</v-btn>
</v-form>
</template>
<script>
.....
props: {
value: { type: Object, required: true },
},
data() {
return {
valid: false,
id: '',
name: '',
}
},
methods: {
sendData() {
this.$emit("submit",{id: this.id, name: this.name });
},
}
</script>
Then in parent you get the information when the user submits the information.
With this approach you can let the logic to validate the input to the component, which to me makes a lot of sense.

Related

How do you push an object into another object when the data within it gets changed?

I have a very large array of objects containing various errors logs. There are 1015 objects in total. At the moment I am saving the entire array every time I make a small change to a value in one of the objects. This leads to timeout errors because it takes too long to go through the whole array.
So instead I would like to figure out how I can change it so that the program ONLY saves an object if that object has been changed on the frontend.
So if I have 1015 objects and I only change something in object no. 2, then only object no. 2 should be saved on submit.
I was thinking, maybe it would be possible to first let the program look for any changes. Then IF a change has occured it will move that particular object to a new (empty) object, which I can then submit.
So, in my code example, I would like to have this function implemented on the computed property "Fields" which has the key "done". This contains a checkbox that sets the value error.done to true or false. So I would like for the program to check if this specific value has changed. If it has changed from true to false or vice versa I would like to send this object to a new object.
eg. if errors.done is set from true to false, then move the changed object to a new object called changedValue.
<template>
<b-container>
<b-card class="mt-4">
<h5>{{ $t('events') }}</h5>
<b-table
:items="errors"
:fields="fields"
:per-page="[10, 25, 50]"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
#row-clicked="showModal"
sort-desc
/>
</b-card>
<error-log-entry-modal ref="errorLogEntryModal" :selected-error-log="selectedRows"/>
<button #click="submit">Submit</button>
</b-container>
</template>
<script>
import {errorService} from '#/services/error';
import ErrorLogEntryModal from '#/components/error-log/ErrorLogEntryModal';
import moment from 'moment';
export default {
components: {
ErrorLogEntryModal,
},
props: {
ownerId: String
},
data() {
return {
errors: null,
selectedRows: []
};
},
computed: {
fields() {
return [
{
key: 'done',
label: '',
thStyle: 'width: 1%',
template: {
type: 'checkbox',
includeCheckAllCheckbox: true,
}
},
{
key: 'priority',
label: this.$t('errorLogs.priority'),
sortable: true,
},
{
key: 'creationDateTime',
label: this.$t('creationDateTime'),
formatter: date => moment(date).locale(this.$i18n.locale).format('L'),
sortable: true,
},
{
key: 'stackTraceShort',
label: this.$t('errorLogs.stackTrace'),
sortable: true,
},
{
key: 'errorMessage',
label: this.$t('message'),
sortable: true
},
]
},
},
methods: {
load(){
errorService.getErrorLogs().then(result => {
result.data.forEach(log => log.stackTraceShort = log.stackTrace.substring(0,30));
this.errors = result.data
})
},
submit(){
return errorService.setStatusOnErrorEntryLog(this.errors).then( result => {
console.log(result)
})
},
onRowSelected(fields){
this.selectedRows = fields
},
showModal(){
if (this.selectedRows) {
this.$refs.errorLogEntryModal.show()
}
},
},
created() {
this.load()
},
};
</script>
If I have understood correctly the selected rows correspond to errors.done ? In this case you can just edit the onRowSelected method like this :
onRowSelected(fields){
this.selectedRows.push(fields)
},
Then replace this.errors by this.selectedRows in you're submit method ?

call child's computed method in VUE

I got three VUE components with a structure like this:
Table->Modal->Form. When user selects a record, the modal will be triggered and shown.
The Form component contains a computed method method1 for computing a property that is not stored by the database. I want to add a column in the Table component and display the return value of method1 for each record.
So something like this:
Vue.component('Form`, {
computed: {
method1: function() {
// ...some calculations
// No parameter cuz Form contains the selected record's data model
return a_calculated_value
}
}
}
And I want to call method1 on each of the records in Table
In addition, if possible I do not want to store this calculated value in the database.
Is this possible?
look at my example maybe it can help
<template>
<marker-table
ref="dt"
v-model="showTable"
:raw-table-data="tableData"
/>
<v-btn #click="showGasTable">
Show GAS Table
</v-btn>
<v-btn #click="shoeElecTable">
Show Electricity Table
</v-btn>
</template>
<script>
import markerTable from './components/markerTable.vue'
methods:{
showGasTable () {
this.tableData = this.gas
this.$refs.dt.popHeaders('gas')
},
showElecTable () {
this.tableData = this.elec
this.$refs.dt.popHeaders('electricity')
},
}
</script>
Component markerTable.vue
<template>
<v-data-table
:headers="headers"
:items="tableData"
>
</v-data-table>
</template>
<script>
export default { // eslint-disable-next-line
props: ['rawTableData'],
data () {
return {
headers: [],
title: '',
rawData: '',
tableData: ''
}
},
computed: {
dataUpdate () {
this.rawData = this.rawTableData
return this.rawData
}
},
methods: {
popHeaders (value) {
if (value === 'gas') {
this.headers =
[{ text: 'Volume', value: 'Vm' },
{ text: 'Day', value: 'day' }]
this.title = 'GAS Supply'
} else if (value === 'electricity') {
this.headers =
[{ text: 'Units', value: 'units' },
{ text: 'Watts', value: 'watt' },
{ text: 'Time', value: 'time' }]
this.title = 'Electric Supply'
}
}
}
You can access the method popHeader from the child component with sets of data and can process them in your child and it will be returned to your main page.

VueJs Vuetify Click on table row event not reading item

i am new to VueJs and Vuetify and i have a script that read data from a Json and display it into a Table.is a glossary type app. I have the option to select from different languages to be shown into the table. The problem is that when i click on a row i would like to display into a popup (alert) the item information. What i did is not working at all, is just showing the Alert popup but without information.
The format of the Json is:
{"glossary":[
{"id":2,"English":{"term":"contact","definition":"the state of physical touching"},"Vietnamese":{"term":"tiếp xúc"},"Simplified_Chinese":{"term":"接触"},"Arabic":{"term":"ملامسة"},"Swahili":{"term":"mgusano"}}]}
<v-data-table dense light :headers="selectedHeaders" :item-key="id" #click:row="showAlert(item)" :items="glossary.glossary" class="elevation-1" :single-expand="true" :disable-sort=true :search="search">
<template #item.ar.term="{item}">
<div style="text-align:right;">
<span>{{item.ar.term}}</span>
</div>
</template>
</v-data-table>
<script>
import About from '#/views/About.vue'
import json from '#/assets/data/glossary.json'
export default {
name: 'App',
components: { About },
data () {
return {
publicPath: process.env.BASE_URL,
glossary: json,
search: '',
value: [],
expanded: [],
selectedHeaders: [],
dialog: false,
headers: [
{ text: 'English', value: 'English.term' },
{ text: 'Vietnamese', value: 'Vietnamese.term' },
{ text: 'Arabic', value: 'Arabic.term' },
]
}
},
methods: {
filter(value, search, item) {
return value != null &&
search != null &&
typeof value === 'string' &&
value.toString().toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) !== -1
},
showAlert(a){
if (event.target.classList.contains('btn__content')) return;
alert('Extra Information:! \n'+this.English.term );
console.log(this);
}
watch: {
value(val) {
this.selectedHeaders = val ;
}
},
created() {
this.selectedHeaders = this.headers;
}
}
</script>
You are not using a which is the item passed. this just returns the vue object. What you might want is this
showAlert(a){
if (a.target.classList.contains('btn__content')) return;
alert('Extra Information:! \n'+a.English.term );
console.log(a);
}
Here is a codepen that I found that should help you understand it better https://codepen.io/nsiggel/pen/KRdGgE. Note: I did not write this codepen.

Vee validate return true, but should return false

Im using Vuetify, and have a form where im using VeeValidate for form validation...
When im using this:
this.$validator.validateAll().then((result) => {
console.log("result form", result);
//result ? this.onSubmit() : scrollTo(0, 250);
});
It always returns true, even if the validation on my input field isn't valid...
The input looks like:
<v-textarea
filled
name="string"
:label="placeholderText"
auto-grow
single-line
:placeholder="placeholderText"
v-model="answer"
:required="isRequired"
v-validate:computedProp="checkRequired"
:error-messages="errors.collect('string')"
data-vv-name="string"
:hint="hintText"
#blur="updateAnswer"
></v-textarea>
The code for the input component:
export default {
$_veeValidate: {
validator: 'new'
},
name: 'String',
props: {
placeholderText: {
default: 'Add a value'
},
hintText: {
default: 'Add a value'
},
isRequired: {
default: true
}
},
data: () => ({
answer: ''
}),
computed: {
checkRequired() {
return this.isRequired ? 'required' : ''
}
},
methods: {
updateAnswer() {
this.$validator.validateAll();
this.$emit('updateAnswer', this.answer);
}
}
}
Im calling this.$validator.validateAll() in another component... The input component is a standalone component... I have it all wrappet in a form tag, and running the validate function on a submit
You have two choice:
Pass to the component the v-validate from the $attrs
Inject the $validator to the component
Parent
export default {
name: "App",
components: {
YourComponent
},
provide() {
return {
$validator: this.$validator
};
},
Child
$_veeValidate: {
validator: "new"
},
inject: ["$validator"],
name: "String",
You can also simplify your html code, see the VeeValidate Syntax
Html
v-validate="{ required: this.isRequired }"
And you can safely remove
:required="isRequired"

VueJs watcher vs multiple event listeners on the instance of the model

I'm trying to figure out which approach is more appropriate / less resource intense. Both examples below will do the same job, but my understanding is that there might be situations where both events #keyup and #change might be triggered unnecessarily - hence my question, whether watch method would be a better option here?
The reason why I also need to check for a #change event is when someone simply selects saved input value rather than type it.
<template>
<input
type="text"
:name="name"
#keyup="update()"
#change="update()"
v-model="field"
>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
initialValue: {
type: String,
required: false,
default: ''
}
},
data() {
return {
field: this.initialValue
}
},
mounted() {
this.update();
},
methods: {
update() {
this.$emit('input', this.field);
}
}
}
</script>
vs
<template>
<input
type="text"
:name="name"
v-model="field"
>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
initialValue: {
type: String,
required: false,
default: ''
}
},
data() {
return {
field: this.initialValue
}
},
mounted() {
this.update();
},
watch: {
field() {
this.update();
}
},
methods: {
update() {
this.$emit('input', this.field);
}
}
}
</script>