call child's computed method in VUE - vue.js

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.

Related

How do I work with Bootstrap select options when looping through a non-manual array?

I want to take an object containing an array that I get from a database, and I want the content of this array to appear as an select-option in a Bootstrap's form. I have tried reading Bootstrap's documentation on Form Select, as well as going through some old questions here.
In all cases I get examples of options that have been manually written into the code like this:
<script>
export default {
data() {
return {
selected: 'A',
options: [
{ item: 'A', name: 'Option A' },
{ item: 'B', name: 'Option B' },
{ item: 'D', name: 'Option C', notEnabled: true },
{ item: { d: 1 }, name: 'Option D' }
]
}
}
}
</script>
But this is not what I want. In my case I am fetching some data from a database and storing it in an object that contains an array. That array contains stuff like id, name etc. In my case I just want the name.
<template>
<b-container>
<b-form-select v-model="myDatabaseData">
</b-form-select>
</b-container>
</template>
<script>
export default {
data() {
return {
myDatabaseData: null,
options: []
};
},
created() {
myDB.getData()
.then(result => this.myDatabaseData = result.data)
.catch(error => console.error(error));
},
};
</script>
I have the v-model set up correctly, but what exactly do I do in options?
I thought this was the correct approach:
<template>
<b-container>
<b-form-select v-model="myDatabaseData" :options="options">
</b-form-select>
</b-container>
</template>
<script>
export default {
data() {
return {
myDatabaseData: null,
options: [{ value: this.myDatabaseData.name }]
};
},
created() {
myDB.getData()
.then(result => this.myDatabaseData = result.data)
.catch(error => console.error(error));
},
};
</script>

Vue.js: Child Component mutates state, parent displays property as undefined

I have a parent component that lists all the tasks:
<template>
<div class="tasks-wrapper">
<div class="tasks-header">
<h4>{{ $t('client.taskListingTitle') }}</h4>
<b-button variant="custom" #click="showAddTaskModal">{{ $t('client.addTask') }}</b-button>
</div>
<b-table
striped
hover
:items="tasks"
:fields="fields"
show-empty
:empty-text="$t('common.noResultsFound')">
</b-table>
<AddTaskModal />
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import AddTaskModal from '#/components/modals/AddTaskModal'
import moment from 'moment'
export default {
name: 'TaskListing',
components: {
AddTaskModal
},
data () {
return {
tasks: [],
fields: [
{ key: 'createdOn', label: this.$t('tasks.tableFields.date'), formatter: 'formatDate' },
{ key: 'domain', label: this.$t('tasks.tableFields.task') },
{ key: 'comment', label: this.$t('tasks.tableFields.comment') },
{ key: 'status', label: this.$t('tasks.tableFields.status') }
]
}
},
computed: {
...mapGetters('users', ['user'])
},
methods: {
...mapActions('tasks', ['fetchTasks']),
...mapActions('users', ['fetchUserById']),
formatDate: function (date) {
return moment.utc(date).local().format('DD.MM.YYYY HH:mm')
},
showAddTaskModal () {
this.$bvModal.show('addTaskModal')
}
},
async mounted () {
const currUserId = this.$router.history.current.params.id
if (this.user || this.user.userId !== currUserId) {
await this.fetchUserById(currUserId)
}
if (this.user.clientNumber !== null) {
const filters = { clientReferenceNumber: { value: this.user.clientNumber } }
this.tasks = await this.fetchTasks({ filters })
}
}
}
</script>
Inside this component there is a child which adds a task modal.
<template>
<b-modal
id="addTaskModal"
:title="$t('modals.addTask.title')"
hide-footer
#show="resetModal"
#hidden="resetModal"
>
<form ref="form" #submit.stop.prevent="handleSubmit">
<b-form-group
:invalid-feedback="$t('modals.requiredFields')">
<b-form-select
id="task-type-select"
:options="taskTypesOptions"
:state="taskTypeState"
v-model="taskType"
required
></b-form-select>
<b-form-textarea
id="add-task-input"
:placeholder="$t('modals.enterComment')"
rows="3"
max-rows="6"
v-model="comment"
:state="commentState"
required />
</b-form-group>
<b-button-group class="float-right">
<b-button variant="danger" #click="$bvModal.hide('addTaskModal')">{{ $t('common.cancel') }}</b-button>
<b-button #click="addTask">{{ $t('modals.addTask.sendMail') }}</b-button>
</b-button-group>
</form>
</b-modal>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'AddTaskModal',
data () {
return {
comment: '',
commentState: null,
taskTypesOptions: [
{ value: null, text: this.$t('modals.addTask.taskType') },
{ value: 'OnBoarding', text: 'Onboarding' },
{ value: 'Accounts', text: 'Accounts' },
{ value: 'TopUp', text: 'Topup' },
{ value: 'Overdraft', text: 'Overdraft' },
{ value: 'Aml', text: 'Aml' },
{ value: 'Transfers', text: 'Transfers' },
{ value: 'Consultation', text: 'Consultation' },
{ value: 'TechnicalSupport', text: 'TechnicalSupport' },
{ value: 'UnblockPin', text: 'UnblockPin' },
{ value: 'Other', text: 'Other' }
],
taskType: null,
taskTypeState: null
}
},
computed: {
...mapGetters('users', ['user']),
...mapGetters('tasks', ['tasks'])
},
methods: {
...mapActions('tasks', ['addNewTask', 'fetchTasks']),
...mapActions('users', ['fetchUserById']),
async addTask (bvModalEvt) {
bvModalEvt.preventDefault()
if (!this.checkFormValidity()) { return }
const currUserId = this.$router.history.current.params.id
if (this.user || this.user.userId !== currUserId) {
await this.fetchUserById(currUserId)
}
const data = {
clientPhone: this.user.phoneNumber,
comment: this.comment,
clientReferenceNumber: this.user.clientNumber,
domain: this.taskType
}
await this.addNewTask(data)
if (this.user.clientNumber !== null) {
const filters = { clientReferenceNumber: { value: this.user.clientNumber } }
this.tasks = await this.fetchTasks({ filters })
// this.tasks may be useless here
}
console.log(this.tasks)
this.$nextTick(() => { this.$bvModal.hide('addTaskModal') })
},
checkFormValidity () {
const valid = this.$refs.form.checkValidity()
this.commentState = valid
this.taskTypeState = valid
return valid
},
resetModal () {
this.comment = ''
this.commentState = null
this.taskTypeState = null
}
}
}
</script>
When I add a task I call getalltasks to mutate the store so all the tasks are added. Then I want to render them. They are rendered but the property createdOn on the last task is InvalidDate and when I console log it is undefined.
The reason I need to call gettasks again in the modal is that the response on adding a task does not return the property createdOn. I do not want to set it on the front-end, I want to get it from the database.
I logged the store and all the tasks are added to the store.
Why is my parent component not rendering this particular createdOn property?
If I refresh the page everything is rendering fine.
If you add anything into a list of items that are displayed by v-for, you have to set a unique key. Based on your explanation, I assume that your key is the index and when you add a new item, you mess with the current indexes. Keys must be unique and unmutateable. What you need to do is to create a unique id for each element.
{
id: Math.floor(Math.random() * 10000000)
}
When you create a new task, use the same code to generate a new id, and use id as key. If this doesn't help, share your d-table and related vuex code too.

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.

Display Boolean on Vuetify dataTable

I made a data table with vue vuetify and I fetch my data from my api server.
In my table, everything is display except boolean.
Can you help me to see clearer
Table component
<template>
<v-data-table
:headers="headers"
:items="fixture"
:items-per-page="5"
class="elevation-10"
>
</v-data-table>
</template>
<script>
export default {
name: 'my_fixtures',
props: ['fixture'],
data () {
return {
headers: [
{ text: 'name',value: 'name'},
{ text: 'State', value: 'on' },
{ text: 'Group', value: 'group' },
{ text: 'Recipe', value: 'recipe' },
{ text: 'start', value: 'startDate' },
{ text: 'end', value: 'endDate' },
{ text: 'Action', value: 'action' },
],
items: this.fixtures
}
}
}
</script>
In the object that I receive , the key 'on' is a Boolean.
I have all display , but nothing in the 'on' column
and this is what I do with props
<template>
<v-container>
<my_fixtures v-bind:fixture="fixtures"></my_fixtures>
<router-view></router-view>
</v-container>
</template>
<script>
import my_fixtures from "./greenhouse/fixtures";
export default {
name: "my_client",
data: function (){
return {fixtures: []}
},
components: {my_fixtures},
mounted() {
http.get('fixture/client')
.then(result => {
this.fixtures = result;
})
.catch(error => {
console.error(error);
});
}
}
</script>
Process and print data using methods.
try this.
<td class="text-xs-right">{{ computedOn(props.fixture.on) }}</td>
export default {
methods: {
computedOn (value) {
return String(value)
}
}
}
UPDATE
Modifying original data due to vuetify bug
https://github.com/vuetifyjs/vuetify/issues/8554
export default {
mounted() {
http.get('fixture/client')
.then(result => {
this.fixtures = result.map(value => {
value.on = String(value.on)
})
})
.catch(error => {
console.error(error);
});
}
}

vuejs reactivity of complex objects

I am using Vue.js 2.5.17 I have two components, App (parent) and TreeNode (child), which display a tree structure of items from a deeply nested object. Each node in the tree is presented with the TreeNode component which is a recursive component.
TreeNode component
const TreeNode = Vue.component('TreeNode', {
name: 'TreeNode',
template: '#treenode',
props: {
model: Object,
},
data() {
return {
open: false,
};
},
computed: {
isExpandable() {
return this.model.children &&
this.model.children.length;
},
},
methods: {
toggle() {
if (this.isExpandable) {
this.open = !this.open;
}
},
},
});
TreeNode template
<script type="text/x-template" id="treenode">
<li>
<input type="checkbox" :id="model.name" style="display:none;"/>
<label :for="model.name" style="color:gray;" #click="toggle">
{{ model.name }}
{{ model.data.example }}
</label>
<ul v-show="open" v-if="isExpandable">
<TreeNode
class="item"
v-for="(model, index) in model.children"
:key="index"
:model="model">
</TreeNode>
</ul>
</li>
</script>
App component template
<script type="text/x-template" id="oulist">
<div>
<div id="unitsTable" class="row filterlist treelist b_widget2" style="width:85%;">
<div class="css-treeview">
<TreeNode
class="item"
:model="items">
</TreeNode>
</div>
</div>
</script>
App component
const App = Vue.component('App', {
template: '#oulist',
components: {
TreeNode,
},
data() {
return {
items: {
name: 'item1',
data: { example: '1' },
children: [
{
name: 'item11',
children: [],
data: { example: '1' },
},
{
name: 'item12',
children: [
{ name: 'item121', children: [], data: { example: '1' } },
{ name: 'item122', children: [], data: { example: '1' } },
],
data: { example: '1' },
},
],
},
};
},
methods: {
updateItem(currNode, name, data) {
if (currNode.name === name) {
Object.assign(currNode.data, data);
this.items = Object.assign({}, this.items); // tried to create a new object here and overwrite it, but it didn't help
return;
}
if (currNode.children) {
currNode.children.forEach(c => this.updateItem(c, name, data));
}
},
},
});
The object posted above is just an example, my actual object has a lot more nested levels and items per level.
The problem am I facing is that whenever a property deep within my items object is changed (more specifically, the example property of the data object inside a node), the DOM is not updated. I read through the reactivity caveats and saw that adding new properties is not reactive by default, but I am not adding new properties here, just changing the existing ones.
When data from a tree node is updated, I traverse the object to find the correct node and update its properties as follows:
updateItem(currNode, name, data) {
if (currNode.name === name) {
Object.assign(currNode.data, data);
this.items = Object.assign({}, this.items); // tried to create a new object here and overwrite it, but it didn't help
return;
}
if (currNode.children) {
currNode.children.forEach(c => this.updateItem(c, name, data));
}
},
this.updateItem(this.items, 'item121', { example: 'newVal' });
Any tips ? Thanks!
EDIT: The data is always changed only in the parent (App) component.