Reusable button for dropdown toggle (Vue and Laravel) - 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

Related

Failed to bind data from alpineJs to livewire

i'm trying to bind data rendered by alpine to livewire.
the below code render for me 5 values when i select one of them it placed in the input but when i submit the value is null i must press a key beside the value and submit to get value,any hint
<input x-on:click="open = !open" x-model="search" wire:model="manager_id">
<ul x-show="open" x-on:click.outside="open = !open" >
<template x-for="item in filteredItems" :key="item">
<li x-text="item" x-on:click="search = item,open = !open" ></li>
</template>
</ul>
tried to change the on-click to on change and it didn't work also

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.

Button that shows modal for each b-table row

Im using laravel, vue, and bootstrap-vue.
I have created a vue component that displays a table of elements (subnets in this example).
For each of them I show a component (modal_edit-subnet) thats should open a modal that allows to edit the data of the element of the related row.
The problem is that it shows modals for all of the table elements. For example, if the table has 3 rows, it shows 3 modals (after closing one it shows the next). Each of the modals with the data of each of the rows.
I've tried to add "key"s but no success.
What am i doing wrong?
Thanks!
Component that shows the table
<template>
<div>
<b-card class="text-center">
<b-table small striped hover :items="data_subnets" :fields="fields" :tbody-tr-class="rowClass">
<template slot="[ip_address]" slot-scope="data_subnets">
<b>{{ long2ip(data_subnets.item.ip_address) }}</b>
</template>
<template slot="[actions]" slot-scope="data_subnets">
v-on:deleteSubnet="deleteSubnet"></modal_delete-subnet>
<modal_edit-subnet :key="'modal_edit_subnet' + data_subnets.item.id" :subnet="data_subnets.item" v-on:editSubnet="editSubnet"></modal_edit-subnet>
</template>
</b-table>
</b-card>
</div>
</template>
Modal modal_edit-subnet
<template>
<div>
<b-button size="sm" v-b-modal.modal-edit-subnet>Edit</b-button>
<b-modal
id="modal-edit-subnet"
ref="modal"
title="Edit subnet"
#ok="handleOk"
>
This is subnet {{data_subnet.id}}
</b-modal>
</div>
</template>
The problem is that:
You're rendering a modal for each row of the table and;
Reading the docs, it seems like the modal is triggered by the id, and your b-modal id is not dynamic depending on the row.
How to fix it:
Use just one modal on the b-table level
Dynamically inject id into your modal_edit-subnet component:
<template>
<div>
<b-button size="sm" v-b-modal[id]>Edit</b-button>
<b-modal
:id="id"
ref="modal"
title="Edit subnet"
#ok="handleOk"
>
This is subnet {{data_subnet.id}}
</b-modal>
</div>
</template>
<script>
export default {
props: {
id: {
type: String | Number
}
}
}
</script>
Use v-model (this is the way I would do it)
<template>
<div>
<b-button size="sm" #click="show = true">Edit</b-button>
<b-modal
v-model="show"
ref="modal"
title="Edit subnet"
#ok="handleOk"
>
This is subnet {{data_subnet.id}}
</b-modal>
</div>
</template>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>

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