Why the modal doesn't close in laravel livewire? - laravel

I'am using laravel livewire to delete records in two tables, the problem is the modal, the records are being deleted but the modal still shows.
the strange thing is that when I comment one of the lines of code to delete data, it works!
I'm using Bootstrap 4.1
this is my function:
public function delete($id)
{
DB::beginTransaction();
try
{
// If I comment on any of the following two lines (it doesn't matter what line it is), it works!
DetalleRamComputadora::where('fk_computadora', '=', $id)->delete();
Computadora::where('id', '=', $id)->delete();
DB::commit();
$this->emit('confirm'); // Close modal "confirm"
session()->flash('success', 'Registro eliminado con éxito');
} catch (\Throwable $th) {
DB::rollBack();
$this->emit('confirm'); // Close modal "confirm"
session()->flash('error', 'Ocurrió un error y no se almacenó el registro');
}
}
this is the script to close modal from livewire:
window.livewire.on('confirm', () => {
$('#delete_confirm').modal('hide');
});
help me please!!

I was able to solve the problem. Only I added this code in the div of the modal
**wire:ignore.self**
<div wire:ignore.self class="modal fade" id="delete_confirm" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
...
</div>

In your function delete, add a dispatch browser event
public function delete($id)
{
DB::beginTransaction();
try
{
/*...Your code ... */
$this->dispatchBrowserEvent('closeModal');
} catch (\Throwable $th) {
/*...Your code ... */
}
}
And in your app.blade.php, try adding this window event listener like so.
window.addEventListener('closeModal', event => {
$("#delete_confirm").modal('hide');
})
In that way, you will be able to close the modal by triggering the javascript from your frontend.
P.S.
There's a youtube video series for laravel livewire tutorial that actually uses Boostrap Modal for the CRUD functionality. You can watch it here!
https://www.youtube.com/watch?v=_DQi8TyA9hI

First of all, there's no way we can verify that #delete_confirm is actually the name of your modal. Secondly, check if the event confirm is being triggered.
window.livewire.on('confirm', () =>
{
alert('Yes, I have reached here');
});
If the event is being fired, then try the following:
window.livewire.on('confirm', () =>
{
$('.modal').modal('hide');
});
If this still does not work, force the modal to be destroyed completely:
window.livewire.on('confirm', () =>
{
$('.modal').modal('hide').data('bs.modal', null);
$('.modal').remove();
$('.modal-backdrop').remove();
$('body').removeClass('modal-open');
$('body').removeAttr('style');
});

I'm tired of fixing that problem also,but I got an idea that close that modal directly without any code of js and livewire,only in bootstrap itself. Here what I did:
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" wire:click.prevent="store()" data-bs-dismiss="modal">Add Students</button>

Adding wire:ignore.self on modal popup
<div wire:ignore.self class="" id="">
</div>

Related

I am stuck in Laravel Livewire in which I am using multiple select2 and it is a wizard form

I have a wizard in laravel livwire with 4 steps.
in the first step I have select box of select2 for multiple selection, everything works fine but when I go to step 2 and return back, the problems comes
Select2 distorts and loose it's styling
Selectbox does not contain it's selected values
I am new to livwire so do not enough about it.
Here is my blade component
I don't exactly know what is the purpose of wire:ignore but I used it from internet.
<div class="col-sm-12 col-md-6 col-xl-6 col-lg-6 mb-10">
<x-label for="vehicle_groups" class="required">Vehicle Groups</x-label>
<div wire:ignore>
<x-select-two class="form-select-solid" id="vehicle_groups" multiple>
<option value=""></option>
#foreach($vehicleGroups as $vehicleGroup)
<option value="{{ $vehicleGroup->id }}">{{ $vehicleGroup->group_name }}</option>
#endforeach
</x-select-two>
</div>
#error('vehicle_groups')
<x-error>{{ $message }}</x-error>
#enderror
</div>
<script>
document.addEventListener("livewire:load", () => {
$('#vehicle_groups').select2().on('change', function (e) {
var data = $('#vehicle_groups').select2("val");
#this.set('vehicle_groups', data);
});
});
</script>
here is my livewire component
<?php
namespace App\Http\Livewire;
use App\Models\Vgroup;
use Livewire\Component;
class Vehicle extends Component
{
public $currentPage = 1;
public $type, $vehicle_groups;
public function mount()
{
}
public function render()
{
$vehicleGroups = Vgroup::get(['id', 'group_name']);
return view('admin.livewire.vehicle', compact('vehicleGroups'));
}
public function gotToNextPage()
{
if ($this->currentPage === 1) {
$this->validate([
'type' => ['required'],
'vehicle_groups' => ['required']
]);
} elseif ($this->currentPage === 2) {
} else {
}
$this->currentPage++;
}
public function gotToPreviousPage()
{
$this->currentPage--;
}
public function submitForm()
{
$this->currentPage = 1;
}
}
wire:ignore you will use it, if you don't want to rerender a part of your code, for example imagine you have a js code, and when using it with livewire it can not work well, because livewire makes requests every time and the js code can't do it's job, now, you have to know that select2, creates it's own DOM and livewire keeps making requests(so you can see it's not a good combination), for that reason you need to add wire:ignore(to ignore select2).
So the problem that the styles doesn't render again, you could try adding the select 2 inside a DOMContentLoaded event:
document.addEventListener("DOMContentLoaded", () => {
$('#vehicle_groups').select2();
})
like this, the styles will be added when the page is loaded
on the other side, to get the data, you could try and add the ready event:
$(document).ready(function() {
$('#vehicle_groups').select2("val");
})
or
$(document).ready(function() {
$('#vehicle_groups').select2();
$('#vehicle_groups').val();
})
I see that you are using an event "livewire:load", in my case this event didn't work for me(in the inspector it throw me an error that the event didn't exist), you could try and use a hook instead of "livewire:load"
Livewire.hook('message.processed', (message, component) => {
})
this event will be called when all the requests of livewire are finished
https://laravel-livewire.com/docs/2.x/lifecycle-hooks
here is the link to the documentation if you want to check it
And last i want to comment something, i saw that you are using $vehicleGroup (i guess it's an array), you are getting the data on the view, all okei there, however you using the array on select 2, to overrite the data, is that what you want to do?(#this.set('vehicle_groups', data))
you could use the attribute wire:model(if you don't need to make requests every time in that select better use wire:model.defer, like this the only request that is going to make is when it's submited), to call a variable on livewire and store the value of the select
<x-select-two class="form-select-solid" wire:model="vehicle_variable" id="vehicle_groups" multiple>
<option value=""></option>
#foreach($vehicleGroups as $vehicleGroup)
<option value="{{ $vehicleGroup->id }}">
{{$vehicleGroup->group_name }}
</option>
#endforeach
</x-select-two>
wire:model="vehicle_variable"
now on livewire:
public $vehicle_variable
and on select2:
#this.set('vehicle_variable', data)
now you have the value on a variable and not an array(if it's an array)
to check if the data exists, you could use the debugger of laravel "ddd()" inside the function mount (the mount function, will be called when the page is being loaded):
public function mount()
{
ddd($vehicle_variable);
}
if it's not null, then select2 should work however, if the value is null you need to check your livewire
if you have any question, just ask and i will try to answer!

How to make alert with SweetAlert in Laravel

I would like to use SweetAlert to display my data.
I did my function
public function registration()
{
Gate::authorize('admin-level');
$url = URL::signedRoute(
'register',
now()->addMinutes(20));
return redirect('users')->with('success','$url');
}
and route that goes with it
Route::get('registration', [App\Http\Controllers\UserController::class, 'registration'])->name('registration');
The problem is with message, since I downloaded SweetAlert with composer I should probably got everything working, but then when I try to execute my class with button threw the route:
<button type="button" class="btn btn-outline-primary">{{ __('Registration link') }}</button>
#if(session('succes_message'))
<div class= "alert alert-succes">
{{session('succes_message')}}
</div>
#endif
Nothing pops up(when it should)
What might be wrong with it?
When you use ->with() it means, store items in the session for the next request.
return redirect('users')->with('success', '$url');
Here comes the question. What do you do after this?
Create a notification information or an alert (popup with SweetAlert)?
If it will be used as a notification, your code has no problem. If you want to make alert (popup with SweetAlert), your understanding is wrong.
Just because the class you are using uses the name alert, doesn't mean it make an alert with SweetAlert.
To use SweetAlert, you can add JavaScript in the header or before the </body> tag:
<script>
#if($message = session('succes_message'))
swal("{{ $message }}");
#endif
</script>
Or to use SweetAlert2 :
<script>
#if($message = session('succes_message'))
Swal.fire(
'Good job!',
'{{ $message }}',
'success'
)
#endif
</script>
If you are confused about placing the script in a specific blade view, please read my answer here.

Using data on dispatched event and trigger div

I have a component that dispatches a browser event with an object
// Livewire Component Method
public function passToDashboard($dataId)
{
$data = Model::find($dataId);
$this->dispatchBrowserEvent('show-data', ['data' => $data]);
}
Now on my dashboard blade view i've got
<div class="some-classes" x-data="{dataDisplay:false}">
<div x-show="dataDisplay">
{{-- This is where i want to use the object --}}
{{ $data->title }}
</div>
</div>
<script>
window.addEventListener('show-data', data => {
console.log(data.detail.title); // outputs title just fine
})
</script>
The question is, how to 'unhide' the dataDisplay and how to show it with the passed data? Thanks!
You can listen for these events directly on the element using #event-name.window="dataDisplay = true"
To get the event data, you use the $event variable and it should be under $event.detail.data.title
Use x-text to get the text onto the element. See my full example below.
So in your case:
<div class="some-classes" x-data="{dataDisplay:false, title: ''}" #event-data.window="dataDisplay = true; title = $event.detail.data.title">
<div x-show="dataDisplay">
<h3 x-text="title"></h3>
</div>
</div>
The documentation for this can be found here: https://laravel-livewire.com/docs/2.x/events#browser
Do notice I changed the event name, because it does apparently not work if you start the event name with "show". When I changed to "event-data" or anything else it started working again.

How to show data of a single id using Vue js?

I'm displaying all records on a page at this URI xxx.test/employer/search/filter-by. I'm using Algolia Search to display all of the records/results. I added a button called View Profile and attached an empty method to it called showProfile.
When a user clicks on this button, I want to display this specific profile/record on a new page by itself. If I was fetching data on my own, i.e. without Algolia's code I would be able to do this, but in this case I'm not really sure how to do this.
EmployerSearchComponent.vue:
<ais-instant-search
:search-client="searchClient"
index-name="candidate_profiles"
>
<ais-search-box v-model="query" class="mb-3" placeholder="Search by job title, company name or city..." />
<ais-configure
:restrictSearchableAttributes="['job_title', 'employment_type']"
:hitsPerPage="25"
/>
<b-row class="job-search-card">
<ais-hits class="w-100">
<template slot="item" slot-scope="{ item }">
<b-col cols="12" class="mb-3">
<b-card>
<div class="float-right">
<i class="flaticon-paper-plane"></i> View Profile
</div>
<h4 style="margin-bottom: 20px;"><router-link to="/">{{item.job_title}}</router-link></h4>
<p>Type: {{item.employment_type}}</p>
<b-card-text class="mt-2"><span v-if="item.experience && item.experience.length < 300">{{item.experience}}</span><span v-else>{{item.experience && item.experience.substring(0,300)+"..."}}</span></b-card-text>
</b-card>
</b-col>
</template>
</ais-hits>
<ais-pagination />
</b-row>
</ais-instant-search>
If I click on the network tab in the console, and on the algolia query search URI, I can see that the search results are in results[0].hits. Below is a screenshot of this data.
P.S. My data is empty it just contains algolia client ID's.
How can I display a single id on a new page? I understand that I need to get the id from the record that is being displayed, and show this information on a new page, but I don't know how to put it all together.
Again I think I'll need a route, so I created this
Route::get('/employer/search/filter-by/show/{id}', 'EmployerSearchController#show')->name('employer.search.show');
I'm using Laravel for my backend. Thanks in advance.
------------------------------------- UPDATED: -------------------------------------
I feel like I'm really close, but $itemId in my controller after I die and dump returns "undefined" for some reason.
router.js (Vue Router):
{
path: '/employer/search/filter-by/:itemId/show',
name: 'employer-search-index',
component: SearchIndex,
meta: {
breadcrumb: 'Search Candidates',
requiresAuthEmployer: true,
employerHasPaid: true
},
},
EmployerSearchComponent.vue - with the <router-link> button:
<template slot="item" slot-scope="{ item }">
<b-col cols="12" class="mb-3">
<b-card>
<div class="float-right">
<router-link class="apply-job-btn btn btn-radius theme-btn apply-it" :to="'/employer/search/filter-by/' + item.id + '/show'">View Profile</router-link>
</div>
<h4 style="margin-bottom: 20px;"><router-link to="/">{{item.job_title}}</router-link></h4>
<p>Type: {{item.employment_type}}</p>
<b-card-text class="mt-2"><span v-if="item.experience && item.experience.length < 300">{{item.experience}}</span><span v-else>{{item.experience && item.experience.substring(0,300)+"..."}}</span></b-card-text>
</b-card>
</b-col>
</template>
EmployerSearchController.php:
public function show ($itemId)
{
$candidateProfiles = CandidateProfile::with(['user', 'photo', 'resume', 'video'])
->where('id', '=', $itemId)->get();
return Response::json(array(
'candidateProfiles' => $candidateProfiles,
), 200);
}
api.php:
Route::get('/employer/search/filter-by/{itemId}/show', 'EmployerSearchController#show')->name('employer.search.show');
And Finally, in the .vue file that shows the Full Single Profile, I'm loading the data like this.
mounted() {
this.loadCandidateProfileData();
},
methods: {
loadCandidateProfileData: async function () {
try {
const response = await employerService.loadCandidateProfileData();
this.candidateProfiles = response.data.candidateProfiles;
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
},
}
And the employerService.js file from the above code:
export function loadCandidateProfileData(itemId, data) {
return http().get(`employer/search/filter-by/${itemId}/show`, data);
}
As you suggest, you'll need an API endpoint to fetch the data from, returning it as a JSON object. You'll need to add a route to your client-side routes that takes the job ID (or slug) as a parameter. In your job component, you can retrieve the route param (e.g. in the created() method) as $route.params.id, for example, and use that to fetch the data from your API.
If your Algolia search is returning all the data that you want to display on your single job listing page, you could just put that in your Vuex store and display it without having to make another HTTP request. The downside of that would be that if a user bookmarked the page to return to later, there'd be no data in the store to display.
Thank you to Nilson Jacques responses. If you follow our conversation.
Finally I got the itemId param from the route and passed it to loadCandidateProfileData() like this:
loadCandidateProfileData: async function() {
try {
const itemId = this.$route.params.itemId;
const response = await employerService.loadCandidateProfileData(itemId);
this.candidateProfiles = response.data.candidateProfiles;
console.log(this.candidateProfiles);
if (response.data.candidateProfiles.current_page < response.data.candidateProfiles.last_page) {
this.moreExists = true;
this.nextPage = response.data.candidateProfiles.current_page + 1;
} else {
this.moreExists = false;
}
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
},

CakePHP 1.3 - Send non-form data to controller with js helper

I have a ul of dynamic buttons in my view that appears like the following:
<ul id="dashboard_list">
<li id="id_100" class="btn btn-primary">
<a id="id_100" href="/plugin_name/controller_name/action_name/100">Default View</a>
</li>
<li id="id_200" class="btn btn-primary">
<a id="id_200" href="/plugin_name/controller_name/action_name/200">Second View</a>
</li>
<li id="id_300" class="btn btn-primary">
<a id="id_300" href="/plugin_name/controller_name/action_name/300">Third View</a>
</li>
</ul>
The above links are created using the JSHelper as follows:
echo $this->Html->link($view->name, '/plugin_name/controller_name/action_name/'. $view->id, array('class' => 'ajax-link', 'id'=> $view->id));
I'm using the script below that I found while researching:
// onClick function
function onClick(){
$('#view_container').load($(this).attr('href'), onSuccess);
return false;
}
// activate ajax links to call the onClick function
$('.ajax-link').live('click', onClick);
// onSuccess-callback function
function onSuccess(){}
Now, in my controller / action im doing a simple check for data as follows:
function actionName() {
if ($this->data != null) {
die('We has data!');
}
else
{
die('We has no data.');
}
}
My #view_container element updates properly with "We has no data" on every click. So, I'm obviously not communicating the link's view id number (data) between the view and the controller.
Can anyone offer some direction on how to implement this functionality in CakePHP 1.3 to access the selected id (variable) in the controller? I mostly seem to find form submission examples (or just dead links), and I unfortunately don't have the option to upgrade cakePHP.
FYI: The proper helpers, scripts and the js->writeBuffer are being included.
Thank you for any responses in advance!
Rewrite your function as follows:
function actionName($id) {
debug($id);
if ($this->data != null) {
die('We has data!');
}
else
{
die('We has no data.');
}
}
If you need to do more than one variable in the URL ex:
href="/plugin_name/controller_name/action_name/300/yellow/bannana"
Then your function would look like:
function actionName($id,$color,$fruit) {
}

Resources