Livewire component unintentionally gets nested inside another - laravel

My Laravel app has a blade view that contains two Livewire component that I want to put inside a grid.
<div class="grid grid-cols-12">
<div class="col-span-8">
<livewire:cars.index />
</div>
<div class="col-span-4">
<livewire:types.index />
</div>
</div>
But for some reason the second Livewire component gets nested inside the first one. Am I breaking some convention? Each component has one root element.

Check if
<livewire:cars.index />
component doesnt have any div that isnt closed

Related

how to pass callback function in component? (laravel alpine.js)

I make a draft implementation for my reusable input component.
The code below obviously throws an error.
Question is how to pass the $event back to register blade to get or log the value of the input?
register.blade.php
<div>
<x-input onChange="(value) => {console.log('value', value)}"></x-input>
<div/>
input.blade.php
#props(['onChange' => 'null'])
<input x-on:change="{{ $onChange($event) }}">
A few things here.
First off, your markup is wrong. You have a the closing slash at the wrong end of the closing div. Should be </div> not <div/>.
Then you're using x-on without x-data. Alpine only picks up components with the x-data attribute.
Finally, events propagate automatically, so you could just listen on the parent instead:
{{-- register.blade.php --}}
<div x-data>
<x-input x-on:change="console.log('value', $event.target.value)" />
</div>
{{-- input.blade.php --}}
<input {{ $attributes }}>
I learned we could just achieve this through Alpine.Js dispatch. I don't need to pass onClick props via Laravel component. I just simply use dispatch to listen the event (x-on).
What I like in this implementation is that,
aside of event information, passing of extra data is easy
you don't have to use Laravel props and assigned unnecessary props in the tag.
register.blade.php
<div>
<x-input x-on:custom-input="console.log('your values =', $event.target.newValue)"
></x-input>
<div/>
input.blade.php
<input x-on:change="$dispatch('custom-input', { newValue: $event.target.value })">
you can pass "key" prop to distinguish each component.

Alpine.js - How do I pass a variable defined inside a x-for to another Alpine component?

I currently have a Bootstrap modal inside an x-for Alpine.js template. So, if I looped 5 users, the modal is rendered 5 times.
Similar to this:
<div x-data="initParent()">
<template class="parent" x-for="(user, index) in users">
<div>
<div x-text="user.name"></div>
<a id="modalForUser" href="#" data-toggle="modal" :data-target="#openModal-${index}">
Open Modal
</a>
<!-- MODAL -->
<div :id="openModal-${index}" class="modal fade" tabindex="-1" role="dialog">
<!-- Do stuff with user object-->
...
</div>
</div>
</template>
</div>
But the modal behavior is becoming complex, and I wanted to extract the HTML to a template partial with its own Alpine state.
This means:
a#modalForUser is clicked and sets the Alpine user variable
This user variable is passed to the Alpine component.
And here my problem begins...
If I just leave the new Alpine state nested inside the x-for, I can assess the user variable just fine, since it's nested inside the for-loop. However, it complains that the named x-data function is being repeated.
If I place the modal outside the x-for loop, I have trouble extracting the user from inside the looped element that has been clicked.
I sense the 2nd approach is more correct. I've tried using:
an eventListener on the modal's state's init method.
using the $dispatch magic method on the child anchor and the $watch on the modal state... but I must be getting it wrong.

Livewire + AlpineJS: Using x-data as wire:click param

I have a Laravel Blade template which has an AlpineJS div defined like this:
<div x-data="{ id: 2 }">
...
<button type="button" wire:click="deleteAddress(id)">Button</button>
</div>
What I want is somehow "pass" that id variable to the wire:click call.
The above code throws an Uncaught ReferenceError: id is not defined in my JS console.
Any ideas? Just starting with the TALL stack and I do not know the optimal workflows yet.
Thanks in advance.
You could use Alpine click listener with the magic $wire, as described here:
https://laravel-livewire.com/docs/2.x/alpine-js
This way you'll stay "inside" Alpine, but with access to your Livewire component method. So it's going to be:
<div x-data="{ id: 2 }">
...
<button type="button" #click="$wire.deleteAddress(id)">Button</button>
</div>
Add a wire:key to the same div as the x-data.
<div wire:key="id" x-data="{ id: 2 }">
...
<button type="button" wire:click="deleteAddress(id)">Button</button>
</div>
I think this is because Livewire only updates what is changing. And the x-data div is the top div of an alpine component. so if you add the id as wire:key to the div that contains the x-data then this div will also get updated and it will rerun the alpine component.

Including laravel blade template with button click

I can not find a solution how to include blade template using button click.
For example lets say I have a main view:
<div id="HEREGOESMYTEMPLATES">
</div>
<div class="row">
<button onclick="addTemplate()">ADD TEMPLATE</button>
</div>
Then script, which should include template
<script type="app/js">
function addTemplate()
{
//Get template and include into div with id HEREGOESMYTEMPLATE
}
</script>
And then the template itself (lets say partial.blade.php):
<div>Include me to main blade file</div>
Is there any possible way by including it without creating every tag in JS and then appending to the div?
Thank you.
UPDATE.
As blade template is not supposed to do such work as it is rendered in backend, I used VueJS to this, which I believe is the best solution in this situation.

Nesting custom tags in Vue

I have a Laravel / Vue app that I'm building and I've run into a bit of a snag. I have been able to successfully create individual stand-alone components, however when I try to nest a stand-alone component into another component, only the nested component shows up. A bit of code:
CompanyLogo.vue
<template>
<figure class="company-logo" :style="{
width: size,
height: size,
backgroundImage: `url(${src})`
}"></figure>
</template>
LogoUploader.vue
<template>
<div class="logo-container">
<company-logo size="65px" :src="`${company.logo.url}`"></company-logo>
</div>
<div class="logo-uploaded-details">
<p>Last updated: {company.logo.last_updated}</p>
<button class="file-browse-btn">Upload Image</button>
</div>
</template>
What's happening is that when in company.blade.php I simply have
<logo-uploader></logo-uploader>
The app compiles and loads, however only the CompanyLogo shows up on the screen. The entire markup for the logo-uploaded-details section isn't rendered at all.
I have tried adding a require for the CompanyLogo component to the registration for the LogoUploader component, but that didn't work either.
If I split out the components they both show up. The issue is only once they're nested.
Any ideas?
Vue instances and components must have a single root element. In your LogoUploader you have two root elements.
You need to wrap them in a root.
<template>
<div>
<div class="logo-container">
<company-logo size="65px" :src="`${company.logo.url}`"></company-logo>
</div>
<div class="logo-uploaded-details">
<p>Last updated: {company.logo.last_updated}</p>
<button class="file-browse-btn">Upload Image</button>
</div>
</div>
</template>

Resources