How to render data with AJAX in Vuejs 2? - ajax

I have problems while trying to set data in a Vue instance
(example in: https://jsfiddle.net/a5naw6f8/4/ )
I assign the values to data property by AJAX. I can render exam object but when I want to access to an inner array exam.questions.length I get an error:
"TypeError: exam.questions is undefined"
This is my code :
// .js
new Vue({
el: '#app',
data:{
id: "1",
exam: {}
},
created: function () {
this.fetchData();
},
methods: {
fetchData: function () {
$.get("https://dl.dropboxusercontent.com/s/15lkz66wy3fut03/data.json",
(data) => {
this.exam = data;
}
);
}
}
});
<!-- .html -->
<div id="app">
<h1>ID: {{ id }}</h1>
<h1>Exam: {{ exam }}</h1>
<!-- exam.questions array is undefined -->
<!-- <h1>Questions length: {{ exam.questions.length }}</h1> -->
</div>
I found a similar problem but it was relative to this reserved word scope:
Vuejs does not render data from ajax
Does someone know how to fix this problem?

You have a couple problems:
Your JSON file isn't valid. The trailing commas after the the false cause the parser to choke.
You aren't parsing your JSON (which is why you probably didn't see the JSON error). Once you fix the JSON file, you need to parse it with something like this.exam = JSON.parse(data) rather than assigning the string to this.exam
Once you do that, you can test for the object in the template with v-if before trying to access its properties:
<h1 v-if="exam.questions">Questions length: {{ exam.questions.length }}</h1>
v-if will prevent the attempt to access exam.questions.length until the ajax request has returned data

Related

Laravel vue.js 2 response view won't output parameters

I am new to vue.js and recently been assigned a project to learn it and refactor old code to vue.js like some of the existing pages already have been and I am having some issues. vue2
My question:
I have a get request with a controller that has a response something like this:
return Response::view('somebladefile', ['title' => 'some_title']);
Within the blade file I include a js file which will be responsible for vue
<script src="{{ cdnMix('somefile.js') }}"></script>
somefile.js contents:
const IndexPage = Vue.component('indexpage',require('./somepath/IndexPage.vue').default);
window.indexPageInstance = new IndexPage().$mount('#vuecontainerid');
So now within IndexPage.vue i would like to access variable 'title' that I passed with the response to my blade file originally. What would be the best way one would go about it? Tried few ways I found on YT/Google but without success, this is my code currently, any pointers would be appreciated, thanks!
<template>
<HeaderComponent></HeaderComponent>
</template>
<script>
const HeaderComponent = require('./somepath/HeaderComponent.vue').default;
export default {
name: 'indexpage',
props: ['data'],
components: {
'HeaderComponent': HeaderComponent,
},
data: function() {
return {
// why doesn't it work!!: '',
}
},
mounted: function() {
console.log(this.data);
}
}
</script>
Vue works, but I can't seem to be able to access 'title' variable.
Also I would like to be able to access 'title' within other components like the HeaderComponent I have within indexpage

What is the correct way to call a method property inside an object in a store?

I am working on my first Sveltekit app project, converting from a React based project. I got stuck with a bit of a tricky method inside an object inside a store. Before I refactor the entire data structure to something perhaps more intuitive ( this is still a data structure derived from the React way of doing things ) , I wanted to understand how to do this properly in case I need it again.
An object stores some meta data about a dataset.
const dataSetsIndex = [
{id: ':sample:alphabet',
permanent: true,
metadata: {
title: 'Alphabetic Letter Frequency',
source: 'https://observablehq.com/#d3/bar-chart-transitions'
},
attachments: {
data: import ('/src/dataSets/alphabet.json'),
}
}
];
export default dataSetsIndex;
There would be more objects with the { id: permanent: metadata: { title: source: } attachments: { data: ()=> } } structure in this dataSetsIndex component prop.
But when my program eventually tries to access the data from an external JSON ti display on a route in Sveltekit , I can't seem to find a way to make that Promise from the import('/src/dataSets/alphabet.json') method return.
Following the docs, I tried an interface that destructures the data and stores it in a writable - the data in the JSON file is fields:[] , rows:[]
import DataSets from "../dataSets/dataSetsIndex.js";
import {writable} from "svelte/store";
export const dataSetsStore = writable([]);
let destructedDataSets = () => {
const dataSets = DataSets.map( ( dataset, index ) =>
{
return {
id: index,
title: dataset.metadata.title,
source: dataset.metadata.source,
fields: dataset.attachments.data().then(
(success) => { return success.fields},
(fail) => {return fail})
}
}
)
dataSetsStore.set(dataSets);
};
destructedDataSets();
then bringing that in to a route which is reactive
<script>
import {dataSetsStore} from "../stores/dataSetsStore.js"
</script>
{#each $dataSetsStore as metadataObject}
<div>
{metadataObject.title.toUpperCase()}
{metadataObject.fields}
</div>
{/each}
only displays ALPHABETIC LETTER FREQUENCY [object Promise]
What am I missing here?
OK, so I figured it out and this is working. I looked at this related post to help me understand the role of {#await} in the context of this particular structure... my code excerpt below uses Bulma to draw up a table for the results
<script>
import {dataSetsStore} from "../stores/dataSetsStore.js"
</script>
{#each $dataSetsStore as metadataObject}
{#await metadataObject.importDataFrom()}
<p>loading...</p>
{:then theFields}
<table class="table">
<thead>{metadataObject.title.toUpperCase()}</thead>
{#each theFields.fields as f}
<th>
<abbr class="has-background-success is-size-5 p-1" title={f.name}>
{f.name.trim().slice(0,10)}
</abbr>
<span class="has-text-info is-size-8">
{f.type}
</span>
</th>
{/each}
</table>
{:catch error}
<p>Something went wrong: {error.message}</p>
{/await}
{/each}

VueJS: How do I initialise data so it shows in my template on component load

This is driving me nuts!
//ProfilePage.vue
<template>
<div>
<p>{{ this.$data.profile.desc }}</p>
<profileImage v-bind:profile="profile"></profileImage>
<profileText v-bind:profile="profile" v-on:updateData="updateDesc"></profileText>
</div>
</template>
<script>
import profileText from './ProfileText.vue';
import profileImage from './ProfileImage.vue';
export default {
name: 'profilePage',
component: {
profileText,
profileImage
},
data() {
return {
profile: {
image: '',
desc: ''
}
}
},
created() {
this.fetchProfile();
},
methods: {
async fetchProfile() {
const uri = 'http://localhost:8000/api/......get';
const response = await axios.get(uri);
.then(response => this.updateProfileData(response.data))
},
updateProfileData(data) {
this.$data.profile.image = data['image'];
this.$data.profile.desc = data['description'];
},
updateDesc(data) {
this.$data.profile.desc = data.desc;
},
}
}
</script>
<style scoped>
</style>
In the above .vue file. I execute a fetch to the back end which successfully returns the correct data from the DB. I successfully save the data returned to the data() part of the file. Next I import a component (the code for which is below) from the correct page, add it as a component and add it to the template and use v-bind to pass in profile from the data() part of this page. Now the imported/child component looks like this:
//ProfileText.vue
<template>
<div>
<form #submit="update">
<textarea v-model="description"></textarea>
<button type="submit">Submit</button>
</form>
<div>
<template>
<script>
export default{
name: "profileText",
props: ["profile"],
data() {
return {
description: this.$props.profile.desc
}
},
methods: {
update(e) {
e.preventDefault();
const newData = {
desc: this.$data.description
}
this.$emit('updateData', newData);
}
}
}
</script>
<style scoped>
</style>
I use v-model to bind the contents of "description" in data() to the contents of the textarea. I have it so when i edit the text area and click submit the function emits the data to the parent component which triggers a function that updates the parent data() with the new data from the text area of this component. This parts works perfectly.
However, the part I can't figure out is when the parent component executes the fetch and binds the response with the child component, why isn't the response showing up in the textarea when it loads.
I have done the exact same thing with another lot of components and it works fine on that lot. The only difference there is that with that lot the execute function brings back a response with an array of data and I use v-for(x in xs) and then bind the attributes of data() with the component x. That's the only difference. What am I missing in the code above to load the data sent in "profile" from the parent component with v-bind to the textarea in the child component with v-model. In data() i have it to return description: this.$props.profile.desc, but it is not initialising description with profile.desc - Going nuts here $#! I've been staring at the code for two days straight trying different things.
mounted Function
Called after the instance has been mounted, where el is replaced by
the newly created vm.$el. If the root instance is mounted to an
in-document element, vm.$el will also be in-document when mounted is
called.
Note that mounted does not guarantee that all child components have
also been mounted. If you want to wait until the entire view has been
rendered, you can use vm.$nextTick inside of mounted:
mounted: function () { console.log('component mounted'); }
This hook is not called during server-side rendering.
Source
Component Lifecycle
Few things:
Your syntax has errors in the ProfileText.vue file. Missing closing template and div tags
<template>
<div>
<form #submit="update">
<textarea v-model="description"></textarea>
<button type="submit">Submit</button>
</form>
</div>
</template>
You are mixing async/await and .then(). It should be:
async fetchProfile() {
const uri = 'http://localhost:8000/api/......get';
const response = await axios.get(uri);
this.updateProfileData(response.data)
},

Vue: props keep coming back as undefined

I'm checking my Root and child (Topbar) component and each time, the foo prop is undefined in each one. I'm not sure what I am doing wrong since I am defining it.
app.js
window.Vue = require('vue');
Vue.component('Topbar',
require('./components/Topbar.vue').default);
new Vue({
el: "#app",
data: {
activeTab: "cart"
},
props: {
foo: "testing props"
}
})
Topbar.vue
<template>
<div class="cont">
<div class="tabs">
<a v-on:click="handleClick('account')" v-bind:class="[ activeTab === 'account' ? 'active' : '' ]">Account</a>
<a v-on:click="handleClick('cart')" v-bind:class="[ activeTab === 'cart' ? 'active' : '' ]">{{foo}}</a>
</div>
</div>
</template>
<script>
export default {
props: ['activeTab'],
data() {
return {
activeTab: "CCC"
}
},
props: ["foo"],
}
</script>
laravel-blade-template file
<div id = "app">
<Topbar :foo="testing props"></Topbar>
</div>
Try removing the colon in front of foo.
If you add the colon, then it's executed as JavaScript, and the variable testing doesn't exist. If you remove the colon, then you should receive "testing props" as a string.
<div id = "app">
<Topbar foo="testing props"></Topbar>
</div>
There are 3 problems with your code.
You are defining a prop in app.js. You cannot assign a value to it. The syntax you are using cannot be used to pass a variable. More about the syntax about declaring a prop here.
In Topbar you are assigning a value to a prop, but are using the binding syntax. Vue will execute the following js code testing props which is not a valid code. You should use this :foo="'testing props'". The reason for the apostrophe inside the quotes is the fact that 'testing props' is a valid string in JS, and it will get assigned to your prop.
You are also defining props 2 times in Topbar.vue. You'll end up with your last definition and that's probably not what you want.
I could identify two errors in your codes:
You should not have props definition in your export
The value testing props that you have assigned to foo is a string and should wrapped with single quotes (').
The following codes should work:
export default {
props: ['activeTab', 'foo'],
data() {
return {
activeTab: "CCC"
}
}
}
And this
<Topbar :foo="'testing props'"></Topbar>

Laravel Echo Vue Js TypeError: Cannot read property 'push' of undefined

I'm having an issue particularly with Vue Js hoping someone can point me in the right direction. I am using Laravel's Echo feature which is connected to Pusher. I currently am getting data back from pusher that part is fine and dandy. The issue I can't seem to figure out is on the client side code. I'm trying to add the new items coming from pusher to the already existing items on my page. However when I use this.items.push() within the Echo channel block I am receiving a console error with TypeError: Cannot read property 'push' of undefined. I am thinking that "this.items" is out of scope?
<div id="app">
<ul id="example-1">
<li v-for="item in items">
#{{ item }}
</li>
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
items: []
},
mounted: function () {
this.listen();
},
methods: {
/**
* Listen to the Echo channels
*/
listen: function() {
// pushed fine from here
this.items.push("dddd");
Echo.channel('test_channel')
.listen('OrderCreated', function(e) {
//TypeError: Cannot read property 'push' of undefined
this.items.push('elkqwejfh')
});
}
}
});
</script>
scope of this changes inside Echo.channel, you have save this in a different variable and use that variable inside instead of this, That's why it work perfectly outside Echo.channel but inside this.items is null, so it throws error. You need to make following changes:
methods: {
/**
* Listen to the Echo channels
*/
listen: function() {
// pushed fine from here
this.items.push("dddd");
var self = this
Echo.channel('test_channel')
.listen('OrderCreated', function(e) {
//TypeError: Cannot read property 'push' of undefined
self.items.push('elkqwejfh')
});
}

Resources