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

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.

Related

Livewire component unintentionally gets nested inside another

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

Reusable button for dropdown toggle (Vue and Laravel)

I'm learning vue and i'm trying to build a reusable dropdown component. After hours of work i finally got it together and now i'm stuck because all i want is to pass the button name to my vue component.
This is my dropdown:
https://imgur.com/StvEjyF
What I want is a way to pass the button name from my blade to the button.
My blade and only the string of the button name i'm trying to pass is not working:
<Dropdown>
<template slot="toggler">
<button>from blade</button>
</template>
<Dropdowncontent>
<Dropdownitems>Link1</Dropdownitems>
<Dropdownitems>Link2</Dropdownitems>
</Dropdowncontent>
</Dropdown>
My dropdown component which contains the button:
<template>
<div class="relative" v-click-outside="onClickOutside">
<slot name="toggler">
<button
#click="showCategories"
class="flex max-h-52 w-full overflow-auto py-2 pl-3 pr-9 text-sm font-semibold lg:inline-flex lg:w-32"
>
from vue
</button>
</slot>
<slot />
</div>
</template>
I tried to accept it as a prop so I added props: [buttonName] to export default in my component and from the blade i added :buttonName="bla bla" to the dropdown tags but it doesn't update the {{buttonName}} in the component so this didn't work.
All i need is a way to pass only the button name from blade to vue because i don't want to create the button in the blade as it's my toggle for the dropdown content and items
I fixed it by simply passing title prop to the component

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.

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>

Laravel 4 View Nesting Main Layout Subviews Content and Sidebar

This is my first post so please be gentle with me.
I have set my master layout to call "main"
<div class="row">
{{$main}}
</div>
and I have a view called home.blade
<div class="col-md-9">
<div class="content">
{{$content}}
</div>
</div>
<div class="col-md-3">
<aside class="sidebar">
{{$sidebar}}
</aside>
</div>
and in my controller I have:-
$this->layout->main = View::make('home')->nest('content', 'property.viewmany', compact('properties'));
This works for the content variable, but at this point I'm stumped at how to get the sidebar variable to work. Is there a way to nest the two variables into the home.blade from the controller?
I've attempt various, albeit, shots in the dark, but I always get either content not defined or sidebar not defined.
You can put a first variable in the first view like this:
$this->layout->main = View::make('home', compact('sidebar'))->nest('content', 'property.viewmany', compact('properties'));

Resources