1

I have an API to get list of books, in each book I have author ID. I also want to get author name from that ID through another API (get author), so I use v-for to get items in list of book. Each item I call getAuthor(authorId) function but it repeats infinitely. Does anyone know what the reason is? My source code:

export default {  
name: 'GetBooks',  
    data() {
        return {
            books: [],
            categories: [],
            author: [],
        };
    },
    created()  
    {  
        this.getBooks();  
    },  

methods: { 
    getBooks() {  
        BookServices.getBooks().then(response => {  
                this.books = response.data;  
                console.log(response.data);  
            })  
            .catch(e => {  
                console.log(e);  
            });  
    },  
    getAuthor(id) {  
        BookServices.getAuthor(id).then(response => {  
                this.author = response.data.name;
                console.log(response.data.name);  
            });
        return this.author;
    },  
}  

AND:

<tbody>  
    <tr v-for="item in books" :key="item.id">  
        <td>{{ item.id }}</td>  
        <td>{{ item.name }}</td>  
        <td>{{ getAuthor(item.authorId) }}</td>  
        <td>{{ item.price }}</td>  
        <td>{{ item.year }}</td>   
        <td><input class='myclass' type='button' value='Detail'/></td>
    </tr>  
</tbody>  

2 Answers 2

2

The model-fetching methods should decide when and how to fetch authors, not the markup. (this is @IgorMoraru's good second idea, corrected to properly handle the async fetch of authors).

This also fixes OP code error that assigned the book author to the view instance.

getBooks() {  
    BookServices.getBooks().then(response => {  
            this.books = response.data;  
            console.log(response.data);
            this.getAuthors();  
        })  
        .catch(e => {  
            console.log(e);  
        });  
},
getAuthors() {
  let promises = this.books.map(book => this.getAuthor(book));
  return Promise.all(promises);
},
getAuthor(book) {  // note the change: pass the book here, not the id
    BookServices.getAuthor(book.id).then(response => {  
            book.author = response.data.name;
            console.log(response.data.name);  
        });
    return this.author;
},  

Defend the markup for books that are (temporarily) missing authors...

    <td>{{ item.id }}</td>  
    <td>{{ item.name }}</td>  
    <td v-if="item.author">{{ item.author }}</td>
    <td v-else>fetching...</td>
    <td>{{ item.price }}</td>  
    <td>{{ item.year }}</td>   
Sign up to request clarification or add additional context in comments.

1 Comment

your solution did help me a lot! I fixed it successfully 😉 @danh
1

The issue is that each call to getAuthor trigger a template rerender, which in turn trigger a new call to getAuthor, thus the infinite calls.

I can think of two solutions:

  1. Use the v-once directive on the td in cause. This directive prevent further update of the element after initial render.
<td v-once> {{ getAuthor(item.authorId) }}</td>
  1. Populate the authors when getting book data (also it can be done in API).

     getBooks() {  
         BookServices.getBooks().then(response => {  
                 this.books = response.data;
    
                 this.books = this.books.map(book => {
                     book["author"] = this.getAuthor(book.authorId)
                     return book
                 })  
    
             })  
             .catch(e => {  
                 console.log(e);  
             });  
     },  
    

Then, reference the author in template:

<tr v-for="item in books" :key="item.id">  
    <td>{{ item.id }}</td>  
    <td>{{ item.name }}</td>  
    <td>{{ item.author }}</td>  
    <td>{{ item.price }}</td>  
    <td>{{ item.year }}</td>   
    <td><input class='myclass' type='button' value='Detail'/></td>
</tr>  

1 Comment

Many thanks! As you said, each call to getAuthor trigger a template rerender, and that's the reason why it has infinite calls. Thank you so much @IgorMoraru

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.