I have an Ag-Grid that has certain action buttons and dynamic data getting filled from a MongoDB database. I have a method on my MasterData.Vue file that refreshes the Grid. Each action button inside my grid's record perform update/delete operations. When I click on those buttons I have designed a customized pop up modal component in another Modal.Vue file. I want to call that RefreshGrid() method in Modal.Vue. I tried using props to share the data but same thing doesn't work on method.
MasterData.Vue Script
<script>
import { AgGridVue } from 'ag-grid-vue';
import { mapGetters } from 'vuex';
import gridEditButtons from '#/components/GridEditButton';
import MasterModal from '#/components/MasterModal';
export default {
name: 'masterData',
data () {
return {
addBtnClick: false,
delBtnClick: false,
editVisible: false,
selected: 'Business Area',
dropdown_tables: [
'Business Area',
'Council',
'Sub Area',
'Type',
'Work Flow Stage'
],
gridOptions: {
domLayout: 'autoHeight',
enableColumnResize: true,
rowDragManaged: true,
animateRows: true,
context: {
vm: null
}
}
};
},
components: {
'ty-master-modal': MasterModal,
'ag-grid-vue': AgGridVue,
gridEditButtons
},
methods: {
// Filter Grid Contents based on Dropdown selection
RefreshGrid: function () {
let cName;
if (this.selected === 'Business Area') {
cName = 'businessarea';
} else if (this.selected === 'Council') {
cName = 'council';
} else if (this.selected === 'Type') {
cName = 'typemaster';
} else if (this.selected === 'Work Flow Stage') {
cName = 'workflowstage';
}
let obj = {
vm: this,
collectionName: cName,
action: 'masterData/setMasterData',
mutation: 'setMasterData'
};
this.$store.dispatch(obj.action, obj);
}
};
</script>
Modal.Vue Script
<script>
import {mapGetters} from 'vuex';
export default {
name: 'MasterModal',
props: {
readOnly: Boolean,
entryData: Object,
addBtnClick: Boolean,
delBtnClick: Boolean,
editVisible: Boolean,
selectedTable: String
},
data () {
return {
fieldAlert: false,
isReadOnly: false,
dialog: false,
dialogDelete: false,
valid: false,
visible: false,
disable: false
};
},
computed: {
...mapGetters('masterData', {
entryState: 'entryState',
// entryData: 'entryData',
columns: 'columns',
selectedRowId: 'selectedRowId'
})
},
watch: {
addBtnClick: function (newValue, oldValue) {
this.setDialog(!this.dialog);
},
editVisible: function (newValue, oldValue) {
this.setVisible(!this.visible);
},
delBtnClick: function (newValue, oldValue) {
this.setDialogDelete(!this.dialogDelete);
}
},
methods: {
setDialog (bValue) {
this.dialog = bValue;
},
setDialogDelete (bValue) {
this.dialogDelete = bValue;
},
}
};
</script>
there are a couple of ways to achieve this.
One is to use the emit
in the MasterModal.vue component run this.$emit('refreshGrid') in the parent MasterData.Vue component use <ty-master-modal #refreshGrid="RefreshGrid" ...>
if you have a direct parent-child relationship, this is likely the best option
Another way is just to pass a function as a prop to the child component.
<ty-master-modal :onRefreshGrid="RefreshGrid" ...>
and add a prop onRefreshGrid to MasterModal.vue, then you can invoke the function.
Another way, using vuex, is to add a watch to MasterData.Vue and watch a variable in the vuex store ie. actionInvoker. when actionInvoker changes, the action executes. To change the value, set it to 0 and increment or toggle between, or set to random value. The advantage is that you can call this from anywhere.
The problem with this (and the previous) solution is that you have functionality tied to a view/component that shouldn't be there. I would recommend a third solution, which is to push the functionality into a vuex action, and then you can call it from anywhere. This would require though that you store the selected variable in vuex too, and if you want to have multiple instances of Modal and Master components, a singular store will prohibit that (unless you add support for multiple instances).
Related
I'm trying to clear up the form in the child component after the event containing the entered form data has been successfully passed from the child to parent component. However, I notice that the form gets cleared before the data gets propagated via the event to the parent component, such that the event passes empty values to the parent. I tried delaying the clearForm() using a timeout, but it didn't help. Is there a way to modify the behavior such that the clearForm() happens only after the event completes and the data has been saved?
Attached is the code.
Child Component
<template>
<!-- Contains a form -- >
</template>
<script>
export default {
data() {
return {
additionalInfo:
{
id: new Date().toISOString(),
fullName: '',
preAuthorize: '',
serviceAddress: ''
},
validation: {
fullNameIsValid: true,
serviceAddressIsValid: true
},
formIsValid: true,
addServiceButtonText: '+ Add Service Notes (Optional)',
serviceNotes: [],
showServiceNotes: false,
enteredServiceNote: '', //service notes addendum
}
},
computed : {
// something
},
methods: {
setServiceNotes(){
this.showServiceNotes = !this.showServiceNotes;
},
addAnotherParty(){
this.validateForm();
if(!this.formIsValid){
return;
}
this.$emit('add-parties', this.additionalInfo); //event
console.log(this.clearForm);
},
clearForm(){
this.additionalInfo.fullName = '';
this.additionalInfo.serviceAddress = '';
this.additionalInfo.preAuthorize = false;
}
}
}
</script>
Parent Component
<template>
<div>
<base-card
ref="childComponent"
#add-parties="updateAdditionalInfoList">
<!-- Wrapper for the `Parties Being Served` component-->
<template v-slot:title>
<slot></slot>
</template>
</base-card>
</div>
</template>
<script>
export default {
data() {
return {
hasElement: false,
selectedComponent: 'base-card',
additionalInfoList : [],
clearForm: false
}
},
methods: {
updateAdditionalInfoList(additionalInfo){ //save changes passed via event
this.additionalInfoList.push(additionalInfo);
console.log('emitted');
console.log(this.additionalInfoList);
setTimeout(() => {
this.$refs.childComponent.clearForm(); //clear the form in child
}, 2000);
}
}
}
</script>
Try this
addAnotherParty(){
this.validateForm();
if(!this.formIsValid){
return;
}
let emitObj = JSON.parse(JSON.stringify(this.additionalInfo));
this.$emit('add-parties', emitObj); //event
console.log(this.clearForm);
}
If your object is not deep then you can use
let emitObj = Object.assign({}, this.additionalInfo);
instead of stringify and parse
The meta data on my website is not updating when the route changes. The route itself has a watch on it which updates the view fine, but the metaInfo() from vue-meta is not keeping up. The <script> section of my code looks like this:
<script>
export default {
name: "Product",
watch: {
'$route.params.ProductID': {
deep: true,
immediate: true,
handler() {
this.getProduct(); // calls getProduct() on route change. Can I also call metaInfo() from here somehow?
}
}
},
metaInfo() {
return {
title: this.Product.ProductTitle,
meta: [
{
name: 'description', content: this.Product.ProductTitle
}
]
}
},
computed: {
Product() {
return this.$store.getters.getProduct
}
}, mounted() {
if (this.Product == null || !this.Product.length) {
this.getProduct();
}
}, methods: {
getProduct() {
return this.$store.dispatch('loadProduct', {ProductID: this.$route.params.ProductID})
}
}
}
</script>
What is happening is that when I change my route and go from /product/123 to /product/124, the metaInfo() still shows the meta data for /product/123. If I hit refresh, then the metaInfo() updates and shows the correct data for /product/124.
I need the watch to trigger an update of metaInfo() but don't know how to do it. I can't find this information in the docs anywhere. Please help?
For reactive, use variables outside return statements.
metaInfo() {
const title = this.Product.ProductTitle;
return {
title: title,
meta: [
{
name: 'description', content: title
}
]
}
}
https://vue-meta.nuxtjs.org/guide/caveats.html#reactive-variables-in-template-functions
I'm working on a Vue project which has a component for loading content into a modal via an ajax call:
<load-content target="foo"></load-content>
<load-content target="bar"></load-content>
<load-content target="oof"></load-content>
<load-content target="rab"></load-content>
Here's an example template:
<template>
<span class="load-content-wrapper" v-on:click="load">
Click
</span>
</template>
<script>
export default {
name: 'load content',
props: {
target: {
type: String,
required: true
}
},
methods: {
load() {
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
</script>
Which would trigger this example action:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
});
},
};
This all works well, except we cannot guarantee that the Ajax call will always return content. Depending on the target it could return 404.
Ideally I want to automatically disable individual load-content components if '/api/fetch-content/' + target isn't available to prevent users from trying to select unavailable content.
What is the correct/ most efficient way to do this?
You should make your "target" field not required and instead add a default value empty string.
And add an "if" condition to your load method. If "target" is empty, it will not proceed.
export default {
name: 'load content',
props: {
target: {
type: String,
default: ''
}
},
methods: {
load() {
if (!this.target) return;
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
Create a store variable loading and mutate it in your actions as follows:
loading: false
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
commit(setLoading)
});
},
};
Then in muatations ->
setLoading (state, loading) {
state.loading = true
}
Now in your vue file use this store variable and check if it is true then load the component.You may check this created or mounted events of the component.
Option 1
Preemptively load the content, and disable the ones that return an error.
This is what the parent component will look like
<template>
<load-content
v-for="(target, index) in loadedTargets"
:key="index"
target="target"
/>
</template>
<script>
export default {
name: 'load content parent',
data: function() {
return {
targets: [
{link: 'foo', data: null, loaded: false, error: null},
{link: 'bar', data: null, loaded: false, error: null},
{link: 'oof', data: null, loaded: false, error: null},
{link: 'rab', data: null, loaded: false, error: null},
]
}
},
computed: {
loadedTargets() {
return this.targets.filter(t => t.loaded)
}
},
methods: {
load(target) {
const self = this;
$.ajax({
url: '/api/fetch-content/' + target.link,
}).then((data) => {
self.targets[indexOf(target)].data = data
self.targets[indexOf(target)].loaded = true
}).catch((error) => {
self.targets[indexOf(target)].error = error
});
},
},
mounted() {
this.targets.forEach(target => this.load(target))
}
};
</script>
Option 2
Preemptive loading is expensive (and since I don't know how many targets you might have), you could also show success/error in the modal. Proper UX would dictate that an explicit action by the user should lead to a result (i.e. if the user clicks a link, he should either see data in the modal, or an error)
This is what your action will look like:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
}).catch((error) => {
// Load Modal Window, and show the error
});
},
};
Hi All I'm having an issue with vue tables 2.
I pass my application a Vue component with the following:
<tenant-applications :url="{{ json_encode($tenant->websites->first()->hostnames()->first()->fqdn) }}/api/applications"></tenant-applications>
Which returns "http://anotherdomain.com/api/endpoint"
But when I do the following for Vue Tables 2 I get the following error with this config: "Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead."
<template>
<v-server-table url="//{{url}}" :columns="columns" :options="options"></v-server-table>
</template>
<script>
import {ServerTable, ClientTable, Event} from 'vue-tables-2';
Vue.use(ClientTable, {}, false, 'bootstrap4');
export default {
data: function () {
return {
applications: "",
columns: ['Application', 'Type', 'Lender', 'Options', 'Sale', 'Rate', 'Broker'],
tableData: applications,
options: {
perPage:25,
perPageValues:[25],
filterable: false,
}
}
},
props: {
url: String,
},
created (){
},
methods: {
}
}
}
</script>
Is there a better way to achieve what I need? I am using Laravel so can only access that url variable from the blade template
Just add computed to solve this and bind to url:
// template
<v-server-table :url="urlForServerTable" :columns="columns" :options="options"></v-server-table>
// component
...
computed: {
urlForServerTable() {
return `//${this.url}`
}
}
...
I'm building a Vue 2 application and, in a page, I need to keep track of the value of a single checkbox. So I did this:
<template>
<div>
<input
type="checkbox"
v-model="checkboxValue"
/> Check to accept payment terms and conditions
</div>
</template>
<script>
export default {
props: {
cardData: {
type: Object,
required: true,
},
eventBus: {
type: Object,
required: true,
},
url: {
type: String,
required: true,
},
},
data() {
return {
checkboxValue: false,
};
},
computed: {
forwardCheckboxValue() {
console.log(this.checkboxValue);
this.eventBus.$emit("checkbox_value", {
checkboxValue: this.checkboxValue,
});
},
},
};
</script>
<style>
</style>
Basically I want to keep track if the checkbox is selected or not, and everytime the value changes I want to emit an event that warns me about that.
The problem is that the console.log in the computed property is not triggered.
What am I missing?
You can use computed setter and remove checkboxValue from the data option. Here is the fiddle
computed:{
checkboxValue:{
get(){
return false;
},
set(newValue){
this.$emit('checkbox-changed', newValue);
}
}
}
Or as frank provost suggested set up a watcher which should have the same name of the data property you are watching. Here is the fiddle
watch:{
checkboxValue(newValue){
this.$emit('checkbox-changed', newValue);
}
}