How To Get Nested x-for Loops Working with Data Binding In Alpine.js 3

As I was learning Alpine.js for a new project I came across a problem when using HTML inputs in nested x-for loops. Data binding was working great in a single x-for loop wrapped around an HTML input tag. But, when I nest nested loops, the data entered in the inner loop inputs is not binding to the data object.

The Non-Working Code

Here’s an example to demonstrate:

<script>
book_data = { books: {
  a1: ['one', 'two', 'three' ],
  a2: ['four', 'five', 'six' ],
  a3: ['seven', 'eight', 'nine' ]
}
  };
</script>


<!--  PROOF OF CONCEPT OF NESTED X-FOR LOOP IN ALPINE!
<div x-data = "book_data">
  <template x-for="book in books">
    <div>
      <template x-for="number in book">
        <input x-model="number" type="text" />
      </template>
      <button @click="book.push('ten')">Add Number</button>
    </div>
  </template>
  
  <button @click="console.log(book_data);">Submit</button>
</div>

In this example, I have a nested x-for loop that loops through each of the books and then through multiple values for each book.

In the inner loop, there is an HTML input tag that has data with bi-directional binding using x-model.

So, when you type in data into an input, you would expect to see that data in the book_data object (in my example you can press submit to dump the object to the console). But this doesn’t work!

The Solution

The problem is that the inner loop operates on a copy of the data (number), not the data itself (book). Thus, the solution is to reference the data in the inner loop using the variable from the outer loop. Here’s the working code using the same data object:

<div x-data = "book_data">
  <template x-for="book in books">
    <div>
      <template x-for="(number,index) in book">
        <input x-model="book[index]" type="text" />
      </template>
      <button @click="book.push('ten')">Add Number</button>
    </div>
  </template>
  
  <button @click="console.log(book_data);">Submit</button>
</div>

The problem was solved by changing x-model=”number” to x-model=”book[index]”, i.e., referencing the variable from the outer loop with the appropriate index, instead of the inner.

If you type in data into an input, then press the submit button, you’ll see that data in the object.

As a bonus, I have a repeater button just to show that things are working well.

I feel like this is a potential “gotcha” for beginners (like me) in Alpine.js. Did it help for you? Let me know in the comments! – Brian

Shares

Please Leave a Question or Comment

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Inline Feedbacks
View all comments
Tuan Nguyen
Tuan Nguyen
22 days ago

This help me a lot. Thank you