Custom validation on radio buttons if not selected | Tailwind - laravel

How to validate by showing a red ring around the radio button and make text red. Currently I am just showing a validation popup message. See image for a better idea.
Radio component
<input type="radio" {!! $attributes->class(['border-gray-300 text-gray-600 shadow-sm focus:border-gray-300 focus:ring focus:ring-gray-200 focus:ring-opacity-50', 'border-red-500 text-red-500' => $errors->has($attributes->get('name'))]) !!} />
Livewire view snippet
<div class="space-y-5">
#foreach ($ticket_types as $ticket_type)
<div>
<div class="relative flex items-start">
<div class="absolute flex items-center h-5">
<x-radio-button wire:model="ticket_type" #click="isQuestion = false" id="{{ $ticket_type->id }}" aria-describedby="{{ $ticket_type->id }}_description" value="{{ $ticket_type->id }}" class="h-4 w-4 transition duration-150 ease-in-out" />
</div>
<div class="pl-7 text-sm leading-5">
<x-jet-label for="{{ $ticket_type->id }}_title">
{{ $ticket_type->title }}
</x-jet-label>
<p id="{{ $ticket_type->id }}_description" class="text-gray-500">
{{ $ticket_type->description }}
</p>
</div>
</div>
</div>
#endforeach
</div>
<x-input-error for="ticket_type" />
Current look and feel
What I want to achieve (ignore the checkbox part)

You can conditionally include classes on components. So you could do something like:
<input type="radio"
{!! $attributes->class([
// these styles are applied by default
'border-gray-300 text-gray-600 shadow-sm focus:border-gray-300 focus:ring focus:ring-gray-200 focus:ring-opacity-50',
// these are applied if the name of the component is found in the error bag
// meaning there was an error
'border-red-500 text-red-500' => $errors->has($attributes->get('name'))
])
!!} />

Related

Laravel set class if radio button selected

I have these radio buttons:
It's a separate Laravel blade component.
#props(['active'])
#php
$classes = ($active ?? false)
? 'group relative border bg-white py-3 px-4 flex items-center justify-center text-black text-sm font-semibold uppercase focus:outline-none sm:flex-1 shadow-sm cursor-pointer transition duration-150 ease-in-out'
: 'group relative border py-3 px-4 flex items-center justify-center text-gray-300 text-sm font-semibold uppercase focus:outline-none sm:flex-1 shadow-sm cursor-pointer duration-150 ease-in-out';
#endphp
<label {{ $attributes->merge(['class' => $classes]) }}>
<input type="radio" name="size" value="{{ $slot }}" class="sr-only">
<span> {{ $slot }} </span>
<span class="absolute -inset-px pointer-events-none" aria-hidden="true"></span>
</label>
I call it on a different page like so:
<x-product-size>XS</x-product-size>
How do I change the variable if it's selected? I can only find solutions on changing it depending on the route by doing something like this:
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
You can use JS for this purpose.
add an id to your radio input for example radioID.
<input type="radio" id="radioID" name="size" value="{{ $slot }}" class="sr-only">
and:
<script>
$(document).ready(function () {
$('#radioID').on('change', function () {
var selected = this.value;
if(selected == "XS") // or a variable
var slot = "some data";
{
$("#spanID").append(' + slot + ');
}
});
});
</script>
If you want to add active to a div, span or something other you can do it like this using their CLASS or ID:
$('.someclass').first().addClass('active');
$('#someid').first().addClass('active');

user _id doesn't have a default value laravel

user_id doesn't have a default value that's is the error I'm getting when I clicked on the clone button and also user_id is fillable livewire movie show components
public function clone(Movie $movie)
{
$cloneMovie = $movie->replicate();
$cloneMovie->cloned_movie_id = $movie->id;
$cloneMovie->save();
}
livewire movie show blade view
<div>
<div class="">
<div class="p-1 bg-white rounded-md">
<div class="flex justify-between p-2 mb-2 text-white bg-gray-400 rounded-md">
<h3 class="font-bold text-white text-md">{{ $movie->title }}</h3>
<span class="text-xs">100 views</span>
</div>
<img src="{{ asset('/assets/images/blog/img-1.jpg') }}" alt="" class="w-auto rounded-md">
<p class="p-2">{{ $movie->body }}</p>
<button wire:click="clone({{ $movie->id }})">clone</button>
</div>
</div>
</div>
$cloneMovie->push();
Is what you’re looking for.

Better solution instead of $wire.set to pass computed Alpine value for livewire

I am new in Alpine / Livewire. This tag script works well expect one thing: the form is sending continiusly query to the server. Is better way to pass the form data for livewire?
<div class="col-span-6 my-2" x-data="{tags: [], newTag: ''}" >
<x-jet-input id="tags"
type="text"
wire:model="tags"
:value="$wire.set('tags','JSON.stringify(tags)')"
class="mt-1 block w-full hidden"
/>
<div class=" w-full mx-auto ">
<div class="tags-input border-gray-300 shadow-sm focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
<template x-for="tag in tags" :key="tag">
<span class="tags-input-tag">
<span x-text="tag"></span>
<button type="button" class="tags-input-remove" #click="tags = tags.filter(i => i !== tag)">
×
</button>
</span>
</template>
<input class="tags-input-text w-full" placeholder="{{__('Add music tags eg.') }} Pop Trap Live..."
#keydown.enter.prevent="if (newTag.trim() !== '') tags.push(newTag.trim()); newTag = ''"
#keydown.space.prevent="if (newTag.trim() !== '') tags.push(newTag.trim()); newTag = ''"
#keydown.backspace="if (newTag.trim() === '') tags.pop()"
x-model="newTag"
/>
</div>
<x-jet-input-error for="tags" class="mt-2" />
<x-slot name="actions">
<x-jet-action-message class="mr-3" on="saved">
{{ __('Saved.') }}
</x-jet-action-message>
<x-jet-button
>
{{ __('Save') }}
</x-jet-button>
</x-slot>
For everyone who has problem with $wire.set:
I find the solution and is not in documentation.
$wire.set need third parameter true to skip watch function and it won't send data in the background if no need
$wire.set('param','value',true)

Unable to swap Jetstream default TailwindCSS modal for Bootstrap Modal and still achieve the expected or same result as its was with TailwindCSS

Am working on a Laravel 8 package that swaps the #TALL stack for what I call the #BALL stack, and its basically a Bootstrap, AlpineJs Laravel, Livewire stack. Bootstrap 5 is leaning towards utility classes and no longer makes use of JQuery which gives room for AlpineJs so I really don't see a lot of upsides with Tailwind, not to mention I've gotten really comfortable with Bootstrap which should be all that matters at the end right?
Now the problem is this, I've been able to make changes to a lot of the JetStream blade files and if youve install Jetstream then your familiar with this image:
(Yes!!!! Its the exact same thing but with Bootstrap!)
but one particular component has kept me up for days and its the Bootstrap Modal.
Whenever I call the Bootstrap modal Livewire requests seem to make the Modal itself disappear leaving just the backdrop, forcing a page request before anything else can be clicked.
The final request is never completed
I have no way to hold the modal until a confirmation request is loaded and then programmatically disable the modal when finished.
The exact place I have this problem is in the views/api/api-token-manager.blade which is added by Jetstream when you install the livewire stack. My files looks like so:
My views/api/api-token-manager.blade
<!-- API Token List -->
// Inside a x-jet-action-section component
<x-slot name="content">
<div class="space-y-6">
#foreach ($this->user->tokens->sortBy('name') as $token)
<div class="d-flex justify-content-between">
<div>
{{ $token->name }}
</div>
<div class="d-flex">
#if ($token->last_used_at)
<div class="text-sm text-muted">
Last used {{ $token->last_used_at->diffForHumans() }}
</div>
#endif
#if (Laravel\Jetstream\Jetstream::hasPermissions())
<button class="btn btn-link text-secondary" data-toggle="modal"
wire:click="manageApiTokenPermissions({{ $token->id }})"
data-target="#managingApiTokenPermissions-{{ $token->id }}">
Permissions
</button>
#endif
<button class="btn btn-link text-danger text-decoration-none"
data-toggle="modal"
data-target="#confirmApiTokenDeletion-{{ $token->id }}">
Delete
</button>
</div>
</div>
<!-- API Token Permissions Modal -->
<x-jet-dialog-modal id="managingApiTokenPermissions-{{ $token->id }}">
<x-slot name="title">
API Token Permissions
</x-slot>
<x-slot name="content">
<div class="mt-2 row">
#foreach (Laravel\Jetstream\Jetstream::$permissions as $permission)
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="{{ $permission }}" wire:model.defer="updateApiTokenForm.permissions">
<label class="form-check-label">
{{ $permission }}
</label>
</div>
</div>
#endforeach
</div>
</x-slot>
<x-slot name="footer">
<x-jet-secondary-button data-dismiss="modal">
{{ __('Nevermind') }}
</x-jet-secondary-button>
<x-jet-button class="ml-2" wire:click="updateApiToken"
wire:click="$emit('updateApiToken', {{ $token->id }})"
data-dismiss="modal">
{{ __('Save') }}
</x-jet-button>
</x-slot>
</x-jet-dialog-modal>
#push('scripts')
<script>
Livewire.on('updateApiToken', id => {
#this.manageApiTokenPermissions(id)
#this.updateApiToken()
})
</script>
#endpush
<!-- Delete Token Confirmation Modal -->
<x-jet-confirmation-modal id="confirmApiTokenDeletion-{{ $token->id }}">
<x-slot name="title">
Delete API Token
</x-slot>
<x-slot name="content">
Are you sure you would like to delete this API token?
</x-slot>
<x-slot name="footer">
<x-jet-secondary-button data-dismiss="modal">
Nevermind
</x-jet-secondary-button>
<x-jet-danger-button class="ml-2"
wire:click="$emit('deleteApiToken', {{ $token->id }})"
data-dismiss="modal">
Delete
</x-jet-danger-button>
</x-slot>
</x-jet-confirmation-modal>
#push('scripts')
<script>
Livewire.on('deleteApiToken', id => {
#this.confirmApiTokenDeletion(id)
#this.deleteApiToken()
})
</script>
#endpush
#endforeach
</div>
</x-slot>
My x-jet-dialog-modal component
#props(['id' => null, 'maxWidth' => null])
<x-jet-modal :id="$id" :maxWidth="$maxWidth" {{ $attributes }}>
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ $title }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{{ $content }}
</div>
<div class="modal-footer">
{{ $footer }}
</div>
</div>
</x-jet-modal>
My x-jet-modal component
#props(['id', 'maxWidth', 'modal' => false])
#php
$id = $id ?? md5($attributes->wire('model'));
switch ($maxWidth ?? '') {
case 'sm':
$maxWidth = ' modal-sm';
break;
case 'md':
$maxWidth = '';
break;
case 'lg':
$maxWidth = ' modal-lg';
break;
case 'xl':
$maxWidth = ' modal-xl';
break;
case '2xl':
default:
$maxWidth = '';
break;
}
#endphp
<!-- Modal -->
<div class="modal fade" tabindex="-1" id="{{ $id }}" aria-labelledby="{{ $id }}" aria-hidden="true">
<div class="modal-dialog{{ $maxWidth }}">
{{ $slot }}
</div>
</div>
I really don't want to make the change to Tailwind regardless of the hype so I would love any assistance the community can render.
To make a direct contribution to the project click here.
Thanks!!!
I was able to solve the above problem with Livewire Event. Its important to note that the reason for all this hassle was to create assets to replace Jetstream assets without affecting the business logic i.e. MODEL and CONTROLLER.
Here is what my solution looks like:
<div class="mt-3">
<x-jet-danger-button wire:click="$emit('confirmingUserDeletion')"
wire:loading.attr="disabled">
Delete Account
</x-jet-danger-button>
</div>
<!-- Delete User Confirmation Modal -->
<x-jet-dialog-modal id="confirmingUserDeletionModal">
<x-slot name="title">
Delete Account
</x-slot>
<x-slot name="content">
Are you sure you want to delete your account? Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.
<div class="mt-4 w-75" x-data="{}" x-on:confirming-delete-user.window="setTimeout(() => $refs.password.focus(), 250)">
<x-jet-input type="password" class="{{ $errors->has('password') ? 'is-invalid' : '' }}" placeholder="Password"
x-ref="password"
wire:model.defer="password"
wire:keydown.enter="deleteUser" />
<x-jet-input-error for="password" />
</div>
</x-slot>
<x-slot name="footer">
<x-jet-secondary-button wire:click="$toggle('confirmingUserDeletion')"
wire:loading.attr="disabled"
data-dismiss="modal">
Nevermind
</x-jet-secondary-button>
<x-jet-danger-button wire:click="deleteUser" wire:loading.attr="disabled">
Delete Account
</x-jet-danger-button>
</x-slot>
</x-jet-dialog-modal>
#push('scripts')
<script>
Livewire.on('confirmingUserDeletion', () => {
#this.confirmUserDeletion()
new Bootstrap.Modal(document.getElementById('confirmingUserDeletionModal')).toggle()
})
</script>
#endpush

How to update v-model data in laravel blade file in edit form

I have a single file for creating and updating the category data.
while creating category, i am using v-model in title to also create slug in the same form.
works well when creating but i am facing issue with update / edit form part.
below is my code :
<input class="form-input mt-1 block w-full" name="name" v-model="title" v-on:keyup="getSlug" placeholder="Category Title" value="{{ $category->name ?? '' }}">
adding whole blade file code for reference:
#extends('layouts.backend.app')
#php
if(isset($record) && $record != null){
$edit = 1;
} else {
$edit = 0;
}
#endphp
#section('content')
<section id="app" class="container mx-auto">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold">
#if($edit) Edit #else Create #endif Category Form
</h1>
Back
</div>
<div class="border rounded mt-8 p-8">
<form action="#if($edit) {{route('category.update', $record->id)}} #else {{route('category.store')}} #endif" method="POST">
#csrf
<label class="block mb-4">
<span class="text-gray-700 font-bold">Name</span>
<input class="form-input mt-1 block w-full" name="name" v-model="title" v-on:keyup="getSlug" placeholder="Category Title" value="{{ $category->name ?? '' }}">
#error('name')
<span class=" text-red-400 invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</label>
<label class="block mb-4">
<span class="text-gray-700 font-bold">Slug</span>
<input
class="form-input mt-1 block w-full"
name="slug"
id="slug"
v-model="slug"
placeholder="Slug / Pretty URL">
#error('slug')
<span class=" text-red-400 invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</label>
<label class="block mb-8">
<span class="text-gray-700 font-bold">Banner</span>
<input
class="form-input mt-1 block w-full"
name="banner"
value="#if($edit) {{$record->banner}} #else https://source.unsplash.com/random #endif"
placeholder="Banner / URL">
</label>
<div class="flex justify-between">
<button class="px-4 py-2 border bg-gray-200 text-gray-900 rounded" type="submit">Submit</button>
<button class="px-4 py-2 border bg-red-400 text-white rounded" type="rest">Reset</button>
</div>
</form>
</div>
</section>
#endsection
as you can see the title input is use to create the slug on keyup event. now the given code don't use the data from database for pre populating the edit form title input field. because i am using v-model="title" which is in my app.js and is null at the moment.
How to either assign v-model="title" my current value from database.
This is not a vue component. its a laravel blade file. Kindly guide me for this.
Thanks
You can move the app.js code to the Blade view and populate title with the Blade expression
#extends('layouts.backend.app')
#php
if(isset($record) && $record != null){
$edit = 1;
} else {
$edit = 0;
}
#endphp
#section('content')
<section id="app" class="container mx-auto">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold">
#if($edit) Edit #else Create #endif Category Form
</h1>
Back
</div>
<div class="border rounded mt-8 p-8">
<form action="#if($edit) {{route('category.update', $record->id)}} #else {{route('category.store')}} #endif"
method="POST">
#csrf
<label class="block mb-4">
<span class="text-gray-700 font-bold">Name</span>
<input class="form-input mt-1 block w-full" name="name" v-model="title" v-on:keyup="getSlug"
placeholder="Category Title" value="{{ $category->name ?? '' }}">
#error('name')
<span class=" text-red-400 invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</label>
<label class="block mb-4">
<span class="text-gray-700 font-bold">Slug</span>
<input class="form-input mt-1 block w-full" name="slug" id="slug" v-model="slug"
placeholder="Slug / Pretty URL">
#error('slug')
<span class=" text-red-400 invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</label>
<label class="block mb-8">
<span class="text-gray-700 font-bold">Banner</span>
<input class="form-input mt-1 block w-full" name="banner"
value="#if($edit) {{$record->banner}} #else https://source.unsplash.com/random #endif"
placeholder="Banner / URL">
</label>
<div class="flex justify-between">
<button class="px-4 py-2 border bg-gray-200 text-gray-900 rounded" type="submit">Submit</button>
<button class="px-4 py-2 border bg-red-400 text-white rounded" type="rest">Reset</button>
</div>
</form>
</div>
</section>
{{-- Place the script here, after closing the section with id of app --}}
<script>
const app = new Vue({
el: '#app',
data() {
return {
title: '{{ $category->name }}'
}
},
methods: {
getSlug() {
this.title = this.title.toLowerCase()
.replace(/ /g,'-')
.replace(/[^\w-]+/g,'');
}
}
});
</script>
#endsection
Hope this helps
In The End, I went back to the old style of doing things.
code as below:
getSlug() {
var title = document.getElementById('title').value;
document.getElementById('slug').value = title.replace(/\s+/g, '-').toLowerCase().trim();
},
and replaced v-model with id tag on the input field.

Resources