Render an image before loading the component in vue js - javascript

I have a Vue application in which i display list of events and every event individually, when i visit the page of the selected link i get an error in my console says GET http://localhost:1337/undefined 404 (Not Found) then the image loads
i used this method to set the id of the event to the component
export default {
data: function() {
return {
id: this.$route.params.id,
e: {},
users: []
}
},
methods: {
issueTicket: function(id, user) {
}
},
created(){
this.$http.get('http://localhost:1337/api/v1/event/find', { params : { id : this.id } }).then(result => {
this.e = result.body.result[0];
})
}
}
is there a way to get rid of this error ? i'm kind of new to Vue JS

You should add your frontend code, in order to make clear where the error occurs.
A first wild guess: You try to access an image like
<img :src="e.img">
However, your e has no .img property until it's loaded. So you might want to consider to set
e: null
Initially and add a v-if for your page
<div class="this is your page div" v-if="e">
<img :src="e.img">
...
This will ensure that you are not accessing undefined properties of e
In addition you should consider not mixing code styles
created() { .. }
vs
created: function() { ... }

Related

Froala Instance not working on edit mode of my vueJS3 application

I have a crud page, it is only a one file that works for both editing existing content and creating a new one, I am using froala editor on product description. I did the config of the froala, and it works fine when i try to create a new product but it is not working on the editing side also not throwing any error. Here is what i did:
I am using Vue3 for the project,
Firstly i defined and imported froala,
data() {
return {
product: undefined as Product<Populated>,
froala: {
instance: null as FroalaTypes.FroalaEditor,
config: froala.baseConfig
}
}
},
Then i did the config on mounted hook
mounted() {
this.froala.instance = FroalaEditor(
'.froala-editor',
froala.extendBase({
events: {
contentChanged: () => {
this.product.description = this.froala.instance.html.get()
this.$forceUpdate()
}
}
})
)
},
unmounted() {
froala.destroyAllInstances()
},
When i print out this.froala.instance on console, it prints an empty array
But all works fine on create mode
What is the problem with this?

Wait for data in mapstate to finish loading

I have stored a userProfile in Vuex to be able to access it in my whole project. But if I want to use it in the created() hook, the profile is not loaded yet. The object exists, but has no data stored in it. At least at the initial load of the page. If I access it later (eg by clicking on a button) everything works perfectly.
Is there a way to wait for the data to be finished loading?
Here is how userProfile is set in Vuex:
mutations: {
setUserProfile(state, val){
state.userProfile = val
}
},
actions: {
async fetchUserProfile({ commit }, user) {
// fetch user profile
const userProfile = await fb.teachersCollection.doc(user.uid).get()
// set user profile in state
commit('setUserProfile', userProfile.data())
},
}
Here is the code where I want to acess it:
<template>
<div>
<h1>Test</h1>
{{userProfile.firstname}}
{{institute}}
</div>
</template>
<script>
import {mapState} from 'vuex';
export default {
data() {
return {
institute: "",
}
},
computed: {
...mapState(['userProfile']),
},
created(){
this.getInstitute();
},
methods: {
async getInstitute() {
console.log(this.userProfile); //is here still empty at initial page load
const institueDoc = await this.userProfile.institute.get();
if (institueDoc.exists) {
this.institute = institueDoc.name;
} else {
console.log('dosnt exists')
}
}
}
}
</script>
Through logging in the console, I found out that the problem is the order in which the code is run. First, the method getInstitute is run, then the action and then the mutation.
I have tried to add a loaded parameter and played arround with await to fix this issue, but nothing has worked.
Even if you make created or mounted async, they won't delay your component from rendering. They will only delay the execution of the code placed after await.
If you don't want to render a portion (or all) of your template until userProfile has an id (or any other property your users have), simply use v-if
<template v-if="userProfile.id">
<!-- your normal html here... -->
</template>
<template v-else>
loading user profile...
</template>
To execute code when userProfile changes, you could place a watcher on one of its inner properties. In your case, this should work:
export default {
data: () => ({
institute: ''
}),
computed: {
...mapState(['userProfile']),
},
watch: {
'userProfile.institute': {
async handler(institute) {
if (institute) {
const { name } = await institute.get();
if (name) {
this.institute = name;
}
}
},
immediate: true
}
}
}
Side note: Vue 3 comes with a built-in solution for this pattern, called Suspense. Unfortunately, it's only mentioned in a few places, it's not (yet) properly documented and there's a sign on it the API is likely to change.
But it's quite awesome, as the rendering condition can be completely decoupled from parent. It can be contained in the suspensible child. The only thing the child declares is: "I'm currently loading" or "I'm done loading". When all suspensibles are ready, the template default is rendered.
Also, if the children are dynamically generated and new ones are pushed, the parent suspense switches back to fallback (loading) template until the newly added children are loaded. This is done out of the box, all you need to do is declare mounted async in children.
In short, what you were expecting from Vue 2.

Submit a form from a Modal in Vue js

I need to send a form from a modal. Not using a full Vue app, but inserting Vue.js in my HTML page.
I tried a lot of unsuccesful things with my current modal, so I reduced it to the basic modal example I used for the first time https://v2.vuejs.org/v2/examples/modal.html
For the form, I used also the most basic form validation example at https://v2.vuejs.org/v2/cookbook/form-validation.html (I have it working in other places).
And I have created this unsuccessful fiddle:
https://jsfiddle.net/JIBRVI/03qnok9m/53/
Vue.component('modal', {
template: '#modal-template'
})
// start app
// eslint-disable-next-line no-new
new Vue({
el: '#app',
data: {
showModal: false,
errors: [],
name: ''
},
methods: {
checkForm: function (e) {
if (this.name) {
return true
}
this.errors = []
if (!this.name) {
this.errors.push('Name required.')
}
e.preventDefault()
}
}
})
In the basic modal example I added the form with a field, a submit button and a placeholder to show errors. Also the input field «name» and the array «errors» to the data section in the app. I also added the «checkForm» method.
The main error says:
Property or method "checkForm" is not defined on the
instance but referenced during render. Make sure that this property is
reactive, either in the data option, or for class-based components, by
initializing the property
Maybe the main page can communicate with the modal, so data and methods from the main page can’t be used.
I also tried to make a component with the form, but it didn’t work either. I can’t communicate with the modal.
Any help will be aprreciated.
You need to move the name and checkform methods to the modal component. You have currently defined those two in the app component and are trying to access it from the modal which is in the modal component.
Vue.component('modal', {
template: '#modal-template',
data(){
return {
name: '',
errors: [],
}
},
methods: {
checkForm: function (e) {
if (this.name) {
return true
}
this.errors = []
if (!this.name) {
this.errors.push('Name required.')
}
e.preventDefault()
}
}
})

How to focus on a specific input text after the component is rendered?

I'm struggling to make this work properly every time I rendered the component by pushing it using the router object something like this this.$router.push('/Home/mypath'); which will focus only on the first input text element with index = 0 after the component is rendered even if I passed another value for index. Basically, I passed an index value to the ref of the input text element which is inside the v-for loop of a component and so at mounted(), I have something like this
mounted() {
this.$nextTick(() =>
{
this.$refs.newInp[index].focus();
});
}
but it keeps focusing on the first input element even though I passed a value of 1 or 2. When I looked at the console log, it shows this error on the console.
TypeError: Cannot read property '0' of undefined
pointing on this line this.$refs.newInp[index].focus();
Sample Code to fetch the data in the v-for
async GetContentDetails() {
let testRes =
await axios.get('myUrl/api', {
params: {
ContentId: this.$cookie.get('OpV-ContId')
}
}).then(res => this.contentItems = res.data)
.then()
.catch(error => console.log(error));
this.testData = testRes;
}
Template:
<div v-for="(contItem, index) in contentItems" :key="contItem.commentId">
<textarea class="reply-commented" ref="newInp"></textarea>
</div>
How to fix this type of issues? What is the solution for this?
Thanks.
From what I understood, you want to focus a textarea after fetching some data, that said trying to focus inside the mounted method wont work because you can't tell if the data has been fetch and the textareas already exist in the DOM.
So the best way to do this is to focus after being sure the data has been fetched, inside the then callback.
new Vue({
el: '#app',
data: {
posts: [],
index: 3 // Change this to focus whatever textarea you want
},
mounted () {
this.fetchItems();
},
methods: {
fetchItems () {
const url = 'https://jsonplaceholder.typicode.com/posts'
axios.get(url).then(response => {
this.posts = response.data
this.$nextTick(_ => {
this.$refs.newInp[this.index].focus()
})
})
}
}
});
<div id="app">
<div v-for="(post, index) in posts" :key="post.id">
<textarea class="reply-commented" ref="newInp" v-text="post.body"></textarea>
</div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
For few days of research and thorough testing and observation on DOM behavior on how vue.js renders component and of course basing on the suggestions from other folks on this thread. I realized you can't really focus in the created/mounted properties on a specific index of an element within the for loop particulary in this case the input text element if the data being fetch to bind on the component is coming from the server due to its asynchronous behavior and you have to wait until the component is completely rendered. So I found a solution at least on my case to use a dynamic watcher either in the created or mounted properties and set a dummy or duplicate data properties for the default change of the data properties for the purpose of only to activate the watcher to focus on the specific element after the component has been rendered. This how it looks like. Hope this help to folks that encountering the same scenario as me.
created() {
this.GetContentDetails();
this.$watch('commentItems2', function () {
this.$nextTick(() => {
this.$refs.newRep[mapCredential.state.index2].focus();
});
});
},
methods: {
async GetComment2() {
let testRes =
await axios.get('myUrl/api/GetContent/GetComments')
.then(this.GetReply2())
.catch(error => console.log(error));
this.commentItems = testRes.data;
this.commentItems2 = testRes.data;
},
}

Framework 7 Vue how to stop Firebase from listening to changes when on different pages?

Suppose I have pageA where I listen for a firebase document changes
export default {
mounted() {
this.$f7ready(() => {
this.userChanges();
})
},
methods: {
userChanges() {
Firebase.database().ref('users/1').on('value', (resp) => {
console.log('use data has changed');
});
}
}
}
Then I go to pageB using this..$f7.views.current.router.navigate('/pageB/')
If on pageB I make changes to the /users/1 firebase route I see this ,message in the console: use data has changed, even though I'm on a different page.
Any way to avoid this behavior, maybe unload the page somehow?
I tried to stop the listener before navigating away from pageA using Firebase.off() but that seems to break a few other things.
Are you properly removing the listener for that specific database reference? You'll have to save the referenced path on a dedicated variable to do so:
let userRef
export default {
mounted() {
this.$f7ready(() => {
this.userChanges();
})
},
methods: {
userChanges() {
userRef = Firebase.database().ref('users/1')
userRef.on('value', (resp) => {
console.log('use data has changed');
});
},
detachListener() {
userRef.off('value')
}
}
}
That way you only detach the listener for that specific reference. Calling it on the parent, would remove all listeners as the documentation specifies:
You can remove a single listener by passing it as a parameter to
off(). Calling off() on the location with no arguments removes all
listeners at that location.
More on that topic here: https://firebase.google.com/docs/database/web/read-and-write#detach_listeners

Categories

Resources