Why I cannot render AJAX JSON value? - javascript

Here is my code:
<ul>
<li v-for="value in RandomTopic" :key="value.id">{{ value.title }}</li>
</ul>
export default {
data() {
return {
RandomTopic: null
}
},
mounted() {
///some method to get data from remote server
console.log(res.data);
this.RandomTopic = res.data;
}
}
I want to render all the data from the remote server in front end. However, after the program ran, it reports this error:
Cannot set property 'RandomTopic' of undefined ; at api request success callback function
TypeError: Cannot set property 'RandomTopic' of undefined
The console.log(res.data); log the JSON successfully so it seems not the problem of AJAX or remote server.
And also, here is a sample of the JSON:
[
{
"id": 421,
"title": "sample1",
"image_url": "bus.png"
},
{
"id": 535,
"title": "sample78",
"image_url": "car.png"
}
]
What's wrong with my code ? I am a beginner of Vue 3, please help me.

As per the error you mentioned, Issue is basically related to the scope of this. Looks like you are using regular function instead of arrow function ( => {...}) which don't provide their own this binding (it retains this value of the enclosing lexical context).
Reference - Arrow function
.then(res => {
this.RandomTopic = res.data;
})

Related

Define response structure in Adonisjs with Middleware

I want to define the response structure of my requests in the simplest way, and the first thing that comes in my mind to do this is a middleware.
My endpoints are returning the response content correctly:
{{base_url}}/users returns a list of users:
{
[
{
"id": 44,
"name": "some name"
[...]
}
]
}
What I want to do (in all requests) is to add the fields status and data (or any other I'd like to add), like this:
{
"status": 200,
"data": [
{
"id": 44,
"name": "some name"
[...]
}
]
}
I've created a middleware that waits for the resolution but I'm not able to get the content nor add some property to it.
[...]
async handle ({request, response}, next) {
await next()
const content = response._lazyBody.content
content.status = response.response.statusCode
}
[...]
I know this will not work but I want something similar to this. I've looked in Adonis docs and forum, but no answers fit to my needs.
Any help will be welcome
You can extend Response By extending the core. The simplest way is to create a file inside start folder and name it hooks.js and copy and paste the content below inside it:
const { hooks } = use('#adonisjs/ignitor')
const Response = use('Adonis/Src/Response')
hooks.after.providersBooted(() => {
Response.macro('customJson', function (status, data) {
this.status(status).json({
status,
data
})
})
})
this piece of code extends the Response module and add customJson method to it which takes two arguments, status and data, and send them back to the client.
And here you can see how to use it:
Route.get('/users', async ({ response }) => {
let status = ''// whatever you want
let data = ''// whatever you want
return response.customJson(status, data)
})

rendering a nested objected in reactjs

I am having a hard time to render a nested object on to a reactjs page
import React, { Component } from "react";
import Toolpanel from "./Todopanel";
import Toollist from "./Toollist";
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
city: "Auckland",
cityWeather: {}
};
this.updateUser = this.updateUser.bind(this);
}
updateUser(entry) {
console.log(entry);
let item = {
text: entry,
key: Date.now()
};
this.setState(prevstate => {
return {
users: prevstate.users.concat(item)
};
});
}
componentDidMount() {
let apiId = "***************************";
let city = this.state.city;
let ApiString =
"http://api.openweathermap.org/data/2.5/weather?q=" +
city +
"&APPID=" +
apiId;
fetch(ApiString)
.then(results => results.json())
.then(data => this.setState({ cityWeather: data }));
}
render() {
let test = this.state.cityWeather;
return (
<div>
<Toolpanel parentUpdate={this.updateUser} />
<div>wind speed : {test.wind.speed} </div>
</div>
);
}
}
export default App;
I have added my JSON file that I received from my weather API
//Json file
{
"coord": { "lon": 174.77, "lat": -36.85 },
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 293.7,
"pressure": 1018,
"humidity": 77,
"temp_min": 293.15,
"temp_max": 294.26
},
"visibility": 10000,
"wind": { "speed": 5.1, "deg": 360 },
"clouds": { "all": 92 },
"dt": 1553672420,
"sys": {
"type": 1,
"id": 7345,
"message": 0.0043,
"country": "NZ",
"sunrise": 1553624951,
"sunset": 1553667823
},
"id": 2193733,
"name": "Auckland",
"cod": 200
}
I am trying to render the wind speed from the JSON to my page.. but is throwing me a error message saying "TypeError: Cannot read property 'speed' of undefined"...Please help. I am fairly new to ReactJs.
If you look at the code, here is the sequence of events:
Component is created, i.e. constructor is called
Component is mounted, i.e. componentDidMount is called
componentDidMount starts an async request to fetch the data which is then parsed and set in state.
render method tries to read the data from state.
Now, since the request in #3 is an async one, it may not have completed in time when the render method has been called the first time.
So, you need to check if your request has completed or failed or is running.
You can use that to conditionally render the content in your render method.
Recommended reading
The official reactjs blog entry on async rendering with examples of when data is fetched from an external resource
You're not wrong the way you approached it. The error you're getting is because the fetch you're performing is taking some time, and render first executes without having the data populated.
So first time it gets in your render method the value of test = {}. So test.wind.speed will throw an error.
Instead, show a loading state of some sort or simply return null until the call is performed:
render() {
let test = this.state.cityWeather;
if (!test) {
return 'Loading...';
}
....
}
You are accessing the properties too fast since fetch is an asynchronous call it will take some time but your render fires before that already.
Use it like this
{ test && <div>wind speed : {test.wind.speed} </div>}
Initially your test will be null as you haven't received any response from your API so you should check the variable presence before using it. So just check if it is present before using it like this:
render() {
let test = this.state.cityWeather;
return (
<div>
<Toolpanel parentUpdate={this.updateUser} />
<div>wind speed : {test && test.wind && test.wind.speed ? test.wind.speed : ''} </div>
</div>
);
}
Since you didn't post ToolPanel Component implementation, I may be wrong (I'm missing some information). But, I'm also pretty sure that your problem is not having a loading variable.
Basically, the first time render() method is called, you have this.state.cityWeather to be an empty object {}; that is because you fetch the data in componentDidMount(). Thus, the first time render() is called, being this.state.cityWeather empty, you cannot access this.state.cityWeather.wind.speed because you don't have the property wind in this.state.cityWeather!
So, usually, the common way to do this is adding a property loading in the state, and setting it to true in the constructor. Then, in the callback of the fetch, while you set the data in this.state.cityWeather, you also set loading to true.
Finally, in the render() method you wrote a conditional rendering: if this.state.loading === true, then you print a simple paragraph like <p>I'm retrieving info!</p>, otherwhise, if this.state.loading === false, you can render what you want.

Audio not pausing in ReactJS

componentWillMount() {
console.log('Component WILL MOUNT!')
axios.get('/channels').then( (res) => {
//console.log(res.data.data.playList);
let playlists = [];
res.data.data.playList.map((value, key) => playlists.push(new Audio(value.url)));
this.setState((prevState) => {
return { audioList: playlists, categories: res.data.data.playList }
}, () => console.log(this.state.audioList));
}).catch( (err) => {
console.log(err);
});
}
**I also call this in componentDidUpdate() **
The above code that I used in my ReactJS web app to retrieve data from my DB that looks something like:
{
"_id": {
"$oid": "5a2b903abcf92a362080db4f"
},
"name": "test",
"playList": [
{
"url": "https://p.scdn.co/mp3-preview/a3fd5f178b7eb68b9dba4da9711f05a714efc966?cid=ed36a056ee504173a3889b2e55cbd461",
"artist": "Lil Pump",
"songName": "D Rose",
"_id": {
"$oid": "5a2c5631e54ca10eb84a0053"
}
},
{
"url": "https://p.scdn.co/mp3-preview/155643656a12e570e4dda20a9a24d9da765b9ac5?cid=ed36a056ee504173a3889b2e55cbd461",
"artist": "Tee Grizzley",
"songName": "From The D To The A (feat. Lil Yachty)",
"_id": {
"$oid": "5a2c5631e54ca10eb84a0054"
}
}
],
"__v": 0
}
I retrieve the url for each songs and store it inside my state this.state.audioList to make a playable list.
I access each song with an index
So, this.state.audioList[0] would be the first song.
When I try to play this music by doing
this.state.audioList[0].play(), this totally works fine.
The problem is when I try to pause it.
this.state.audioList[0].pause() does not pause the song for some reason.
I am assuming that it is because the this.state.audioList is getting updated every time and the Audio object that I am trying to pause is a new object that has nothing to do with the one currently being played.
Am I right? If so, is there a solution to this issue?
Please help!
That should work in componentWillMount although componentDidMount is preferred, see : https://reactjs.org/docs/react-component.html#componentdidmount
Quoted from this link (remote endpoint being your axios URL here) :
If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
But you're almost certain that won't work if you put your axios.get() request in componentDidUpdate, because this method is called each time your component has been updated and re-rendered.
From the React Component Lifecycle document, you'll see that componentWillMount and componentDidMount both stay in the Mounting section (that is, they are called only once when the components DOM element are inserted in the DOM), whereas componentDidUpdate is in the Updating section, and therefore is called each time your component's state or props are changed (what happens when your axios.get() promise is fulfilled).
Also, as map returns an array, why not assign its returned value to your audioList ? Here is an example that you may want to try (untested though, sorry !):
componentDidMount() {
console.log('Component DID MOUNT!')
axios.get('/channels').then( (res) => {
//console.log(res.data.data.playList);
this.setState({
audioList: res.data.data.playList.map(value => new Audio(value.url)),
categories: res.data.data.playList
});
}).catch( (err) => {
console.log(err);
});
}
Hope this helps!

Ember get filtered data from the store/mirage

I am trying to build an app for smarthome devices with ember. I already have installed mirage and declare an array which is called data. It holds arrays like households, users and devices. Now I am stuck with get filtered data from the store and i am really confused by the behaviour of the store. For this reason I already read some equal topics like this Filtering store data in ember but this doesn´t work in my case.
Actually this is my router.js
Router.map(function() {
this.route('about');
this.route('users', function() {
});
this.route('households', function() {
this.route('index', { path: '/:user_id' })
this.route('rooms',{ path: '/:household_id' });
this.route('devices');
});
});
If I am going to households.index I want to see all households which have the user-id in his member-array. The following code snipped shows an example of my data-structure.
"users": [
{
"id":101,
"forename":"Peter",
"surname":"Peterson",
"memberIn":[
501
]
},
{
"id":102,
"forename":"Anna",
"surname":"Peterson",
"memberIn":[
501
]
}
]
"households":[
{
"id":501,
"name":"Zuhause",
"admin":131000,
"member":[
101,
102
]
}
I am calling the route household.index like this {{#link-to "households.index" user.id}} and my route in household.index looks like this
model(params) {
//holt alle Haushalte und gibt dann nur die Haushalte weiter, die auch den aktuellen Benutzer als Member haben.
return this.get('store').findAll('household').then(results => results.filter((site) => {
return site.get('member').filter(x => x == params.user_id).length > 0;
}));
}
And my config.js part at mirage like this:
this.get('/households', function(db, request) {
return { households: data.households };
});
This works fine!
But in my opinion the server is responsible for giving me the data I am requesting for. So all I want is that I send a specific request and just get the households that I need.
My attempt:
index.js:
export default Route.extend({
model(params) {
return this.get('store').find('household', params.user_id);
}
});
config js part:
this.get('/households/:id', function(db, request) {
console.log('household get');
console.log(data.households.filter(x => x.member.filter(x => x == request.params.id).length > 0));
return { households: data.households.filter(x => x.member.filter(x => x == request.params.id).length > 0) };
});
Debug Error:
Error while processing route: households.index payload.data is null
I cant understand why this error occurs. The log gives me the array i want to return.
Make sure you are using RestSerializer in mirage,
// mirage/serializers/application.js
import { RestSerializer } from 'ember-cli-mirage';
export default RestSerializer;
and for the right format refer RESTAdapterAPI.
Just ensure are you returning data in the below format,
for the single response,
{
"posts": {
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
}
}
and for more than one,
{
"posts": [
{
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
},
{
"id": 2,
"title": "Rails is omakase",
"author": "D2H"
}
]
}
By default Ember Data ships with the JSONAPISerializer and this assumes a few things about the way your server (in this case mirage) is formatting data. In this case the document that mirage is returning is expected to generally conform to the JSON API Spec and have a top-level member data where the response's primary data resides.
If you're looking to stick to your custom formatted responses you may want to check out the Ember Data's JSONSerializer. Otherwise, returning the following should get you closer:
return { data: db.households.filter(x => x.member.filter(x => x == request.params.id).length > 0) };
or you could leverage the JSONAPISerializer that ships with Mirage
See more here Ember Guides | Serializers

CouchDB list function throwing TypeError (point is undefined)

I am attempting to get a list out of my database. The documents look like:
{
"class": "lists",
"collection": "symptoms",
"order": "6",
"en": "Headache",
"_id": "9022034e7d5ecd0efab0762c5b7f0c04"
}
There are an arbitrary number of "collection"s.
A view function simply returns a bunch of objects in the class "lists":
// Emit lists
exports.viewlist = {
map: function(doc) {
if (doc.class === 'lists') {
emit(
doc.collection, {
order: doc.order,
name: doc.en
});
}
}
};
I wrote a list function to try to filter the output to just the list that I want.
exports.viewlist = function(head, req) {
var row;
start({
code: 200,
headers: {
'Content-Type': 'text/json; charset=utf-8',
}
});
while (row = getRow()) {
if (row.collection === req.l) {
send(JSON.stringify(row.value));
}
}
};
CouchDB throws an error when I visit the URL of the list:
http://localhost:5984/dev/_design/emr/_list/viewlists/viewlist?l=symptoms
{"error":"TypeError","reason":"{[{<<\"message\">>,<<\"point is undefined\">>},
{<<\"fileName\">>,<<\"/usr/share/couchdb/server/main.js\">>},
{<<\"lineNumber\">>,1500},\n {<<\"stack\">>,
<<\"(\\\"_design/emr\\\",[object Array],
[object Array])#/usr/share/couchdb/server/main.js:1500\
()#/usr/share/couchdb/server/main.js:1562\
#/usr/share/couchdb/server/main.js:1573\
\">>}]}"}
I can't figure out where I'm going wrong here.
I also ran into this error and what causes it, as hinted at by #Pea-pod here Submitting form to couchDB through update handler not working, is not defining properly your exports in the couchapp's design documents. In our case it was as list function that couldn't be called and instead displayed a 500 error with Type error and point is undefined in the couchdb log.
We use kanso and in the app.js we hadn't required the list file. We had:
module.exports = {
rewrites: require('./rewrites'),
views: require('./views'),
shows: require('./shows')
};
Changing it to the following solved the problem:
module.exports = {
rewrites: require('./rewrites'),
views: require('./views'),
shows: require('./shows')
lists: require('./lists'),
};
Can I suggest to a moderator to change the title of this question to include point is undefined which is the error that shows up in the CouchDB log when this type of error is made, in order to help others find it more easily?

Categories