How can I solve "cannot read properties of undefined" in Strapi 4? - javascript

I'm integrating Strapi 4 into my website, and I'm following the documentation, but I keep getting an error of "cannot read properties of undefined (reading 'Headline')" when I try and test.
The data is showing in the devtools network tab, but it continues to give the error when attempting to pull into the front end. I've tried using 'headline' vs 'Headline' but to know avail. I know it changed between 3 and 4 where the .attributes. is needed, but I don't see anything else that could be causing any issues.
Below is all the relevant code. Thank you for your help in advance!
Single Article Page
<template>
<div>
<v-container>
<v-row>
<v-col cols="12" lg="8" xl="8">
<div>
<div>
<v-card flat color="transparent">
<v-card-text>
<div class="text-h4 font-weight-bold primary--text pt-4">
<h4>{{ PressRelease.attributes.Headline }}</h4>
</div>
<div class="text-body-1 py-4">
{{ PressRelease.attributes.Subheading }}
</div>
<div class="d-flex align-center justify-space-between">
<div class="d-flex align-center">
<v-avatar color="accent" size="36">
<v-icon dark>mdi-newspaper</v-icon>
</v-avatar>
<div class="pl-2 text-body-1">
{{ moment(PressRelease.created_at).format("MMMM Do YYYY") }}
</div>
</div>
</div>
<v-divider class="my-4"></v-divider>
<vue-markdown-it
v-if="PressRelease.attributes.Body"
:source="PressRelease.attributes.Body"
id="editor"
/>
<v-divider class="my-8"></v-divider>
</v-card-text>
</v-card>
</div>
</div>
</v-col>
</v-row>
</v-container>
<div class="mt-5 mb-5">
<hr style="margin-top:0 !important;" class="blue-divider" />
</div>
</div>
</template>
<script>
import Vue from "vue";
var moment = require("moment");
import VueMarkdownIt from "vue-markdown-it";
export default {
name: "pressRelease",
components: {
VueMarkdownIt,
},
filters: {},
data() {
return {
PressRelease: [],
moment: moment,
};
},
async beforeRouteEnter(to, from, next) {
try {
var PressRelease = await Vue.$pressReleaseService.findOne(to.params.id);
return next((vm) => {
vm.PressRelease = PressRelease;
});
} catch (err) {
console.log(err);
next(false);
}
},
async beforeRouteUpdate(to, from, next) {
try {
this.PressRelease = await Vue.$pressReleaseService.findOne(to.params.id);
return next();
} catch (err) {
console.log(err);
next(false);
}
},
};
</script>
Service.js File
// press release service
export default {
install: (Vue) => {
//private
const route = '{{ROUTE}}';
//public
Vue.$pressReleaseService = {
find(params) {
return Vue.axios.get(route, {
params: params
});
},
findOne(key, params) {
return Vue.axios.get(`${route}/${key}`, {
params: params
});
},
};
}
};

After doing some more working, I found the answer. With Strapi v4 I had to go down an extra level:
return next((vm) => {
vm.PressRelease = PressRelease.data.data;
});

Related

I want to display an error message when an email address exists in a Vuetiify form

assumption
I am creating a user registration form using Vuetify. I want to display an error if an email address already exists at registration, how can I implement this functionality using Vuetify's textfield?
The configuration is loading the email componentized in the Form. It uses v-model between components with setter and getter to be updated reactively.
What we want to achieve
I want to use v-text-field of Vuetify to show an error if the email address already exists.
Code
Email
UserFormTextFieldEmail
<template>
<v-row justify="center">
<v-col cols="12" md="10" sm="10">
<v-text-field
v-model="setEmail"
type="text"
label="email"
prepend-icon="mdi-email"
:rules="rules"
/>
<p class="caption mb-0" />
</v-col>
</v-row>
</template>
<script>
export default {
props: ['email'],
data () {
return {
rules: [
v => !!v || '',
v => /.+#.+\..+/.test(v) || ''
]
}
},
computed: {
setEmail: {
get () { return this.email },
set (newVal) { return this.$emit('update:email', newVal) }
}
}
}
</script>
Form
<v-card class="pa-7 ma-10 mx-auto" max-width="600">
<div class="login-logo">
<img
:src="logoImg"
width="70px"
>
</div>
<v-form
ref="form"
v-model="isValid"
>
<v-container>
<UserFormTextFieldUserName :name.sync="userInfo.name" />
<UserFormTextFieldEmail :email.sync="userInfo.email" :error.sync="errorMessage" /> // email
<UserFormTextFieldPassword :password.sync="userInfo.password" />
<v-row justify="center">
<v-col cols="12" md="10" sm="10">
<v-btn
:disabled="!isValid || loading"
:loading="loading"
block
class="white--text"
color="deep-purple lighten-1"
#click="signup"
>
・・・
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card>
</div>
</template>
<script>
import '#/assets/css/user-form.scss'
import logoImg from '~/assets/images/login_logo.png'
export default {
auth: false,
data () {
return {
isValid: false,
loading: false,
logoImg,
show: false,
userInfo: {
name: '',
email: '',
password: ''
},
errorMessage:''
}
},
methods: {
signup () {
this.$axios.post('/api/v1/auth', this.userInfo)
.then((response) => {
this.$store.commit('alertSwitchSuccess', true)
setTimeout(() => {
this.$store.commit('alertSwitchSuccess', false)
this.$router.replace(`/user/login`)
}, 2000)
})
.catch((e) => {
})
}
}
}
</script>
I wanted to do the same thing before, and here's my solution:
methods: {
checkemail() {
axios
.get("/api/v1/auth")
.then((response) => {
this.emails = response.data.map((a) =>
a.email);
});
},
},
This function return an Array of all the emails
data() {
return {
//email input v-model
email: "",
//declare the emails array
emails: [],
rules: [
// your rules
(v) =>
(v && this.emails.indexOf(this.email)<0)||
"this email is already existing",
],
I hope that helps

Data transfer problem between two siblings components (Vue js)

I have three components and one of those is the parent of the others I'm trying to pass an object called talk between siblings emiting it inside an event from FollowedBrowser to LeftBar and then passing it via prop from LeftBar to TalksList component, after that another event is emited by TalksList and listened one more time for LeftBar and finally this component redefine the talk object as an empty object.
This is my parent component LeftBar.
<template>
<v-navigation-drawer width="25%" permanent clipped app light>
<talks-list v-if="inRoute('messages')" :talk="talk" #talkAdded="talkAdded()"/>
<template v-if="inRoute('messages')" v-slot:prepend>
<followed-browser #newTalk="addTalk($event)"/>
</template>
</v-navigation-drawer>
</template>
<script>
import FollowedBrowser from "./FollowedBrowser";
import TalksList from "./TalksList";
import { mapGetters } from "vuex";
export default {
data(){
return {
talk: {}
}
},
components: {
FollowedBrowser,
TalksList
},
methods: {
addTalk(talk){
this.talk = talk;
},
talkAdded(){
this.talk = {};
}
}
}
</script>
And this is my two children:
TalksList.vue
<template>
<v-container class="my-0 px-5">
<v-list flat>
<v-list-item-group class="my-0">
<div class="ma-0 pa-0" v-for="(talk, index) in talks" :key="index">
<v-divider v-if="talk.divider"></v-divider>
<v-list-item v-else class="px-2" style="cursor: pointer">
<template>
<v-list-item-avatar>
<v-img :src="correctedImageUrl(talk.recipient)"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>
<span class="blue--text text--lighten-1">{{ completeName(talk.recipient) }}</span>
</v-list-item-title>
<v-list-item-subtitle>
<span>{{ talk.recipient.username }}</span>
</v-list-item-subtitle>
</v-list-item-content>
</template>
</v-list-item>
</div>
</v-list-item-group>
</v-list>
</v-container>
</template>
<script>
import axios from "axios";
export default {
data(){
return {
talks: []
}
},
props: {
talk: {
type: Object,
default: null,
required: true
}
},
watch: {
talk(val){
if(val){
this.talks.splice(0, 1, val);
this.$emit("talkAdded");
}
}
}
}
</script>
FollowedBrowsed.vue
<template>
<div style="display: inline">
<v-dialog scrollable v-model="dialog" max-width="400px" max-height="500px">
<v-card :loading="loading">
<v-text-field dense outlined color="blue lighten-1" label="Nombre de usuario" class="px-5" append-icon="mdi-magnify" v-model="browsedUsername"/>
<v-divider></v-divider>
<v-card-text style="height: 300px;" class="px-2">
<v-list>
<v-list-item class="px-2" style="cursor: pointer" v-for="listUser in filteredFollowed" :key="listUser.id" #click.prevent="newTalk(listUser)">
<v-list-item-content>
<v-list-item-title>
<span class="blue--text text--lighten-1">{{ completeName(listUser) }}</span>
</v-list-item-title>
<v-list-item-subtitle>
<span>{{ listUser.username }}</span>
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import axios from "axios";
export default {
data(){
return {
browsedUsername: "",
loading: false,
dialog: false,
skeleton: true,
followed: []
}
},
watch: {
dialog(dialog){
if(!dialog){
this.browsedUsername = "";
this.item = null;
}
}
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
user: "auth/user"
}),
filteredFollowed(){
return this.followed.filter((user) => {
return user.username.toLowerCase().indexOf(this.browsedUsername.toLowerCase()) !== -1;
})
}
},
mounted(){
axios.get("all_followers_followed/followed")
.then((response) => {
if(response.data){
this.followed = response.data;
this.skeleton = false;
}
})
.catch((error) => {
console.log(error)
});
},
methods: {
async newTalk(user){
this.loading = "blue lighten-1";
await axios.post("messages/new_talk", {recipient_id: user.id})
.then((response) => {
if(response.data){
this.dialog = false;
this.$emit("newTalk", {
messages_number: 0,
recipient: user,
sender: this.user
});
}
})
.catch((error) => {
console.log(error);
});
}
}
}
When the newTalk method is called inside FollowedBrowser component newTalk event is emited but after that my screen freezes like the app was inside infinite loop and I don't know why. I omitted some code that I thought was irrelevant.
Can anybody help me.
Thanks in advance.
I solved... So simple, I just had to get a copy of talk prop inside TalksList, inside watch just put this:
watch: {
talk(val){
if(val){
if(this.talks.length){
this.talks.unshift({ divider: true });
}
let buffer = new Object();
let talk = new Object();
buffer.data = val;
talk = buffer.data;
this.talks.unshift(talk);
}
}
},

I can't display properly v-data-table data: ''Invalid prop: type check failed for prop "items". Expected Array, got Object''

I'm starting a project in which I had to use Vue. I'm actually really new to this, so I'm learning on the go. I do apologize in advance since this question have answered before, however, I didn't really understand the solutions provided, which is why I'm here asking myself.
Well, I was trying to display some data on my Data Table (more specifically, v-data-table from Vuetify). I was able to get the data from the API, but, for some reason it doesn't show me anything. Thanks to Vuex I can see that the mutation worked because on the console on Google Chrome I can see the Array of objects. But as I said, it still does't show me a single thing on the table, it even says 'no data available'. Some errors that I get are things like '[Vue warn]: Invalid prop: type check failed for prop "items". Expected Array, got Object' and 'TypeError: this.items.slice is not a function'.
Here is the code from List.vue
<template>
<v-container id="data-tables" tag="section">
<div class="text-right">
<v-btn class="mx-2" fab dark color="primary" :to="{ name: 'UserCreate' }">
<v-icon dark>mdi-plus</v-icon>
</v-btn>
</div>
<base-material-card
color="indigo"
icon="mdi-vuetify"
inline
class="px-5 py-3"
>
<template v-slot:after-heading>
<div class="display-2 font-weight-light">
Lista de Empleados
</div>
</template>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
class="ml-auto"
label="Search"
hide-details
single-line
style="max-width: 250px;"
/>
<v-divider class="mt-3" />
<v-data-table
:headers="headers"
:items="users"
:search.sync="search"
:sort-by="['name', 'office']"
:sort-desc="[false, true]"
multi-sort
>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-eye
</v-icon>
<v-icon
small
class="mr-2"
#click="editItem(item)"
:to="{ name: 'UserUpdate' }"
>
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
</v-data-table>
</base-material-card>
</v-container>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'UsersTable',
data() {
return {
headers: [
{
text: 'Nombre',
value: 'empleado.nombre',
},
{
text: 'Apellido',
value: 'empleado.apellido',
},
{
text: 'Dirección',
value: 'empleado.direccion',
},
{
text: 'Correo Electrónico',
value: 'email',
},
{
text: 'Teléfono',
value: 'empleado.telefono',
},
{
sortable: false,
text: 'Actions',
value: 'actions',
},
],
loader: true,
search: undefined,
}
},
created() {
this.$store.dispatch('users/fetchUsers')
},
computed: {
...mapState(['users']),
},
methods: {},
mounted() {},
}
</script>
And the code from user.js, where the fetchUsers it's coming from.
import auth from '#/api/auth'
export const namespaced = true
export const state = {
users: [],
}
export const mutations = {
SET_USERS(state, users) {
state.users = users
},
}
export const actions = {
fetchUsers({ commit, dispatch }) {
auth
.getAllAccounts()
.then((response) => {
commit('SET_USERS', response.data)
})
.catch((error) => {
const notification = {
type: 'error',
message: 'There was a problem fetching users: ' + error.message,
}
dispatch('notification/add', notification, { root: true })
})
},
}
Thanks in advance.
You are not getting the correct user from vuex, because is namespaced, change to:
computed: {
...mapState('users',['users']),
},
MapState helper dosen't work the same way like the other helpers because the state module isn't registred in the global namespace. So namespacing your module will help or you do it in this way:
computed: {
...mapState({
users: state => state.FilenameOfYourModule.users
})
}

Vue Application Using Chartkick Doesn't update chart correctly

I know my mount and computed methods work since I can send that out to be displayed, but my graph only renders after I change the data value in the template to the correct thing that one time and doesn't render it again. Should I be using method so it calculates it everytime or do I need to use Vuex for this issue? Thanks
<template>
<v-app>
{{getDates}}
<v-card class="mt-3 mx-auto" max-width="500">
<v-sheet
class="v-sheet--offset mx-auto"
color="cyan"
elevation="12"
min-width="125%"
max-width="calc(100% - 32px)"
>
<line-chart :data="dates"></line-chart>
</v-sheet>
<v-card-text class="pt-0">
<div class="title font-weight-light mb-2">Lead</div>
<div class="subheading font-weight-light grey--text">TEST TEXT</div>
<v-divider class="my-2"></v-divider>
<v-icon class="mr-2" small>mdi-clock</v-icon>
<span class="caption grey--text font-weight-light">TEST TEXT</span>
</v-card-text>
</v-card>
<p>108J View</p>
</v-app>
</template>
<script>
import axios from "axios";
export default {
name: "E108J",
components: {},
data() {
return {
value: [246, 446, 675, 510, 590, 610, 760],
datas: [],
dates: {},
lead: [],
datesnlead: {}
};
},
mounted() {
axios
.get("http://localhost:3000/E108J")
.then(response => (this.datas = response.data));
},
computed: {
getDates() {
for (let i = 0; i < this.datas.length; i++) {
this.dates[this.datas[i].date] = this.datas[i].lead;
}
return this.dates;
},
getLead() {
for (let i = 0; i < this.datas.length; i++) {
this.lead[i] = this.datas[i].lead;
}
return this.lead;
}
}
};
</script>

How to loop through an API endpoint JSON Object in Vue & Axios?

I have an API endpoint which is a JSON object. I am using Axios and Vuejs to fetch the data into the DOM, but I am only able to get the whole object. When I tried to loop throught with the v-for directive it doesn't output the specific item in the object.
I fetched the data using Axios like so:
export default {
name: 'Reviews',
props: {
title: String
},
data(){
return {
info: []
}
},
// Life cycle hook that calls axios
mounted(){
axios.get('http://dev.muvtravel.com/index.php/explore/test?tripid=6590').then(response => {
console.log(response.data)
this.info = response.data
})
}
}
Then tried loop through using v-for
<div v-for="(item, index) in info" :key="index">
{{ item.establishment_address }}
{{ item.phone }}
</div>
<template>
<div class="reviews container-fluid">
<h1 class="text-center">{{ title }}</h1>
<b-container>
<b-row>
<b-col cols="12" sm="12" md="12" lg="4" xl="4">
Column 1
</b-col>
<b-col cols="12" sm="12" md="12" lg="8" xl="8">
Column 2
</b-col>
</b-row>
</b-container>
<div v-for="(item, index) in info" :key="index">
{{ item.establishment_address }}
{{ item.phone }}
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Reviews',
props: {
title: String
},
data(){
return {
info: []
}
},
// Life cycle hook that calls axios
mounted(){
axios.get('http://dev.muvtravel.com/index.php/explore/test?tripid=6590').then(response => {
console.log(response.data)
this.info = response.data
})
}
}
</script>
<style scoped lang="scss">
</style>
Any help will be appreciate it
So I checked to see if the API endpoint in your code was publicly open - it is.
From looking at your payload, the reason your code isn't working is because you're trying to iterate on an object. The data object that you're returning is the FULL payload from that API endpoint, which is an object {"success": true, "data": [...]"}.
To more clearly illustrate what I'm talking about, here's an example fetch you can run:
fetch(yourAPIEndpoint).then(res => res.json()).then(data => console.log(data));
When I run that, it prints this to the console:
{success: true, data: Array(15)}
When I edit the console.log above to output data.data like so:
fetch(yourAPIEndpoint).then(res => res.json()).then(data => console.log(data.data));
I get the array of locations that you are trying to set.
TL;DR: You need to set this.info = response.data.data.
Happy coding!

Categories

Resources