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

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

Related

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)

Laravel post from modal tries to GET instead of POSt

The modal gets data from the #foreach and displays them. And from the form it posts the data entered to the next POST route {{ route('user.give') }}. However, it keeps trying to perform a GET request to a different route(precisely the route just above it). Please help, thank you for helping
#section('content')
#if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
#endif
#if(count($orgs) > 0)
#foreach($orgs as $org)
<div class="card m-1 col-md-3 p-0">
<img class="card-img-top" src="{{ asset('storage/'.$org->logo) }}" alt="{{ $org->name }} logo" />
<div class="card-body">
<span class="card-title"><strong><i class="fa fa-church"></i> {{ $org->name }}</strong></span>
<div class="small"><i class="fa fa-exchange-alt"></i> {{ $org->alias }}</div>
<p class="card-text"></p>
<button class="btn btn-outline-primary rounded-0" data-toggle="modal" data-target="#exampleModalCenter">
<i class="fa fa-credit-card"></i>
Give
</button>
<i class="fa fa-cogs"></i>
</div>
</div>
#endforeach
#else
<p>Sorry, there's no match for your search.</p>
#endif
and right below is the modal. It is actually continuation of the code above. The modal actually comes up, the issue only occurs at the point of posting
<!-- form modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">Details</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<cite>You are about to give to {{ $org->name }}</cite>
<!-- form -->
<form method="POST" action="{{ route('user.give') }}">
#csrf
<input type="hidden" name="organisation_name" value="{{ $org->name }}">
<input type="hidden" name="organisation_id" value="{{ $org->id }}">
<input type="hidden" name="organisation_email" value="{{ $org->email }}">
<input type="hidden" name="organisation_logo" value="{{ $org->logo }}">
<div class="form-group">
<label for="amount">Amount</label>
<input type="number" class="form-control rounded-0" name="amount" aria-describedby="amount" placeholder="e.g 5000" required>
</div>
<div class="form-group">
<label for="purpose">Purpose</label>
<input type="text" class="form-control rounded-0" name="purpose" placeholder="e.g tithe">
</div>
<button type="submit" class="btn btn-outline border-dark text-dark rounded-0" id="proceed">
Proceed
</button>
</form>
<!-- //form -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline border-dark text-dark rounded-0" data-dismiss="modal">Close</button>
<!-- <button type="button" class="btn btn-primary">Save changes</button> -->
</div>
</div>
</div>
</div>
<!-- //form modal -->
#endsection
Here are my routes
Route::middleware('auth')->group(function (){
Route::post('/user/search', 'HomeController#search')->name('user.search');
Route::post('/user/give/', 'HomeController#give')->name('user.give');
Route::get('/user/verify/{reference}', 'HomeController#verify')->name('user.verify');
It visits user.search instead of user.give and it does that with a GET
$org is not what you want in your modal: it is the last $org from your loop over your $orgs array since it is not inside the #foreach loop.
You need to generate your modal dynamically (through javaScript or other) when you click the button showing the modal.
Found it. Apparently I added a validation rule that was failing when the form was submitted. Since the validation error could not be sent to the modal it was performing a GET.
Breathing fresh air now. Thanks for your support y’all

How to work with pagination in Laravel Algolia search

Pagination is not working. It shows empty page on clicking 2 page.It is not fetching the second page from the search query. And also token is not showing on
2nd page url. Is this is the reason then how to add csrf_token to pagination
Route.
I am using algolia to search
Route::get('posts/search','PostController#search')->name('posts.search');
Controller
public function search(Request $request){
if($request->has('q')) {
$request->flashOnly('q');
$results = Post::search($request->q)->paginate(5);
} else {
$results = [];
}
return view('posts.search')->with('results', $results);
}
View
#extends('layouts.app')
#section('content')
<div class="container">
<h1>Search for Posts</h1>
<form action="{{ route('posts.search') }}" method="get">
{{ csrf_field() }}
<div class="input-group">
<input type="text" name="q" class="form-control input-lg" placeholder="Search for a post..." value="{{ old('q') }}"/>
<span class="input-group-btn">
<button class="btn btn-default btn-lg" type="submit">Search</button>
</span>
</div>
</form>
<hr />
#foreach ($results as $post)
<div class="row" style="margin-top: 20px;">
<div class="col-md-8">
<h3>{{ $post->title }}</h3>
</div>
<div class="col-md-4">
#if ($post->published)
<h4><span class="label label-success pull-right">PUBLISHED</span><h4>
#else
<h4><span class="label label-default pull-right">DRAFT</span><h4>
#endif
</div>
</div>
<div class="row">
<div class="col-md-12">
<p>
{{ str_limit($post->content, 250) }}
</p>
</div>
</div>
#endforeach
#if (count($results) > 0)
<hr />
<div class="text-center">
{{ $results->links() }}
</div>
#endif
#endsection
Try this one may be help
// Making sure the user entered a keyword.
if($request->has('q')) {
// Using the Laravel Scout syntax to search the posts table.
$results = Post::search($request->get('q'))->paginate(15);
}
Update my answer sometimes help this
return view('posts.search',['results' => $results]);
Try :
{{$results->appends(request()->query())->links()}}
insted of
{{ $results->links() }}

Delete nested object from array in Vue

trying to delete a nested object from an array. I've done some research and came across an example similar to mine, Vue.js Remove a Nested Object from Array.
However my issue is a slight more complex as I am trying to delete the object with ajax request. Essentially my engagement array contains nested objects of questions
so here is how I am displaying the list of questions for the engagement
<div v-for="question in engagement.questions" :key="question.id">
<div>
<div ">
{{ question.question }}
</div>
<div >
<span>Answered: </span>
<input type="checkbox" v-model="question.answered">
</div>
</div>
</div>
this is the button that will delete the question
<b-btn #click="deleteQuestion(engagement, question)">Confirm</b-btn>
and this is method that dispatches to the store
deleteQuestion(engagement, question) {
this.$store.dispatch('deleteQuestion', id)
.then(() => {
this.$router.push({path: '/engagement/' +this.engagement.id , query: {alert: 'The Question Was Succesfully Deleted'}});
})
},
and this is the store method
deleteQuestion(context, id) {
axios.delete('/questions/' + id)
.then(response => {
context.commit('deleteQuestion', id)
})
.catch(error => {
console.log(error)
})
},
Right now my alarm is getting “id is not defined” although I have other variations of this code where I will get a 500 internal server error which makes me think that I am not properly capturing the id of the question so it knows which one to delete…
below is the alarm I am getting in the console. I also did which is what the first arrow is pointing to for the observer
console.log(question)
the issue was that the b-model element that contained the #click="deleteQuestion" was outside of the div that contained the v-for so when I would click on the b-modal button it wasn't grabbing the id of the question. So I moved the b-modal to that div and it worked. Thank you for the help.
<div class="card mb-3" v-for="(question, index) in engagement.questions" :key="index">
<div class="card-header">
<div class="h6 m-0 justify-content-between d-flex">
<router-link class="btn btn-sm btn-primary mr-3" to="#" ><i class="far fa-edit mr-2"></i>Edit</router-link>
<b-btn class="outline-secondary" size="sm" v-b-modal.myQuestion><i class="fas fa-trash"></i><span class="ml-2">Delete</span></b-btn>
</div>
</div>
<div class="card-body bg-light d-flex justify-content-between">
<div class="h4 mr-5 text-left">
{{ question.question }}
</div>
<div class="ml-5 d-flex align-self-center">
<span class="font-weight-bold mr-2">Answered: </span>
<input class="mt-2" type="checkbox" v-model="question.answered">
</div>
</div>
<!-- this is the modal for deleting a question -->
<b-modal id="myQuestion" ref="myModalQuestion" hide-footer title="Delete Question">
<div class="d-block text-left">
<h5>Are you sure you want to delete question?</h5>
<br>
<p><strong>*Warning:</strong> Can not be undone once deleted.</p>
</div>
<div class="d-flex">
<b-btn class="mt-3" variant="danger" #click="hideModal">Cancel</b-btn>
<b-btn class="mt-3 ml-auto" variant="outline-success" #click="deleteQuestion(engagement, question.id)">Confirm</b-btn>
</div>
</b-modal>
</div>

Laravel Ajax updating variable

<div class="panel panel-primary">
<div class="panel-heading">Add user Status</div>
<form action="{{ route('post.create') }}" method="post">
<div class="panel-body">
<div class="form-group">
<textarea class="form-control" name="body" id="new-post" rows="5" placeholder="Your Post"></textarea>
</div>
</div>
<div class="panel-footer clearfix">
<button class="btn btn-info pull-right"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></i> Post</button>
</div>
<input type="hidden" value="{{ Session::token() }}" name="_token">
</form>
</div>
<div id="main" class="main">
<header><h3>What other people say...</h3></header>
#foreach($top_15_post as $status)
{!!
view ('layouts.app-internal.user-status-layaout',[
'status' =>$status,
'user' =>\App\User::find($status->user_id),
'comments'=>\App\Comment::where('status_id',$status->id)->get(),
'countcomment'=>\App\Comment::where('status_id',$status->id)->count(),
'countlike'=>\App\Like::where('like',1)->where('post_id',$status->id)->count() ,
'countdislike'=>\App\Like::where('like',0)->where('post_id',$status->id)->count(),
])->render()
!!}
#endforeach
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Right part -->
</div>
</div>
</div>
This is my home.blade.php in laravel. im simply implementing a posting text system which user can comment and like to the posts.I'm passing above variables to the user-status-layaout.blade.php, it shows the comments and likes as well as the posts.
This is my user-status-layaout.blade.php.My like and dislike functions are working properly. but i want update like count using ajax.How do i update countlike and countdislike variable instantly using ajax?
<div class="panel panel-default">
<div class="panel-heading">{{ $status->user->name }} Posted on {{ $status->created_at }}</div>
<div class="panel-body">
<div class="row">
<div class="col-md-1">
<img src="{{Auth::user()->getavatar()}}" class="img-responsive">
</div>
<div class="post" data-postid="{{ $status->id }}">
<p>{{ $status->body }}</p>
<div class="interaction">
{{ Auth::user()->likes()->where('post_id', $status->id)->first() ? Auth::user()->likes()->where('post_id', $status->id)->first()->like == 1 ? 'You like this post' : 'Like' : 'Like' }} |
{{ Auth::user()->likes()->where('post_id', $status->id)->first() ? Auth::user()->likes()->where('post_id', $status->id)->first()->like == 0 ? 'You don\'t like this post' : 'Dislike' : 'Dislike' }}
#if(Auth::user() == $status->user)
|
Edit |
<form method="get" action="{{ route('post.delete', ['post_id' => $status->id]) }}" accept-charset="UTF-8" style="display:inline">
<button class="btn btn-xs btn-danger" type="button" data-toggle="modal" data-target="#confirmDelete" data-title="Delete Post" data-message="Are you sure you want to delete this post ?">
<i class="glyphicon glyphicon-trash"></i> Delete
</button>
</form>
#endif
<ul class="list-unstyled list-inline ">
<li>
<button class="btn btn-xs btn-info" type="button" data-toggle="collapse" data-target="#view-comments-{{$status->id}}" aria-expanded="false" aria-controls="collapseExample">
<i class="fa fa-comments"></i>View & Comment </button>
</li>
<li>{{$countcomment}} comments</li>
<li >{{$countlike}} likes</li>
<li>{{$countdislike}} dislikes</li>
</ul>
</div>
</div>
</div>
</div>
<div class="panel-footer clearfix">
<div class="form-group">
<form action="{{ route('comment.create') }}" method="post">
<div class="input-group">
<input type="text" class="form-control" name="comment-text" id="comment-text" placeholder="Add Comment">
<span class="input-group-btn">
<button class="btn btn-info btn-xs" type="submit"><span class="glyphicon glyphicon-plus"></span>Add </button>
</span>
<input type="hidden" value="{{ $status->id }}" name="id" id="id">
</div><!-- /input-group -->
<input type="hidden" value="{{ Session::token() }}" name="_token">
</form>
</div>
<div class="collapse" id="view-comments-{{$status->id}}">
#if($comments->first())
#foreach($comments as $comment)
<blockquote >
<div class="row" >
<div class="col-md-1" >
<img src="{{\App\User::find($comment->user_id)->getavatar()}}" class="img-responsive">
</div>
<div class="col-md-11">
<ul class=" list-inline list-unstyled ">
<li>
{{\App\User::find($comment->user_id)->name}}
</li>
</ul>
<div class="comment" data-commentid="{{ $comment->id }}">
<p> {{$comment->comment_text}}</p>
<div class="interaction1">
#if(Auth::user() == $comment->user)
Edit |
<form method="get" action="{{ route('comment.delete', ['comment_id' => $comment->id]) }}" accept-charset="UTF-8" style="display:inline">
<button class="btn btn-xs btn-danger" type="button" data-toggle="modal" data-target="#confirmDeleteComment" data-title="Delete Comment" data-message="Are you sure you want to delete this comment ?">
<i class="glyphicon glyphicon-trash"></i> Delete
</button>
</form>
#endif
</div>
</div>
</div>
</div>
<p> posted {{$comment->created_at->diffForHumans()}}</p>
</blockquote>
#endforeach
#else
<p>This status no comment</p>
#endif
</div>
</div>
You could create a timer function using WindowTimers.setTimeout()
$('some-selector').load(function(){
function likeTimer() {
setTimeout(function(){
// Some JSON request that updates the elements' values.
likeTimer();
}, 1000);
}
});
Or you could poll the server periodically with WindowTimers.setInterval()
$('some-selector').load(function(){
setInterval(function() {
// Some JSON request that updates the elements' values.
}, 1000);
});
Then just define a route in Laravel that returns JSON for the data you want to update.

Resources