Vue dynamic component template not working for promise - vue.js

<template>
<component :is="myComponent" />
</template>
<script>
export default {
props: {
component: String,
},
data() {
return {
myComponent: '',
};
},
computed: {
loader() {
return () => import(`../components/${this.component}`);
},
},
created() {
this.loader().then(res => {
// components can be defined as a function that returns a promise;
this.myComponent = () => this.loader();
},
},
}
</script>
Reference:
https://medium.com/scrumpy/dynamic-component-templates-with-vue-js-d9236ab183bb
Vue js import components dynamically
Console throw error "this.loader() is not a function" or "this.loader().then" is not a function.

Not sure why you're seeing that error, as loader is clearly defined as a computed prop that returns a function.
However, the created hook seems to call loader() twice (the second call is unnecessary). That could be simplified:
export default {
created() {
// Option 1
this.loader().then(res => this.myComponent = res)
// Option 2
this.myComponent = () => this.loader()
}
}
demo 1
Even simpler would be to rename loader with myComponent, getting rid of the myComponent data property:
export default {
//data() {
// return {
// myComponent: '',
// };
//},
computed: {
//loader() {
myComponent() {
return () => import(`../components/${this.component}`);
},
},
}
demo 2

Related

How do I communicate the error messages to other components of the form?

I am working with Vue and Laravel, the issue is that I made an error class and as the form works with 3 different components.
For example I have the base form of this form and further down I have a child component of the form both are sent in the same form but I can't figure out how to pass the error message to that component that is in another vue file.
I tried to pass the error message as a prop but it doesn't take it and inside that child component I used watch to capture the error but it doesn't capture it at all.
Error Class
class Errors{
constructor(){
this.errors = {};
}
get(field){
if (this.errors[field]) {
return this.errors[field][0];
}
}
record(errors){
this.errors = errors.errors;
}
}
export default Errors;
**Parent component: CreateClient **
<template>
<contract v-for="contract in contractsFields" :key="contract.id"
:id="contract.id"
:resultForm="onResultsContracts"
:hasErrors="errors"
#remove="removeContract"
></contract>
</template>
<script>
import Errors from '../../class/Errors.js'
import Contract from '../contratos/ContractComponent.vue'
export default {
name: 'CreateClient',
props: ['results'],
components:{
Contract
},
data() {
return {
errors: new Errors(),
}
},
sendForm(){
const params = {
client: this.formClient,
company: this.formCompany,
contract: this.formContract
}
axios.post(route('clients.store'),params)
.then((res) => {
this.errors = []
console.log(res)
})
.catch(error => {
if (error.response.status === 422) {
this.errors = error.response.data.errors || {};
}
})
}
}
</script>
Child Component
<script>
import Errors from '../../class/Errors.js'
import AreaClient from '../clients/AreasClientComponent.vue'
export default {
name: 'Contract',
components: {
AreaClient
},
props:['id','resultForm','hasErrors'],
data() {
return {
error: new Errors(),
}
},
watch: {
hasErrors: function(){
console.log(this.hasErrors) // No working
}
},
}
</script>
Have you tried a deep watcher for your object?
hasErrors: {
deep: true,
handler () {
// do whatever you would like to do
}
}

Vue redirect after getting store's values

I was trying to make an edit page /edit/:id where input's value comes from the vuex store, but if the store doesn't have a task with the same id of the params I want to redirect to /404, how can I achieve this? tried created() but it didn't work. Im using Vue3
<script>
export default {
name: "EditTaskForm",
data() {
return {
newTaskDesc: "",
};
},
created() {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
if (!task) this.$router.push("/404");
},
mounted() {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
this.newTaskDesc = task.desc;
},
methods: {
submitEdit() {
this.$store.dispatch("editTask", {
newTaskDesc: this.newTaskDesc,
id: Number(this.$route.params.id),
});
this.$router.push("/");
},
},
};
</script>
Try follwoing:
beforeRouteEnter(to, from, next) {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
if (!task) this.$router.push("/404");
},
<script>
export default {
name: "EditTaskForm",
data() { ... },
mounted() { ... },
methods: { ... },
beforeRouteEnter(to, from, next) {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
if (!task) this.$router.push("/404");
}
};
</script>
Navigation Guards

Vue 3 Composition API - How to specify a prop in setup()

I wrote a "loading state" mixin for Vue 2:
export default {
props: {
loading: {
type: Boolean,
default: false
},
},
data () {
return {
innerLoading: false,
}
},
mounted () {
this.innerLoading = !!this.loading
},
methods: {
startLoading () {
this.$emit('update:loading', this.innerLoading = true)
},
stopLoading () {
this.$emit('update:loading', this.innerLoading = false)
},
},
computed: {
isLoading () {
return !!this.innerLoading
},
isNotLoading () {
return !this.innerLoading
},
},
watch: {
loading (loading) {
this.innerLoading = !!loading
},
}
}
I use this mixin for other components to hold the loading state. For example for forms, buttons, tables etc.
Now, Im trying to rewrite this mixin to composition API style for Vue 3. Ideally, I would like to use my loading composable like this:
// components/Table.vue
import 'useLoading' from 'src/composables/loading'
export default defineComponent({
setup () {
const { startLoading, stopLoading, innerLoading } = useLoading()
// ...
return { startLoading, stopLoading, innerLoading, ... }
}
})
My question:
// How can I define the loading prop inside the setup() function?
props: {
loading: {
type: Boolean,
default: false
},
},
Of course I can define my component like this:
import 'useLoading' from 'src/composables/loading'
export default defineComponent({
props: {
loading: {
type: Boolean,
default: false
},
},
setup () {
const { startLoading, stopLoading, innerLoading } = useLoading();
}
})
But imagine, I have 20 components using this mixin/composable. So I want to define that loading prop only ONCE (like I did in mixin).
Is there a way how to do it with composition API?
you may be able to do something like this
import {withProps, useLoading} from "src/composables/loading";
export default defineComponent({
props: {
...withProps()
},
setup () {
const { startLoading, stopLoading, innerLoading } = useLoading();
}
})
where withProps is a function that would have your definitions
export const withProps = () => ({
loading: {
type: Boolean,
default: false
},
})
of course it doesn't need to be a function, but in some cases it may be helpful and preemptively making it a function can make api consistent.
Define an Object called loadingProps in separate file called makeLoadingProps:
export const loadingProps = {
loading: {
type: Boolean,
default: false
}
}
then import it inside your component defined using the script setup syntax:
<script setup lang="ts">
import {defineProps} from 'vue'
import { loadingProps } from 'src/composables/makeLoadingProps';
const props = defineProps({
...loadingProps,
//other props
})
const { startLoading, stopLoading, innerLoading } = useLoading(props)
</script>

How to use this data from Vuex inside the component?

What is the best way to use this data from the Vuex store's Action in the needed component?
import axios from 'axios'
export default {
namespaced: true,
state: {
items: []
},
actions: {
fetchCategories ({state, commit}) {
return axios.get('/api/v1/categories')
.then(res => {
const categories = res.data
commit('setItems', {resource: 'categories', items: categories}, {root: true})
return state.items
})
}
}
}
Component
export default {
components: {
ThreadCreateModal,
ThreadList
},
data () {
return {
...
}
},
computed: {
...
},
created () {
...
},
methods: {
...
}
</script>
Where and how should I use that action for binding the data in this component?
Use mapState by import it from vuex and call it in computed:
computed: {
...mapState(['items']), // if you dont use namespace
...mapState("your_module_name", ['items'] ) // if you use namespace
},
then you can access it by this.items.
However, you can access it directly this.$store.state.items

how to get nested getters in vuex nuxt

i have store/index.js like this
new Vuex.Store({
modules: {
nav: {
namespaced: true,
modules: {
message: {
namespaced: true,
state: {
count: 0,
conversations: [],
},
getters: {
getCount: state => {
return state.count;
},
},
mutations: {
updateCount(state) {
state.count++;
},
},
actions: {},
},
requests: {
namespaced: true,
state: {
friends: [],
},
getters: {
getFriends: state => {
return state.friends;
},
},
mutations: {
pushFriends(state, data) {
state.friends.push(data);
},
},
actions: {
pushFriends(commit, data) {
commit('pushFriends', data);
},
},
},
},
},
},
});
i want to use getters in computed property i have tested like this
computed: {
...mapGetters({
count: 'nav/message/getCount',
}),
},
butt getting error
[vuex] unknown getter: nav/message/getCount
what is am missing here
i also want to make separate folder for every modules like my nav have 3 modules message, requests & notifications
i did try but nuxt blow up my codes
I think your index is wrong, the correct thing is to separate the modules independently, something like this:
in your store/index.js
export const state = () => ({
config: {
apiURL: 'https://meuapp.com'
}
})
export const mutations = { }
export const actions = { }
// getters
export const getters = {
test: state => payload => {
if (!payload)
return {
message: 'this is an messagem from index without payload test.', // you don't need pass any payload is only to show you how to do.
result: state.config
}
else
// return value
return {
message: 'this is an message from index test with payload.',
result: state.config, // here is your index state config value
payload: payload // here is yours params that you need to manipulate inside getter
}
}
}
here is your store/navi.js
export const state = () => ({
navi: {
options: ['aaa', 'bbb', 'ccc']
}
})
export const mutations = { }
export const actions = { }
// getters
export const getters = {
test: state => payload => {
if (!payload)
return {
message: 'this is a messagem from nav store without payload test.', // you don't need pass any payload is only to show you how to do.
result: state.navi
}
else
// return value
return {
message: 'this is an messagem from navi test with payload.',
result: state.navi, // here is your index state config value
payload: payload // here is yours params that you need to manipulate inside getter
}
}
}
then in your component you can use as a computed properties:
<template>
<div>
without a paylod from index<br>
<pre v-text="indexTest()" />
with a paylod from index<br>
<pre v-text="indexTest( {name: 'name', other: 'other'})" />
without a paylod from navi<br>
<pre v-text="naviTest()" />
with a paylod from navi<br>
<pre v-text="naviTest( {name: 'name', other: 'other'})" />
access getters from methods<br>
<pre>{{ accessGetters('index') }}</pre>
<pre v-text="accessGetters('navi')" />
<br><br>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters({
indexTest: 'test',
naviTest: 'navi/test'
})
},
methods: {
accessGetters (test) {
if (test && test === 'index' ) {
console.log('test is', test) // eslint-disable-line no-console
return this.indexTest()
}
else if (test && test === 'navi') {
console.log('test is:', test) // eslint-disable-line no-console
return this.naviTest()
}
else {
return 'test is false'
}
}
}
}
</script>
Whenever possible separate your code into smaller parts, one part for each thing. This makes it easier for you to update and keep everything in order.
Hope this helps.
I came here to find a way to access the getters of a module that was nested inside another module in Vue.js and the following solution worked for me:
this.$store.getters['outerModuleName/innerModuleName/nameOfTheGetter']
Maybe this helps someone with a similar problem.