Error when upgrading from V2 in alpine js - alpine.js

I am trying to upgrade (version 2 --> 3) and I have checked all (I think) breaking changes from the list found in the docs.
However, I am still getting an error:
Uncaught TypeError: Cannot read properties of undefined (reading '_x_effects')
My code is originally based on this snippet, so it'd be easier to reproduce it from there (the error appears there too if you replace the script tag in the head to: <script defer src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script> )
Below the code to which the link above points to:
<head>
<!-- ... -->
<script
src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js" defer></script>
<!-- ... -->
</head>
<body class="bg-grey-100 px-3 font-sans leading-normal tracking-normal">
<div class="container pt-8 mx-auto" x-data="loadEmployees()">
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search for an employee..."
type="search"
class="block w-full bg-gray-200 focus:outline-none focus:bg-white focus:shadow text-gray-700 font-bold rounded-lg px-4 py-3"
/>
<div class="mt-4 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
<template x-for="item in filteredEmployees" :key="item">
<div
class="flex items-center shadow hover:bg-indigo-100 hover:shadow-lg hover:rounded transition duration-150 ease-in-out transform hover:scale-105 p-3"
>
<img
class="w-10 h-10 rounded-full mr-4"
:src="`${item.profile_image}`"
/>
<div class="text-sm">
<p
class="text-gray-900 leading-none"
x-text="item.employee_name + ' (' + item.employee_age + ')'"
></p>
<p
class="text-gray-600"
x-text="'$'+item.employee_salary/100"
></p>
</div>
</div>
</template>
</div>
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
first
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
prev
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="index">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : index === pageNumber }"
type="button"
x-on:click="viewPage(index)"
>
<span x-text="index+1"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
next
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
last
</button>
</div>
<div>
<div
class="mt-6 flex flex-wrap justify-between items-center text-sm leading-5 text-gray-700"
>
<div
class="w-full sm:w-auto text-center sm:text-left"
x-show="pageCount() > 1"
>
Page <span x-text="pageNumber+1"> </span> of
<span x-text="pageCount()"></span> | Showing
<span x-text="startResults()"></span> to
<span x-text="endResults()"></span>
</div>
<div
class="w-full sm:w-auto text-center sm:text-right"
x-show="total > 0"
>
Total <span class="font-bold" x-text="total"></span> results
</div>
<!--Message to display when no results-->
<div
class="mx-auto flex items-center font-bold text-red-500"
x-show="total===0"
>
<svg
class="h-8 w-8 text-red-500"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" />
<circle cx="12" cy="12" r="9" />
<line x1="9" y1="10" x2="9.01" y2="10" />
<line x1="15" y1="10" x2="15.01" y2="10" />
<path d="M9.5 16a10 10 0 0 1 6 -1.5" />
</svg>
<span class="ml-4"> No results!!</span>
</div>
</div>
</div>
</div>
<script>
var sourceData = [
{
id: "1",
employee_name: "Tiger Nixon",
employee_salary: "320800",
employee_age: "61",
profile_image: "https://randomuser.me/api/portraits/men/1.jpg",
}, // etc.
];
function loadEmployees() {
return {
search: "",
pageNumber: 0,
size: 10,
total: "",
myForData: sourceData,
get filteredEmployees() {
const start = this.pageNumber * this.size,
end = start + this.size;
if (this.search === "") {
this.total = this.myForData.length;
return this.myForData.slice(start, end);
}
//Return the total results of the filters
this.total = this.myForData.filter((item) => {
return item.employee_name
.toLowerCase()
.includes(this.search.toLowerCase());
}).length;
//Return the filtered data
return this.myForData
.filter((item) => {
return item.employee_name
.toLowerCase()
.includes(this.search.toLowerCase());
})
.slice(start, end);
},
//Create array of all pages (for loop to display page numbers)
pages() {
return Array.from({
length: Math.ceil(this.total / this.size),
});
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
let resultsOnPage = (this.pageNumber + 1) * this.size;
if (resultsOnPage <= this.total) {
return resultsOnPage;
}
return this.total;
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
</script>
</body>

Related

Why image preview is duplicated when using alpinejs and livewire in laravel 9?

Why image preview is duplicated when using alpinejs and livewire in laravel 9?
Alpinejs already included in Laravel with defer.
The code drag and drop the images and shows their preview before submit. However, when I add wire:model=photo it duplicated the image preview, otherwise working perfectly.
Please have a look at below code which is inside livewire component.
<div x-data="dataFileDnD()" class="relative flex flex-col p-4 text-gray-400 border border-gray-200 rounded">
<form wire:submit.prevent="save" x-ref="dnd"
class="relative flex flex-col text-gray-400 border border-gray-200 border-dashed rounded cursor-pointer">
<input accept="images/*" type="file" multiple wire:model="photo"
class="absolute inset-0 z-50 w-full h-full p-0 m-0 outline-none opacity-0 cursor-pointer"
x-on:change="addFiles($event)"
x-on:dragover="$refs.dnd.classList.add('border-blue-400'); $refs.dnd.classList.add('ring-4'); $refs.dnd.classList.add('ring-inset');"
x-on:dragleave="$refs.dnd.classList.remove('border-blue-400'); $refs.dnd.classList.remove('ring-4'); $refs.dnd.classList.remove('ring-inset');"
x-on:drop="$refs.dnd.classList.remove('border-blue-400'); $refs.dnd.classList.remove('ring-4'); $refs.dnd.classList.remove('ring-inset');"
title="" />
<div class="flex flex-col items-center justify-center py-10 text-center">
<svg class="w-6 h-6 mr-1 text-current-50" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<p class="m-0">Drag your files here or click in this area.</p>
</div>
</form>
<template x-if="files.length > 0">
<div class="grid grid-cols-2 gap-4 mt-4 md:grid-cols-6" x-on:drop.prevent="drop($event)"
x-on:dragover.prevent="$event.dataTransfer.dropEffect = 'move'">
<template x-for="(_, index) in Array.from({ length: files.length })">
<div class="relative flex flex-col items-center overflow-hidden text-center bg-gray-100 border rounded cursor-move select-none"
style="padding-top: 100%;" x-on:dragstart="dragstart($event)" x-on:dragend="fileDragging = null"
:class="{ 'border-blue-600': fileDragging == index }" draggable="true" :data-index="index">
<button class="absolute top-0 right-0 z-50 p-1 bg-white rounded-bl focus:outline-none"
type="button" x-on:click="remove(index)">
<svg class="w-4 h-4 text-gray-700" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
<span x-text="index"></span>
</button>
<template x-if="files[index].type.includes('image/')">
<img class="absolute inset-0 z-0 object-cover w-full h-full border-4 border-white preview"
x-bind:src="loadFile(files[index])" />
</template>
<div class="absolute bottom-0 left-0 right-0 flex flex-col p-2 text-xs bg-white bg-opacity-50">
<span class="w-full font-bold text-gray-900 truncate"
x-text="files[index].name">Loading</span>
<span class="text-xs text-gray-900" x-text="humanFileSize(files[index].size)">...</span>
</div>
<div class="absolute inset-0 z-40 transition-colors duration-300"
x-on:dragenter="dragenter($event)" x-on:dragleave="fileDropping = null"
:class="{ 'bg-blue-200 bg-opacity-80': fileDropping == index && fileDragging != index }">
</div>
</div>
</template>
</div>
</template>
</div>
<script src="https://unpkg.com/create-file-list"></script>
<script>
function dataFileDnD() {
return {
files: [],
fileDragging: null,
fileDropping: null,
humanFileSize(size) {
const i = Math.floor(Math.log(size) / Math.log(1024));
return (
(size / Math.pow(1024, i)).toFixed(2) * 1 +
" " + ["B", "kB", "MB", "GB", "TB"][i]
);
},
remove(index) {
let files = [...this.files];
files.splice(index, 1);
this.files = createFileList(files);
},
drop(e) {
let removed, add;
let files = [...this.files];
removed = files.splice(this.fileDragging, 1);
files.splice(this.fileDropping, 0, ...removed);
this.files = createFileList(files);
this.fileDropping = null;
this.fileDragging = null;
},
dragenter(e) {
let targetElem = e.target.closest("[draggable]");
this.fileDropping = targetElem.getAttribute("data-index");
},
dragstart(e) {
this.fileDragging = e.target
.closest("[draggable]")
.getAttribute("data-index");
e.dataTransfer.effectAllowed = "move";
},
loadFile(file) {
const preview = document.querySelectorAll(".preview");
const blobUrl = URL.createObjectURL(file);
preview.forEach(elem => {
elem.onload = () => {
URL.revokeObjectURL(elem.src); // free memory
};
});
return blobUrl;
},
addFiles(e) {
const files = createFileList([...this.files], [...e.target.files]);
this.files = files;
}
};
}
</script>
</div>

AlpineJS x-for not running after x-data update

I am running into a very strange issue where I have a chatroom that should update whenever a new message is posted. Instead when I add a new message the x-data is getting updated and I can see the message there but the x-for doesnt seem to run and it never appears on the frontend. Then when I enter another message the previous message shows up on the frontend but the latest message does not show even though the x-data has been updated to reflect it.
Here is the code below:
<div
x-data="{{ json_encode(['messages' => $messages, 'messageBody' => '']) }}"
x-init="
Echo.join('chat.2')
.listen('ChatSent', (e) => {
#this.call('incomingMessage', e)
console.log(e)
})
">
<div
class="wow fadeInUp mb-10 rounded-md bg-light-bg dark:bg-dark"
data-wow-delay="0s"
>
<h3
class="border-b border-body-color border-opacity-10 py-4 px-8 text-lg font-semibold text-black dark:border-white dark:border-opacity-10 dark:text-white"
>
Chat
</h3>
<div id="chat-window" class="relative w-full p-3 overflow-y-auto h-[14rem]">
<ul class="space-y-2">
<template
x-for="message in messages"
:key="message.id"
>
<div>
<template x-if="message.user_id == {{ Auth::user()->id }}">
<li class="flex justify-end">
<div class="relative max-w-xl px-4 py-2 text-black dark:text-white bg-gray-100 dark:bg-black rounded shadow">
<span class="block" x-text="message.body"></span>
</div>
</li>
</template>
<template x-if="message.user_id != {{ Auth::user()->id }}">
<li class="flex justify-start">
<div class="relative max-w-xl px-4 py-2 text-black dark:text-white bg-blue-100 dark:bg-[#1e2a78] rounded shadow">
<span class="block" x-text="message.body"></span>
</div>
</li>
</template>
</div>
</template>
</ul>
</div>
<div class="flex items-center justify-between w-full p-3 border-t border-gray-300">
<input
#keydown.enter="
#this.call('sendMessage', messageBody)
messageBody = ''
"
x-model="messageBody"
type="text"
placeholder="Message"
class="block w-full py-2 pl-4 mx-3 bg-gray-100 dark:bg-black rounded-full outline-none focus:text-gray-700 dark:focus:text-white dark:text-white"
name="message" required />
<button
#click="
#this.call('sendMessage', messageBody)
messageBody = ''
">
<svg class="w-5 h-5 text-black dark:text-white origin-center transform rotate-90" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" />
</svg>
</button>
</div>
#error('messageBody')
<div class="w-full p-2 text-center">
<span class="text-red-500">Message is required!</span>
</div>
#enderror
</div>
<div
class="wow fadeInUp mb-10 rounded-md bg-light-bg dark:bg-dark"
data-wow-delay="0s">
<h3
class="border-b border-body-color border-opacity-10 py-4 px-8 text-lg font-semibold text-black dark:border-white dark:border-opacity-10 dark:text-white"
>
Active Players
</h3>
<ul class="flex flex-wrap py-6 px-8">
#forelse($here as $authData)
<li>
<a
href="javascript:void(0)"
class="text-body-color-3 mr-3 mb-3 inline-flex items-center justify-center rounded-full border-[.5px] border-body-color bg-body-color bg-opacity-10 py-2 px-4 hover:border-primary hover:bg-primary hover:text-white dark:border-[#363D68] dark:bg-[#272E5C] dark:text-white dark:hover:border-primary dark:hover:bg-primary dark:hover:text-white"
>
{{ $authData['name'] }}
</a>
</li>
#empty
#endforelse
</ul>
</div>
</div>
I figure it has to be something simple I am missing and hopefully someone can point me in the right direction.
EDIT to include Livewire code:
<?php
namespace App\Http\Livewire;
use App\Events\ChatSent;
use App\Models\Chat;
use App\Models\Games;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class Chatbox extends Component
{
public $game_id, $game, $chats;
public $here = [];
public $messages = [];
public function getListeners()
{
$game_id = $this->game_id;
return [
"echo-presence:chat.{$game_id},here" => 'here',
"echo-presence:chat.{$game_id},joining" => 'joining',
"echo-presence:chat.{$game_id},leaving" => 'leaving',
];
}
public function render()
{
return view('games.components.chatbox');
}
public function mount()
{
$this->game = Games::find($this->game_id);
$this->messages = Chat::
where('games_id', $this->game_id)
->with('user')
->latest()
->limit(30)
->get()
->reverse()
->values()
->toArray();
}
public function sendMessage($body)
{
if (! $body) {
$this->addError('messageBody', 'Message body is required.');
return;
}
$message = Auth::user()->chats()->create([
'body' => $body,
'games_id' => $this->game_id,
]);
$message->load('user');
broadcast(new ChatSent($message, $this->game))->toOthers();
$myMessage = $message->toArray();
$this->dispatchBrowserEvent('update-chat');
array_push($this->messages, $myMessage);
}
/**
* #param $message
*/
public function incomingMessage($message)
{
// get the hydrated model from incoming json/array.
$message = Chat::with('user')->find($message['id']);
array_push($this->messages, $message);
$this->dispatchBrowserEvent('update-chat');
}
/**
* #param $data
*/
public function here($data)
{
$this->here = $data;
}
/**
* #param $data
*/
public function leaving($data)
{
$here = collect($this->here);
$firstIndex = $here->search(function ($authData) use ($data) {
return $authData['id'] == $data['id'];
});
$here->splice($firstIndex, 1);
$this->here = $here->toArray();
}
/**
* #param $data
*/
public function joining($data)
{
$this->here[] = $data;
}
}

How to manipulate specific div inside a for-loop of EJS?

Been googling around for multiple hours, I want to change the color of the svg of one element during the onClick event, turns out it either style all the elements in the for loop, or just the first one. I added my ejs and toggleSvg() js script here. Hope you can help me.
ejs snippet:
(look for "svg here")
<div class="max-w-5xl mt-14 mx-auto sm:max">
<% posts.forEach(post=> { %>
<div class="my-20">
<div class=" px-2 mb-2 flex items-center justify-between">
<div class="flex items-center">
<div class="border border-gray-300 p-1 rounded-full w-10 h-10 flex items-center bg-white">
<img
src="<%= post.merchant.image %>"
alt="..."
class="w-10"
loading="lazy"
/>
</div>
<p class="pl-5"><%= post.merchant.name %></p>
</div>
<div>
<!--svg here!-->
<svg id="test" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor" onclick="toggleSvg()" >
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
</svg>
</div>
</div>
<!--image carousell-->
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide w-10 h-20 bg-black ">
<div class="justify-center flex ">
<img
src="<%= post.imageUrl[0] %>"
alt="..."
class="h-72"
loading="lazy"
/>
</div>
</div>
<div class="swiper-slide bg-black">
<div class="justify-center flex bg-black">
<img
src="<%= post.imageUrl[1] %>"
alt="..."
class="h-72"
loading="lazy"
/>
</div>
</div>
<div class="swiper-slide bg-black">
<div class="justify-center flex bg-black">
<img
src="<%= post.imageUrl[2] %>"
alt="..."
class="h-72"
loading="lazy"
/>
</div>
</div>
<div class="swiper-slide bg-black">
<div class="justify-center flex bg-black">
<img
src="<%= post.imageUrl[3] %>"
alt="..."
class="h-72"
loading="lazy"
/>
</div>
</div>
</div>
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-pagination"></div>
</div>
<div class="">
<p><span class="font-bold pr-3"><%= post.merchant.name %></span><%= post.description %></p>
</div>
</div>
<% }) %>
</div>`enter code here`
toggleSvg() js:
<script>
function toggleSvg() {
svgElem = document.getElementById("test");
if(svgElem.style.fill === 'red'){
svgElem.style.fill = 'none';
}else{
svgElem.style.fill = 'red';
}
}
</script>
you're using a id in a loop
the can only select one element, if you want to color all svg you can use a class, if you want to color a specific svg you can do it like this
onclick="toggleSvg(this)"
this way you pass the current element when you click
<script>
function toggleSvg(svgElem ) {
if(svgElem.style.fill === 'red'){
svgElem.style.fill = 'none';
}else{
svgElem.style.fill = 'red';
}
}
</script>

showModal is not defined when sharing State Between Livewire And Alpinejs with #entangle

Basically I'm using the #entangle to share the state between my Livewire component and my Alpine component.
<div x-data="{ open:#entangle('showModal') }" #keydown.escape="showModal=false">
<button type="button" class="block p-2 btn-light-green" #click="showModal=true">Añadir Mezcla</button>
<!--Overlay-->
<div class="overflow-auto" style="background-color: rgba(0,0,0,0.5)" x-show="showModal" :class="{ 'absolute inset-0 z-10 flex items-center justify-center': showModal }">
<!--Dialog-->
<div class="bg-white w-11/12 md:max-w-md mx-auto rounded shadow-lg py-4 text-left px-6"
x-show="showModal"
#click.away="showModal=false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0">
<!-- Title -->
<div class="flex justify-between items-center mb-3">
<p class="text-xl font-bold">Añadir una mezcla</p>
<div class="cursor-pointer z-50" #click="showModal=false">
<svg class="fill-current text-black" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"></path>
</svg>
</div>
</div>
<!-- Content -->
<livewire:mix.user.create-mix-form/>
</div>
<!-- /Dialog -->
</div>
<!-- /Overlay -->
</div>
This is my Livewire Component:
<?php
namespace App\Http\Livewire\Mix\User;
use App\Models\Mix;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use Livewire\WithPagination;
class ShowUserMixes extends Component
{
use WithPagination;
protected $listeners = [
'deletedMix' => 'render',
'addedMix' => 'render'
];
public $showModal = false;
public function closeModal()
{
$this->showModal = false;
}
public function deleteUserMix($mix_id, $user_id)
{
if (Auth::user()->id !== $user_id) {
return false;
}
$mix = Mix::find($mix_id);
$mix->delete();
$this->emitSelf('deletedMix');
}
public function render()
{
return view('livewire.mix.user.show-user-mixes', [
'mixes' => Auth::user()->mixes()->where('active', 1)->paginate(10)
]);
}
}
This is the error I'm getting at the console:
Uncaught ReferenceError: showModal is not defined
at eval (eval at saferEval (alpine.js?df24:1701), :3:36)
at saferEval (alpine.js?df24:112)
at Component.evaluateReturnExpression (alpine.js?df24:1581)
at eval (alpine.js?df24:1557)
at Array.forEach ()
at Component.resolveBoundAttributes (alpine.js?df24:1530)
at Component.initializeElement (alpine.js?df24:1446)
at eval (alpine.js?df24:1430)
at eval (alpine.js?df24:1420)
at walk (alpine.js?df24:84)
I don't know what might me happening here, I'm using the #entangle properties as the Livewire docs do.
https://laravel-livewire.com/docs/2.x/alpine-js#extracting-blade-components
When you #entangle a property in AlpineJS, you're making a reference form your Alpine Property to another property in your Livewire Component.
Your issue is at the top of your modal definition, in your x-data declaration.
If you use x-data="{ open: #entangle('showModal') }", your showModal property will be bound inside alpine with the name open.
You only need to change the name of your definition:
<div
x-data="{ showModal: #entangle('showModal') }"
<!-- Other attributes here -->
>
<!-- Your modal goes here -->
</div>
You are trying to set and evaluate your livewire variable showModal when you should be setting and evaluating the property you set with Alpine which is open.
Try:
<div x-data="{ open:#entangle('showModal') }" #keydown.escape="open=false">
<button type="button" class="block p-2 btn-light-green" #click="open=true">Añadir Mezcla</button>
<!--Overlay-->
<div class="overflow-auto" style="background-color: rgba(0,0,0,0.5)" x-show="open" :class="{ 'absolute inset-0 z-10 flex items-center justify-center': open }">
<!--Dialog-->
<div class="bg-white w-11/12 md:max-w-md mx-auto rounded shadow-lg py-4 text-left px-6"
x-show="open"
#click.away="open=false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0">
<!-- Title -->
<div class="flex justify-between items-center mb-3">
<p class="text-xl font-bold">Añadir una mezcla</p>
<div class="cursor-pointer z-50" #click="open=false">
<svg class="fill-current text-black" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"></path>
</svg>
</div>
</div>
<!-- Content -->
<livewire:mix.user.create-mix-form/>
</div>
<!-- /Dialog -->
</div>
<!-- /Overlay -->
</div>

Q: how to make use of Alpinejs and tailwindcss within Laravel?

I am trying to make use of Alpinejs using Adam Wathan's responsive navbar using vuejs, but i am experimenting if i can get it to work with Alpinejs.
app.blade.php
<head>
[...]
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js" defer></script>
[...]
</head>
In case you are wondering if Alpine is already loaded, it is working trying a simple dropdown toggle, but with this approach i find it hard to get it working.
Navbar.blade.php
#guest('applicant')
#else
<header class="bg-gray-900 sm:flex sm:items-center sm:justify-between xl:bg-white" x-data="dropdown()">
<div class="flex justify-between px-4 py-3 xl:w-72 xl:bg-gray-900 xl:justify-center xl:py-5">
<div>
[...]
</div>
<div class="flex sm:hidden">
<button x-on:click="open" type="button"
class="px-2 text-gray-500 hover:text-white focus:outline-none focus:text-white">
<svg class="h-6 w-6 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path x-if="isOpen" fill-rule="evenodd" clip-rule="evenodd"
d="M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z" />
<path x-if="!isOpen" fill-rule="evenodd"
d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z" />
</svg>
</button>
</div>
</div>
<nav class="sm:flex sm:items-center sm:px-4 xl:flex-1 xl:justify-between"
:class="{ 'hidden': !isOpen, 'block': isOpen }" x-show="open" x-on:click.away="close">
<div class="hidden xl:block xl:relative xl:max-w-xs xl:w-full">
[...]
</div>
<div class="sm:flex sm:items-center">
[...]
<div class="relative px-5 py-5 sm:py-0 sm:ml-4 sm:px-0">
[...]
<Dropdown class="hidden sm:block">
<template #trigger="{ hasFocus, isOpen }">
<span class="block h-8 w-8 overflow-hidden rounded-full border-2 "
:class="[(hasFocus || isOpen) ? 'border-white xl:border-indigo-500' : 'border-gray-600 xl:border-gray-300']">
<img class="h-full w-full object-cover"
src="https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=256&q=80"
alt="">
</span>
</template>
<template #dropdown>
<div class="mt-3 bg-white xl:border rounded-lg w-48 py-2 shadow-xl">
<a href="#account" class="block hover:text-white text-gray-800 px-4 py-2 hover:bg-indigo-500">Account
settings</a>
<a href="#support"
class="block hover:text-white text-gray-800 mt-0 px-4 py-2 hover:bg-indigo-500">Support</a>
<a href="#sign-out" class="block hover:text-white text-gray-800 mt-0 px-4 py-2 hover:bg-indigo-500">Sign
out</a>
</div>
</template>
</Dropdown>
</div>
</div>
</nav>
</header>
<script>
function dropdown() {
return {
open: false,
open() {
this.show = true
},
close() {
this.show = false
},
toggle() {
this.isOpen = !this.isOpen
},
}
}
</script>
#endguest
You do not need to add the scripts to make a dropdown open and close.
You need to have x-data defined in a parent (to both button and dropdown) div. Then reference it in the button and/or dropdown elements.
A simple example:
<div x-data="{isOpen : false}">
<button x-on:click="isOpen = !isOpen" class="button">Menu</button>
<!-- you need to toggle isOpen state on click. You can also use #click just like in vue -->
<div x-show="isOpen" class="dropdown"> <!-- x-show to show and hide -->
Account settings
Support
</div>
</div>
That is all there is to make a dropdown using alpine js.
In my case i didn't my javascript nor installed it, i just used the cdn
{{-- scrip --}}
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js" defer></script>
and had installed tailwind css. my code is..
<div x-data="{dropdownMenu: false}" class="lg:inline-block relative">
<!-- Dropdown toggle button -->
<button #click="dropdownMenu = ! dropdownMenu"
class="text-base no-underline hover:bg-indigo-300 hover:text-cool-gray-900 rounded-3xl py-1 px-2 ">
<span class="sr-only">{{ Auth::user()->name }}</span>
<img class="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
alt="avatar">
</button>
<!-- Dropdown list -->
<div x-show="dropdownMenu"
class="absolute right-0 py-2 mt-2 bg-white bg-gray-100 rounded-md shadow-xl w-44">
<a href="#"
class="block px-4 py-2 text-sm text-gray-300 text-gray-700 hover:bg-gray-400 hover:text-white">
Your Profile
</a>
<a href="#"
class="block px-4 py-2 text-sm text-gray-300 text-gray-700 hover:bg-gray-400 hover:text-white">
Settings
</a>
<a href="#"
class="block px-4 py-2 text-sm text-gray-300 text-gray-700 hover:bg-gray-400 hover:text-white">
Reports
</a>
<a href="{{ route('logout') }}"
class="block px-4 py-2 text-sm text-gray-300 text-gray-700 hover:bg-gray-400 hover:text-white"
onclick="event.preventDefault();document.getElementById('logout-form').submit();">{{ __('Logout') }}</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST"
class="hidden">
{{ csrf_field() }}
</form>
</div>
</div>
this was my result when i click the avatar..
i hope this will assist you solve your problem

Resources