vuex. Can't get access to object variable - vue.js

look at my vuex code:
export default new Vuex.Store({
state: {
article: {},
slug: '',
},
actions: {
getArticleData(context, payload) {
axios.get('/api/article-json', { params: {slug:payload } }).then((response) =>{
context.commit('setArticleData', response.data.data);
});
}
},
getters: {
articleLikes(state) {
return state.article.statistic.likes;
},
articleViews(state) {
return state.article.statistic.views;
}
},
mutations: {
setArticleData(state, payload) {
state.article = payload;
},
setCurrentSlug(state, payload) {
state.slug = payload;
},
setCurrentStatistic(state, payload) {
state.statistic = payload;
}
}
})
the code works like this - getArticleData action set Data in article object.
In create() hook set article opbject:
{
"id": 14,
"title": "Deserunt ea natus pariatur sunt eum voluptatem.",
"img": "https://via.placeholder.com/600/5F113B/FFFFFF/?text=LARAVEL:8.*",
"body": " fugiat.",
"created_at": "1 месяц назад",
"comments": [
{
"id": 40,
"subject": "Impedit inventore quis.",
"body": "Qui rem ut beatae expedita nemo.",
"created_at": "2 дня назад"
},
{
"id": 41,
"subject": "Nam et sit.",
"body": "Dolor voluptas error eos quod.",
"created_at": "2 дня назад"
},
],
"tags": [
{
"id": 1,
"label": "Ut"
},
{
"id": 3,
"label": "Fugiat"
},
],
"statistic":
{
"likes": 7,
"views": 153
}
}
This object formed by Laravel resource
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'img' => $this->img,
'body' => $this->body,
'created_at' => $this->createdAtForHumans(),
'comments' => CommentResource::collection($this->whenLoaded('comments')),
'tags' => TagResource::collection($this->whenLoaded('tags')),
'statistic' => new StateResource($this->whenLoaded('state')),
];
}
I can't get access to article.statistic.views and article.statistic.likes from getters.
In views component i have code
computed: {
viewsNumber() {
return this.$store.getters.articleViews;
},
}
and template
<template>
<span class="badge bg-danger">{{viewsNumber}} <i class="far fa-eye"></i></span>
</template>
this is my console errors
app.js?id=80d6f0783c93a26b57e1:20787 [Vue warn]: Error in render: "TypeError: Cannot read property 'views' of undefined"
found in
---> <ViewsComponent> at resources/js/components/ViewsComponent.vue
<ArticleComponent> at resources/js/components/ArticleComponent.vue
<Root>
app.js?id=80d6f0783c93a26b57e1:22054 TypeError: Cannot read property 'views' of undefined
for comments and tags no problems. In comments component i have access to comments^
computed: {
article() {
return this.$store.state.article;
},
},
<div v-for="comment in article.comments">
<div class="toast showing" style="min-width: 100%;">
<div class="toast-body">
{{comment.body}}
</div>
</div>
</div>
What problem with article.statistic.likes and article.statistic.views?

The problem is it cannot verify whether views or likes will be available in articles.statistic.
You are not ensuring where articles.statistic will be defined or not.
So the simple solution is do a check before accessing any child element of articles.statistic
e.g. You can redefine your method like -
....
getters: {
articleLikes(state) {
if(state.article.statistic){
return state.article.statistic.likes;
} else {
return ...
}
},
articleViews(state) {
if(state.article.statistic){
return state.article.statistic.views;
} else {
return ...
}
}
},
....

Related

how to get list of product under categoryID in vuejs

I'm working on an app in which if a category is clicked it goes to another route and list the product under that category.
This is my html template category id routes :
<div>
<p>{{category.type}}</p>
<div
v-for="product in products(category._id)"
:key="product._id"
>
{{product.title}}
</div>
</div>
My script tag :
<script>
import axios from "axios";
export default {
name: "Product",
components: {},
data() {
return {
categoryID: null,
category: [],
products: [],
show: null
};
},
mounted() {
axios
.get(`http://localhost:5000/api/categories/${this.$route.params.id}`, {})
.then(response => {
console.log(response);
this.category = response.data.category;
})
.catch(error => {
console.log(error);
error;
});
axios
.get(`http://localhost:5000/api/products`, {})
.then(response => {
console.log(response);
this.product = response.data.product;
})
.catch(error => {
error;
});
},
};
</script>
Response I get in my console, the list of categories :
{
"success": true,
"category": {
"_id": "6220db08e861f3dbbaf21e39",
"products": [],
"type": "3 bedroom",
"__v": 0
}
}
This is my list of products :
{
"products": [
{
"_id": "6256711a0e42d6c5ab370e9d",
"category": {
"_id": "6220db08e861f3dbbaf21e39",
"products": [],
"type": "3 bedroom",
"__v": 0
},
"title": "galaxy s22",
"price": 200,
"stockQuantity": 1,
"__v": 0,
"id": "6256711a0e42d6c5ab370e9d"
},
]
}
my category api
router.get(`/categories/:id`, async (req, res) => {
try {
let category = await Category.findOne({
_id: req.params.id
})
res.json({
success: true,
category: category
});
} catch (err) {
res.status(500).json({
success: false,
message: err.message
});
}
});
How can I get the list of products under a specific category ? I get the {{ category.type }}
My suggestion is to filtered out the products in the script and then bind that in the template as you are getting only one category object from an API.
Demo :
new Vue({
el: '#app',
data: {
// This is a mock data just for a demo (actual will come from API)
category: {
"success": true,
"category": {
"_id": "6220db08e861f3dbbaf21e39",
"products": [],
"type": "3 bedroom",
"__v": 0
}
},
// This is a mock data just for a demo (actual will come from API)
products: [
{
"category": {
"_id": "6220db08e861f3dbbaf21e39"
},
"title": "galaxy s22"
}, {
"category": {
"_id": "6220db08e861f3dbbaf21e40"
},
"title": "galaxy s10"
}, {
"category": {
"_id": "6220db08e861f3dbbaf21e39"
},
"title": "galaxy s30"
}
]
},
mounted() {
this.products = this.products.filter(({ category }) => category._id === this.category.category._id);
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p>{{category.category.type}}</p>
<ul>
<li v-for="product in products" :key="product._id">
{{ product.title }}
</li>
</ul>
</div>
To get the products for a category you can filter your products :
let categoryProducts = this.products.filter((p) => {
return p.category.type == this.category.type
})
But perhaps its better to only retrive products of that category from the server, without filtering on js, if you don't need the products of other categories
i don't know your api but something like that perhaps :
axios.get(`http://localhost:5000/api/products?category=` + this.category.id, {})
.then(response => {
console.log(response);
this.product = response.data.product;
})
.catch(error => {
error;
});
mounted() {
axios
.get(`http://localhost:5000/api/categories/${this.$route.params.id}`)
.then(response => {
this.category = response.data.category;
})
.catch(error => {
console.log(error);
});
}
By above api you will get category object which have products. so you don't need any other api. so now just loop using category.products.
<div
v-for="product in category.products"
:key="product._id"
>
{{product.title}}
</div>

TypeError: $data.quotation.company is undefined

I have problem when I try to render data in my Vue3 application.
data() {
return {
quotation: [],
}
},
mounted() {
this.getQuotation()
},
methods: {
async getQuotation() {
this.$store.commit('setIsLoading', true)
const quotationID = this.$route.params.id
await axios
.get(`/api/v1/quotations/${quotationID}/`)
.then((response) => {
this.quotation = response.data
})
.catch(error => {
console.log(error)
})
},
}
The weird part is when I try to access {{quotation.company}} in template I can see the element of "company" without any error. The error TypeError: $data.quotation.company is undefined occurs when I get in depth {{quotation.company.name}} for example.
Axios is getting data like:
{
"id": 20,
"company": {
"id": 4,
"name": "xxxx",
"slug": "xxx",
"categories": [
{
"id": 7,
"name": "xxxx",
"slug": "xxxx"
}
],
"street2": "",
"postcode": 11111,
},
"home_type": "xxxx",
"Urgency": "",
"description": "xxxx",
}
I really don't understand :/
First the quotation property should be declared as an object like quotation: {}, then at the first rendering the field company is not available yet, so you need to add some conditional rendering as follows :
<div v-if="quotation.company" >
{{quotation.company.name}
</div>

Vuejs get dynamic form values

I have select and input component with made by buefy. Everything is ok till I realize how can I get the data.
I'm sort of new on vuejs. So I will be glad if you help me out.
I'm getting dynamic form from backend
So my question is how can get values these inputs and submit to backend again with getOffer() methot.
Here is my codes;
Input.vue
<template>
<b-field :label="fieldLabel">
<b-input
:name="inputName"
:type="inputType"
:maxlength="inputType == 'textarea' ? 200 : null"
></b-input>
</b-field>
</template>
<script>
export default {
name: "Input",
props: {
inputType: {
type: String,
required: true,
default: "text",
},
inputName: {
type: String,
required: true,
},
fieldLabel: {
type: String,
required: true,
}
}
};
</script>
Home.vue
<template>
<div class="container is-max-desktop wrapper">
<div v-for="element in offer" :key="element.id">
<Input
v-model="element.fieldValue"
:value="element.fieldValue"
:fieldLabel="element.fieldLabel"
:inputType="element.fieldType"
:inputName="element.fieldName"
v-if="element.fieldType != 'select'"
class="mb-3"
/>
<Select
v-model="element.fieldValue"
:fieldLabel="element.fieldLabel"
:options="element.infoRequestFormOptions"
:selectName="element.fieldName"
v-if="element.fieldType == 'select'"
class="mb-3"
/>
</div>
<b-button type="is-danger" #click="getOffer()">GET</b-button>
</div>
</template>
<script>
import axios from "axios";
import Select from "../components/Select.vue";
import Input from "../components/Input.vue";
export default {
name: "Home",
data() {
return {
offer: [],
};
},
components: {
Select,
Input,
},
methods: {
getOfferForm() {
axios({
method: "get",
url: `/GETDYNAMICFORM`,
})
.then((response) => {
this.offer = response.data;
})
.catch(() => {
this.$buefy.toast.open({
duration: 3000,
message: "oops",
position: "is-bottom",
type: "is-danger",
});
});
},
getOffer() {
console.log(this.offer);
},
},
created() {
this.getOfferForm();
},
};
</script>
Example Dynamic Form Response like;
[
{
"id": 58,
"fieldLabel": "Name Surname",
"providerLabel": "Name Surname",
"fieldName": "nmsrnm",
"fieldType": "text",
"fieldValue": null,
},
{
"id": 60,
"fieldLabel": "E-mail",
"providerLabel": "E-mail",
"fieldName": "e_mail_60",
"fieldType": "email",
"fieldValue": null,
},
{
"id": 2,
"fieldLabel": "Budget",
"providerLabel": "Budget",
"fieldName": "bdget",
"fieldType": "select",
"fieldValue": "",
"infoRequestFormOptions": [
{
"id": 1,
"orderNum": 0,
"optionValue": 0,
"optionText": "Select",
"minValue": null,
"maxValue": null
},
{
"id": 2,
"orderNum": 1,
"optionValue": 1,
"optionText": "10-30",
"minValue": 10,
"maxValue": 30
}
]
}
]

How to populate a v-select component with json data coming from axios.get

I am having a hard time trying to populate a v-select component with data from backend. The backend data is in json format.
The array 'items_category' is not storing the data. So I see "No data available" in my v-select. Can anyone help me. Thanks. This is my code:
<v-select v-model="category" :items="items_category" chips dense></v-select>
data () {
return {
category: '',
items_category: [],
categories: [],
i: 0
}
},
created () {
this.initialize()
},
methods: {
initialize () {
axios.get('http://localhost:4000/categories', {
})
.then(response => {
this.categories = response.data
for (this.i=0; this.i<this.categories.length; this.i++) {
this.items_category[this.i] = this.categories[this.i].category_name
}
})
.catch(function (error) {
console.log(error);
})
}
}
This is my json (http://localhost:4000/categories):
[
{
"id": 1,
"category_name": "Name 1",
"category_description": "Description 1"
},
{
"id": 2,
"category_name": "Premium",
"category_description": "Description 2"
},
{
"id": 3,
"category_name": "Free",
"category_description": "Description 3"
}
]
Ok I got it correct by using the push method
this.items_category.push(this.categories[this.i].category_name)

Filter on nested (recursive) data ( Vue 2 )

Here is an example of my JSON data :
"data":[
{
"id":01,
"name":"test",
"parent_id":null,
"children":[
{
"id":15,
"name":"subChild",
"parent_id":21,
"children":[
{
"id":148,
"name":"subSubChild",
"parent_id":22,
"children":[
....
]
}
]
}
]
},
I would like to filter this level by level. I have made this method :
computed: {
filteredData: function () {
let filterData = this.filter.toLowerCase()
return _.pickBy(this.data, (value, key) => {
return _.startsWith(value.name.toLowerCase(), filterData)
})
},
This work for only the first "level" and I tried several solutions but none worked for children.
So, I would like to be able to filter by several levels.
If you have an idea!
Thank you
A recursive function could come in handy for this particular purpose.
Try the following approach, and for better view, click on Full page link next to the Run code snippet button down below.
new Vue({
el: '#app',
data() {
return {
filter: '',
maintainStructure: false,
data: [{
"id": 01,
"name": "test",
"parent_id": null,
"children": [{
"id": 15,
"name": "subChild",
"parent_id": 21,
"children": [
{
"id": 148,
"name": "subSubChild",
"parent_id": 22,
"children": []
},
{
"id": 150,
"name": "subSubChild3",
"parent_id": 24,
"children": []
}
]
}]
}]
}
},
methods: {
$_find(items, predicate) {
let matches = [];
for (let item of items) {
if (predicate(item)) {
matches.push(item);
}
else if (item.children.length) {
let subMatches = this.$_find(item.children, predicate);
if (subMatches.length) {
if (this.maintainStructure) {
matches.push({
...item,
children: subMatches
});
}
else {
matches.push(subMatches);
}
}
}
}
return matches;
},
filterBy(item) {
return item.name.toLowerCase().startsWith(this.filter.toLowerCase());
}
},
computed: {
filteredData() {
return this.$_find(this.data, this.filterBy);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<label>Filter by <code>item.name</code>:</label>
<input v-model.trim="filter" placeholder="e.g. subsub" />
</div>
<div>
<label>
<input type="checkbox" v-model="maintainStructure" /> Maintain structure
</label>
</div>
<hr />
<pre>{{filteredData}}</pre>
</div>
Note that I'm prefixing the function with $_ to sort of mark it as private function (as recommended in this Style Guide) since we're not going to invoke it anywhere else.