title: AlpineJS x-for templates
body: <p>I have a problem with AlpineJS in my complicated app, and I'm finding it very difficult to replicate under a simplified example.  This may well mean that it's a bug in Alpine, but I'll ask here for help on the off-chance anyway.  I've tried to reduce the code below to only the bare essentials that are necessary to explain the problem, and doing so may have lead to some typos.  Therefore please excuse me in advance for any errors that are not related to the problem itself.</p>
  <p>I'm using Livewire to synch data between my PHP classes and my AlpineJS front-end.  The two variables that are relevant in the PHP class are:</p>
  <pre class="lang-php prettyprint-override"><code>public $colOrder;        // users are able to &quot;re-order&quot; columns on their table-view.  This preference is saved into their profile and stored in this variable as a 1D array of the column-IDs
  public $datasourceData;  // contains a 2D data that is pulled from a database with: Model-&gt;get()-&gt;toArray(); [0 =&gt; ['col1'=&gt;'data1,1', 'col2'=&gt;'data1,2'], 1 =&gt; ['col1'=&gt;'data2,1', 'col2'=&gt;'data2,2']];
  </code></pre>
  <p>These arrays are then entangled with Alpine variables, and the template is generated from those arrays of data as follows.  Ostensibly, this template works fine:</p>
  <pre class="lang-html prettyprint-override"><code>&lt;div x-data=&quot;{
      eColOrder: @entangle('colOrder').defer,
      eData:     @entangle('datasourceData').defer
  }&quot;&gt;
  &lt;table class=&quot;table&quot; x-cloak&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;template x-for=&quot;(col, ix) in eColOrder&quot; :key=&quot;'th-'+ix&quot;&gt;
          &lt;th x-text=&quot;col&quot;&gt;&lt;/th&gt;
        &lt;/template&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;template x-if=&quot;eData.length==0&quot;&gt;
        &lt;tr&gt;
          &lt;td :colspan=&quot;eColOrder.length&quot; style=&quot;padding: 1em&quot;&gt;No data found&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/template&gt;
      &lt;template x-if=&quot;eData.length&gt;0&quot;&gt;
        &lt;template x-for=&quot;(rec, ix) in eData&quot; :key=&quot;'row-'+ix&quot;&gt;
          &lt;tr&gt;
            &lt;td class=&quot;action&quot;&gt;&lt;/td&gt;
            &lt;template x-for=&quot;(col, pos) in eColOrder&quot; :key=&quot;'td-'+ix+'-'+pos&quot;&gt;
              &lt;td x-text=&quot;rec[col]&quot;&gt;&lt;/td&gt; &lt;!-- I also tried `eData[ix][col]`, but it produced errors in the browser console, even though the on-screen display was fine --&gt;
            &lt;/template&gt;
          &lt;/tr&gt;
        &lt;/template&gt;
      &lt;/template&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
  </code></pre>
  <p>In this screen-shot, you can see that the user's search (in the top-row) has produced a tabulated grid of data below.  Happy days.</p>
  <p><a href="https://i.stack.imgur.com/68ifq.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/68ifq.png" alt="enter image description here" /></a></p>
  <p>The problem arises when the user re-submits a different search.  They do so by updating the search fields, and pressing the &quot;search&quot; button again.  This re-submits the search (through a Livewire JSON call), refreshing the <code>$datasourceData</code> array with new data, entangling itself with the <code>eData</code> variable in Alpine, and producing the following result:</p>
  <p><a href="https://i.stack.imgur.com/i6eS7.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/i6eS7.png" alt="enter image description here" /></a></p>
  <p>What appears to be happening, is that the results of the new search are correctly pulled through.  But for whatever reason, Alpine hasn't cleared the screen from the last set of search-results.  It's interesting that only the data-level of the HTML table is corrupted (that is to say, the <code>&lt;td&gt;</code> cells).  Note that the <code>&lt;th&gt;</code> cells have (correctly) not been duplicated above the right-hand-half of the new table.</p>
  <p>I've debugged and checked that the data returned from the Eloquent models is correct, and that the structure of the data in the entangled JavaScript variable <code>eData</code> is also correct.  This problem is not data-related, it's the rendering that's at fault.</p>
  <p>My gut-feel is that this is an Alpine bug, but I haven't been able to prove it yet.</p>
  <hr />
  <p>My problem stops there.  However, in an attempt to replicate the issue and narrow down the cause of the issue, what I've done is to create a simplified Livewire/Blade/Alpine page.  Strictly speaking, I wasn't able to replicate the problem there either <em>directly</em>, but I did (accidentally) manage to replicate a similar output when I entered a deliberate &quot;bug&quot; into my code.</p>
  <p>Take the following PHP/Livewire component:</p>
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  
  namespace App\Business\Tbd;
  
  use Livewire\Component;
  
  class StartLw extends Component
  {
      public  array $data = [];
      public  array $headings = [];
      public  int $count = 0;
  
      public function mount() {
          for ($i=1; $i &lt;= 6; $i++) {
              $this-&gt;headings[] = &quot;col{$i}&quot;;
          }
          $this-&gt;data = [];
      }
  
      public function formSubmit() {
          $src = 1;
          $this-&gt;data = [];
          for ($i=0; $i &lt; 10; $i++) {
              $this-&gt;data[$i] = [];
              for ($y=1; $y &lt;= 6; $y++) {
                  $this-&gt;data[$i][&quot;col{$y}&quot;] = &quot;source {$src} ({$i},{$y})&quot;;
              }
          }
          $this-&gt;count++;
      }
  
      public function relatedToButSeparateFromForm() {
          $src = 2;
          $this-&gt;data = [];
          for ($i=0; $i &lt; 4; $i++) {
              $this-&gt;data[$i] = [];
              for ($y=1; $y &lt;= 6; $y++) {
                  $this-&gt;data[$i][&quot;col{$y}&quot;] = &quot;source {$src} ({$i},{$y})&quot;;
              }
          }
          $this-&gt;count++;
      }
  
      public function render()
      {
          return view('components.tbd.lw-start-lw')
              -&gt;layout('layouts.tbd.lw');
      }
  }
  </code></pre>
  <p>And this cut-down HTML to render the page:</p>
  <pre class="lang-html prettyprint-override"><code>&lt;div class=&quot;container&quot; x-data=&quot;{
      eData: @entangle('data').defer,
      eHeadings: @entangle('headings').defer
  }&quot;&gt;
  
  &lt;div class=&quot;row&quot;&gt;
      &lt;div class=&quot;col&quot;&gt;&lt;p&gt;{{ $count }}&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  
  &lt;div class=&quot;row&quot;&gt;
      &lt;div class=&quot;col&quot;&gt;
          &lt;form method=&quot;post&quot; wire:submit.prevent=&quot;formSubmit&quot;&gt;
              &lt;p&gt;
              &lt;button type=&quot;submit&quot;&gt;Load data source 1&lt;/button&gt;&amp;nbsp;
              &lt;button type=&quot;button&quot; wire:click=&quot;relatedToButSeparateFromForm&quot;&gt;Load data source 2&lt;/button&gt;
              &lt;/p&gt;
          &lt;/form&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;div class=&quot;row&quot;&gt;
      &lt;div class=&quot;col&quot;&gt;
          &lt;table&gt;
              &lt;thead&gt;
                  &lt;tr&gt;
                  &lt;template x-for=&quot;hd in eHeadings&quot;&gt;
                      &lt;th x-text=&quot;hd&quot; style=&quot;padding: 0.5em; background-color:rgb(220,220,230); border: 1px solid rgb(210,210,230)&quot;&gt;&lt;/th&gt;
                  &lt;/template&gt;
                  &lt;/tr&gt;
              &lt;/thead&gt;
              &lt;tbody&gt;
                  &lt;template x-for=&quot;(row, ix) in eData&quot; :key=&quot;ix&quot;&gt;
                      &lt;tr&gt;
                      &lt;template x-for=&quot;(col, pos) in eHeadings&quot; :key=&quot;'td-'+ix+'-'+pos&quot;&gt;
                          &lt;td x-text=&quot;row[col]&quot; :class=&quot;id&quot; style=&quot;padding: 0.5em; background-color:rgb(240,240,255); border: 1px solid rgb(210,210,230)&quot;&gt;&lt;/td&gt;
                      &lt;/template&gt;
                      &lt;/tr&gt;
                  &lt;/template&gt;
              &lt;/tbody&gt;
          &lt;/table&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  <p>Note the deliberate error!  On the <code>&lt;td&gt;</code> element, <code>:class=&quot;id&quot;</code> should really say <code>:class=&quot;col&quot;</code>.  Now if I take out the error, the page works as I would expect it to.  But with the error re-introduced into the code (together with a bunch of error messages in the browser console saying: <code>Uncaught ReferenceError: id is not defined</code>), after toggling the two buttons back and forth a bit, I get this:</p>
  <p><a href="https://i.stack.imgur.com/yzuV5.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/yzuV5.png" alt="enter image description here" /></a></p>
  <p>As I think you'll agree, that picture is spookily reminiscent of the situation I get in my real world application (except that in the real-world-app, I don't end up with any errors in the browser's console).</p>
  <p>This leads me to the strong belief that there is a silent bug being triggered somewhere in the Alpine engine which triggers the same net result.  I will go and log this on their GitHub support pages too, but I have always found the Stack community to be super useful in the past too.  I hope that someone out there is able to help validate that I'm not missing anything obvious!</p>