问题
I'm trying to render a list of notes and in that list I would like to include the note's user name based on the user_id stored in the note's table. I have something like this, but at the moment it is logging an error stating Cannot read property 'user_id' of undefined, which I get why.
My question is, in Vue how can something like this be executed?
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{user.name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
});
},
fetchUser(id){
return this.$http.get('http://api/user/' + id )
.then(function(response){
this.user = response.body;
});
}
},
created: function(){
this.fetchNotes(this.$route.params.id)
.then( () => {
this.fetchUser(this.note.user_id);
});
}
UPDATE:
I modified my code to look like the below example, and I'm getting better results, but not 100% yet. With this code, it works the first time it renders the view, if I navigate outside this component and then back in, it then fails...same thing if I refresh the page.
The error I am getting is: [Vue warn]: Error in render: "TypeError: Cannot read property 'user_name' of undefined"
Notice the console.log... it the returns the object as expected every time, but as I mentioned if refresh the page or navigate past and then back to this component, I get the error plus the correct log.
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
for( let i = 0; i < response.body.length; i++ ) {
let uId = response.body[i].user_id,
uNote = this.notes[i];
this.$http.get('http://api/users/' + uId)
.then(function(response){
uNote.user = response.body;
console.log(uNote);
});
}
});
},
}
回答1:
It looks like you're trying to show the username of each note's associated user, while the username comes from a different data source/endpoint than that of the notes.
One way to do that:
- Fetch the notes
- Fetch the user info based on each note's user ID
- Join the two datasets into the
notes
array that your view is iterating, exposing auser
property on eachnote
object in the array.
Example code:
let _notes;
this.fetchNotes()
.then(notes => this.fetchUsers(notes))
.then(notes => _notes = notes)
.then(users => this.joinUserNotes(users, _notes))
.then(result => this.notes = result);
Your view template would look like this:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.name}}</em>
</div>
demo w/axios
UPDATE Based on the code you shared with me, it looks like my original demo code (which uses axios
) might've misled you into a bug. The axios
library returns the HTTP response in a data
field, but the vue-resource
library you use returns the HTTP response in a body
field. Attempting to copy my demo code without updating to use the correct field would cause the null
errors you were seeing.
When I commented that axios
made no difference here, I was referring to the logic shown in the example code above, which would apply to either library, given the field names are abstracted in the fetchNotes()
and fetchUsers()
.
Here's the updated demo: demo w/vue-resource.
Specifically, you should update your code as indicated in this snippet:
fetchInvoices(id) {
return this.$http.get('http://localhost/php-api/public/api/invoices/' + id)
// .then(invoices => invoices.data); // DON'T DO THIS!
.then(invoices => invoices.body); // DO THIS: `.data` should be `.body`
},
fetchCustomers(invoices) {
// ...
return Promise.all(
uCustIds.map(id => this.$http.get('http://localhost/php-api/public/api/customers/' + id))
)
// .then(customers => customers.map(customer => customer.data)); // DON'T DO THIS!
.then(customers => customers.map(customer => customer.body)); // DO THIS: `.data` should be `.body`
},
回答2:
Tony, Thank you for all your help and effort dude! Ultimately, with the help from someone in the Vue forum, this worked for me. In addition I wanted to learn how to add additional http requests besides the just the user in the fetchNotes method - in this example also the image request. And this works for me.
Template:
<div v-if="notes.length > 0">
<div v-if="loaded === true">
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
<img :src="note.image.url" />
</div>
</div>
<div v-else>Something....</div>
</div>
<div v-else>Something....</div>
Script:
name: 'invoices',
data () {
return {
invoices: [],
loaded: false,
}
},
methods: {
fetchNotes: async function (id){
try{
let notes = (await this.$http.get('http://api/notes/' + id )).body
for (let i = 0; notes.length; i++) {
notes[i].user = (await this.$http.get('http://api/user/' + notes[i].user_id)).body
notes[i].image = (await this.$http.get('http://api/image/' + notes[i].image_id)).body
}
this.notes = this.notes.concat(notes)
}catch (error) {
}finally{
this.loaded = true;
}
}
来源:https://stackoverflow.com/questions/47195367/vue-js-fire-a-method-based-on-another-methods-output-unique-id