How make json data available for my Vue dynamic routes - vuejs2

I have a List component where I fetch my date from db/blogs.json:
created() {
fetch('http://localhost:3000/blogs')
.then(response => {
return response.json();
})
.then(data => {
this.blogs = data;
})
},
In my BlogDetail.vue I have:
<script>
export default {
data: () => {
return {
blogId:this.$route.params.id
}
},
computed: {
blog() {
return this.blogs.find(
blog => blog.id === this.blogId
)
}
}
}
</script>
But how do I get the blogs data in this component, which I fetched in the List component?
Because now in the <template> section of my BlogDetail.vue I cannot access e.g. {{ blog.name }}
Update:
I try passing blogs with props:
Now I am accepting a prop in BlogDetails.vue:
props: {
blogs: {
type: Array
}
},
But from where (which component), I have to registering the prop like :blogs="blogs"?
Update 2:
This is what I have so far, link to the sandbox

Here is the working sandbox.
Firstly you need to import JSON data from your JSON file correctly. As:
<script>
import ListItem from "./ListItem";
import Blogs from "../../public/db/blogs.json";
export default {
name: "List",
components: {
ListItem
},
data() {
return {
blogs: Blogs.experiences
};
},
created() {}
};
</script>
Have to send props in the router-link as :
<router-link
:to="{ name: 'BlogDetails', params: { id: blog.id,blog:blog }}">More information
</router-link>

You can send props to the child component in the tag name, in your case:
//LIST component(PARENT)
<tamplate>
<BlogDetail :blogs="blogs"></BlogDetail> //CHILD component
</template>

Related

Why action of Vuex returns a promise<pending>?

I have an action in Vuex actions which commit a mutation that it take a payload from the component, that is a number of the index for returning an object, it works fine on Vuex js file meaning that shows the selected item on the console, as I said it gets index from the payload,
but on the component, it gives me Promise <Pending>, why that's happening? for now, I do not use any API for my Nuxt/Vue app, but I will, and for now, I just want to know why this is happening and what is the best solution for solving this
Here my Vuex codes:
export const state = () => ({
articles: [
{
uid: 0,
img: 'https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/1.jpg',
link: '/articles/1',
},
{
uid: 1,
img: 'https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/2.jpg',
link: '/articles/2',
},
],
})
export const getters = {
getArticles(state) {
return state.articles
},
}
export const mutations = {
getSpeceficArticle(state, payload) {
return state.articles[payload]
},
}
export const actions = {
getSpeceficArticle({ commit }, payload) {
commit('getSpeceficArticle', payload)
},
}
and here my component codes:
<template>
<div class="article">
{{ getSpeceficArticle() }}
<div class="article__banner">
<img src="" alt="" />
</div>
<div class="article__text">
<p></p>
</div>
</div>
</template>
<script>
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
// computed: {},
methods: {
async getSpeceficArticle() {
return await this.$store.dispatch('articles/getSpeceficArticle', 0)
},
},
}
</script>
actions are used to update the state they are like mutations but the main difference between them is that actions can include some asynchronous tasks, if you want to get a specific article at given index you should use a getter named getArticleByIndex :
export const getters = {
getArticles(state) {
return state.articles
},
getArticleByIndex:: (state) => (index) => {
return state.articles[index]
}
}
then define a computed property called articleByIndex :
<script>
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
computed: {
articleByIndex(){
return this.$store.getters.articles.getArticleByIndex(0)
}
},
methods: {
},
}
</script>
#Mohammad if you find yourself using a lot of getters/actions etc from Vuex and they're starting to get a little wordy, you can bring in mapGetters from Vuex and rename your calls to something a little more convenient. So your script would become,
<script>
import { mapGetters } from 'vuex'
export default {
name: 'HomeArticlesArticle',
data() {
return {
item: '',
}
},
computed: {
articleByIndex(){
return this.getArticleByIndex(0)
}
},
methods: {
...mapGetters({
getArticleByIndex: 'articles/getArticleByIndex',
})
},
}
</script>
You can add ...mapGetters, ...mapActions to your computed section also.
since there is no web service call in vuex action, try to remove async and await keywords from the component.
Later when you add a webservice call than you can wrap action body in new Promise with resolve and reject and then you can use async and await in component. let me know if this works for you.

Get object from Vuex at specific index

I am getting an array from Vuex and I want an object at 2 positions.
HTML
<p class="">
{{ MainImg[2].para}}
</p>
Vue
export default {
name: "App",
components: { },
data() {
return {
imageQuery: this.$route.params.image,
};
},
computed: {
...mapGetters("design", {
MainImg: ["singleDesigns"]
})
},
created() {
this.fetchDesigns();
},
mounted() {
console.log(this.MainImg);
},
methods: {
fetchDesigns() {
this.$store.dispatch("design/getSingleDesign", this.imageQuery);
}
}
};
But it shows an undefined error.
And When I add MainImg array in Vue data like this.
data() {
return {
imageQuery: this.$route.params.image,
MainImg:[{para:"1"},{para:"2"},{para:"3"},{para:"4"}]
};
It Works.
P.S.-
Store Code-
export const state = () => ({
designs: [],
})
export const getters = {
singleDesigns(state) {
return state.designs;
}
}
I am not adding Action and Mutation because it works fine with other code.
It looks like the array is empty at the first rendering, so you should add a condition to render it :
<p class="" v-if="MainImg && MainImg.length >= 2">
{{ MainImg[2].para}}
</p>

Airtable data into Vue.js

I am new to vue js and have been trying for hours to get airtable data into my application. I am hoping someone could help me as I feel I am almost there! I am using the Airtable NPM package to retrieve the data - https://www.npmjs.com/package/airtable
<template>
<section id="cards-section" class="cards-section">
<div class="centered-container w-container">
<h1>{{ msg }}</h1>
<div class="cards-grid-container">
<Card />
<ul id="example-1">
<li v-for="item in recordsList" v-bind:key="item">
data here {{ item }}
</li>
</ul>
</div>
</div>
</section>
</template>
<script>
import Card from "../components/Card.vue";
import Airtable from "airtable";
export default {
name: "Main",
components: {
Card,
},
props: {
msg: String,
},
data() {
return {
recordsList: [],
};
},
mounted() {
const base = new Airtable({ apiKey: "******" }).base(
"******"
);
base("Table 1")
.select({
view: "Grid view",
})
.firstPage(function (err, records) {
if (err) {
console.error(err);
return;
}
records.forEach( (record) => {
console.log(record.get("Name"));
return record.get("Name")
});
});
},
};
</script>
<style scoped>
</style>
Looking at your code you are probably at the stage that you have succesfully retrieved the data from airtable and seeing some records in your console.log.
Now how to get them from inside that function to your Vue instance:
I will show you two ways to go about this and explain them later:
Method 1: Using a reference to self.
<script>
import Card from "../components/Card.vue";
import Airtable from "airtable";
export default {
name: "Main",
components: {
Card,
},
props: {
msg: String,
},
data() {
return {
recordsList: [],
};
},
mounted() {
// create a reference to this vue instance here.
var self = this;
const base = new Airtable({ apiKey: "******" }).base(
"******"
);
base("Table 1")
.select({
view: "Grid view",
})
.firstPage(function (err, records) {
if (err) {
console.error(err);
return;
}
// now we can set the recordList of our
// vue instance:
self.recordsList = records;
});
},
};
</script>
Method 2: Using javascript arrow function:
<script>
import Card from "../components/Card.vue";
import Airtable from "airtable";
export default {
name: "Main",
components: {
Card,
},
props: {
msg: String,
},
data() {
return {
recordsList: [],
};
},
mounted() {
// no need to create a reference this time.
const base = new Airtable({ apiKey: "******" }).base(
"******"
);
base("Table 1")
.select({
view: "Grid view",
})
.firstPage( (err, records) => {
if (err) {
console.error(err);
return;
}
this.recordsList = records;
});
},
};
</script>
Now what was the problem? In the first example we use a normal javascript anonymous function. this inside a normal javascript anonymous function is not what you expect it to be. We solve this by defining a reference to this (var self = this) somewhere and instead of trying this.recordsList we do self.recordsList inside our anynomous function.
Nem improvements to the javascript language introduced another type of function, the arrow function. One benefit of this function is that this inside this function is the object that you've defined it in. So, this is our vue instance. Now we don't have a problem and can just set this.recordsList.
Other solutions i've ommitted are:
Using Function.bind
async/await

vue.js 2 single file component with dynamic template

I need a single file component to load its template via AJAX.
I search a while for a solution and found some hints about dynamic components.
I crafted a combination of a parent component which imports a child component and renders the child with a dynamic template.
Child component is this:
<template>
<div>placeholder</div>
</template>
<script>
import SomeOtherComponent from './some-other-component.vue';
export default {
name: 'child-component',
components: {
'some-other-component': SomeOtherComponent,
},
};
</script>
Parent component is this
<template>
<component v-if='componentTemplate' :is="dynamicComponent && {template: componentTemplate}"></component>
</template>
<script>
import Axios from 'axios';
import ChildComponent from './child-component.vue';
export default {
name: 'parent-component',
components: {
'child-component': ChildComponent,
},
data() {
return {
dynamicComponent: 'child-component',
componentTemplate: null,
};
},
created() {
const self = this;
this.fetchTemplate().done((htmlCode) => {
self.componentTemplate = htmlCode;
}).fail((error) => {
self.componentTemplate = '<div>error</div>';
});
},
methods: {
fetchTemplate() {
const formLoaded = $.Deferred();
const url = '/get-dynamic-template';
Axios.get(url).then((response) => {
formLoaded.resolve(response.data);
}).catch((error) => {
formLoaded.reject(error);
}).then(() => {
formLoaded.reject();
});
return formLoaded;
},
},
};
</script>
The dynamic template code fetched is this:
<div>
<h1>My dynamic template</h1>
<some-other-component></some-other-component>
</div>
In general the component gets its template as expected and binds to it.
But when there are other components used in this dynamic template (some-other-component) they are not recognized, even if they are correctly registered inside the child component and of course correctly named as 'some-other-component'.
I get this error: [Vue warn]: Unknown custom element: some-other-component - did you register the component correctly? For recursive components, make sure to provide the "name" option.
Do I miss something or is it some kind of issue/bug?
I answer my question myself, because I found an alternative solution after reading a little bit further here https://forum.vuejs.org/t/load-html-code-that-uses-some-vue-js-code-in-it-via-ajax-request/25006/3.
The problem in my code seems to be this logical expression :is="dynamicComponent && {template: componentTemplate}". I found this approach somewhere in the internet.
The original poster propably assumed that this causes the component "dynamicComponent" to be merged with {template: componentTemplate} which should override the template option only, leaving other component options as defined in the imported child-component.vue.
But it seems not to work as expected since && is a boolean operator and not a "object merge" operator. Please somebody prove me wrong, I am not a JavaScript expert after all.
Anyway the following approach works fine:
<template>
<component v-if='componentTemplate' :is="childComponent"></component>
</template>
<script>
import Axios from 'axios';
import SomeOtherComponent from "./some-other-component.vue";
export default {
name: 'parent-component',
components: {
'some-other-component': SomeOtherComponent,
},
data() {
return {
componentTemplate: null,
};
},
computed: {
childComponent() {
return {
template: this.componentTemplate,
components: this.$options.components,
};
},
},
created() {
const self = this;
this.fetchTemplate().done((htmlCode) => {
self.componentTemplate = htmlCode;
}).fail((error) => {
self.componentTemplate = '<div>error</div>';
});
},
methods: {
fetchTemplate() {
const formLoaded = $.Deferred();
const url = '/get-dynamic-template';
Axios.get(url).then((response) => {
formLoaded.resolve(response.data);
}).catch((error) => {
formLoaded.reject(error);
}).then(() => {
formLoaded.reject();
});
return formLoaded;
},
},
};
</script>

How to use Axios with Vue-Multiselect?

New to using Vue-Multiselect. I am using axios to do a GET request from a JSON placeholder to test.
How do I get the title and post id to show up in my drop down?
Right now, I just get [Object Object] - [title] shown in my select box.
<!-- Vue component -->
<template>
<div>
<multiselect v-model='value' :options='posts' :custom-label='postWithTitle' placeholder='Select one' label='title' track-by='id'></multiselect>
{{ value }}
</div>
</template>
<script>
import Multiselect from "vue-multiselect";
import axios from "axios";
export default {
// OR register locally
components: { Multiselect },
data() {
return {
value: null,
posts: []
};
},
created() {
this.getPosts();
},
methods: {
getPosts() {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(response => {
// eslint-disable-next-line
console.log(response);
this.posts = response.data;
})
.catch(error => {
// eslint-disable-next-line
console.log(error);
});
},
postWithTitle(id, title) {
return `${id} - [${title}]`;
}
}
};
</script>
fix:
postWithTitle(option) {
return `${option.id} - [${option.title}]`;
}
explaination:
i saw that when i simply console.logged inside the postWithTitle function:
the custom custom-label attribute was accepting a callback that only accepts one argument. that argument was the entire option object- a single entry of your posts array.