Search code examples
javascriptphplaravelvue.js

Vue content rendering outside parent component


When I load a vue component inside a @foreach tag it gets rendered first, outside of its parent component.

 //view 
     <div class="contenido" style="padding-top:5%">

         <table class="table">
                <thead class="thead-dark">
                    <tr>
                        <th class="col-md-8">Nombre descripción general</th>
                        <th class="col-md-2">Actualizar</th>
                        <th class="col-md-2">Consultar</th>
                    </tr>
                </thead>
                <tbody>
                @foreach($descs as $desc)
                        <tab-descgen desc-nombre = "{{ $desc -> nombre }}" desc-id = "{{ $desc -> id }}"></tab-descgen>
                @endforeach
                </tbody>

            </table>

   //vue component
   <template>
    <tr >
         <td>{{ this.descNombre }}</td>
     <td><i style="font-size:30px;" class="fa fa-edit float-center"></i> 
 </td>
     <td><i style="font-size:30px;" class="fa fa-arrow-circle-right 
   float-center"></i></td>
  </tr>
   </template>

  <script>
    export default {
       props:['descNombre', 'descId'],

When this gets rendered the table rows inside the vue component will be rendered above the header, and I don't quite understand why. I want to know if I'm missing something.


Solution

  • Even ignoring all the server-side stuff, this won't work:

    <tbody>
      <tab-descgen></tab-descgen>
    </tbody>
    

    It'd be fine in a single-file component but if this markup makes it to the browser (as it will in your case) it'll rip the tab-descgen out of the table before Vue gets anywhere near it.

    You'd need to use is to work around it:

    <tbody>
      <tr is="tab-descgen"></tr>
    </tbody>
    

    It's explained here in the docs:

    https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats

    In short, only tr elements are allowed as direct children of tbody, anything else gets pulled out. The workaround is to use a tr and then let Vue know what you really wanted using is.


    Vue3

    Since Vue v3.4.0 Slam Dunk, we have a breaking change

    v-is directive has been removed. It was deprecated in 3.3. Use the is attribute with vue: prefix instead.

    More details for :is are available here.

    But TLDR, the new syntax is

    <tbody>
      <tr is="vue:tab-descgen"></tr>
    </tbody>
    

    This is the reason as of why this happens in Vue, mostly an HTML placement restriction.