I'm using a full-page component that contains a modal child component that handles updating the model. When the user clicks save on the modal and the update is made, I want to refresh the parent component, which is really the overall page. However, when I save, nothing happens.
Here's my parent Component code:
class LocationComponent extends Component
{
public Location $location;
public $client;
public $legalEntities;
public $smsTypes;
public $clientContacts;
public $locationContacts;
protected $listeners = [
'refreshParent' => '$refresh'
];
public function mount()
{
$this->legalEntities = LegalEntityType::all();
$this->smsTypes = SmsType::all();
$this->roles = Role::all();
$this->client = $this->location->client()->first();
$this->clientContacts = $this->client->users()->get();
$this->locationContacts = $this->location->users()->get();
}
public function render()
{
return view('livewire.locations.edit');
}
}
And here's the child component (modal):
class LocationEditModal extends Component
{
public $states;
public Location $location;
protected $rules = [
...
];
public function mount()
{
$this->states = USState::all();
}
public function render()
{
return view('livewire.location-edit-modal');
}
public function save()
{
$validatedData = $this->validate();
$this->location->save($validatedData);
$this->dispatchBrowserEvent('closeModal');
$this->emit('refreshParent');
}
}
I added this code in my parent blade file to see if the event was firing, and it appears it's not:
<script>
Livewire.on('refreshParent', event => {
alert('the refreshParent event was fired');
});
</script>
Here's the route from my web routes file:
Route::get('/locations/{location}', [LocationComponent::class, '__invoke'])
->middleware('auth')
->name('locations.edit');
I've also tried changing $refresh to just render, but that also didn't work.
UPDATE:
Ok, after moving my event listener script to my main layout file, I'm getting the event fired alert now. Still no update of the parent component, but it's a start.
UPDATE 2:
I'm wondering if it's how I'm referencing the model data in my parent component blade view. I'm just referencing it how I normally would like this:
<p class="d-flex" id="name">{{ $location->name }}</p>
I'm not sure if my parent component is maybe not getting the updated $location?
UPDATE 3:
Based on my last update, I thought maybe I need to refresh the model, so I tried adding this to my parent component mount method, but it didn't work either:
$this->location->refresh();
Not sure if this is relevant, but in my parent component, which has the $location, it's calling the child modal component like this:
<livewire:location-edit-modal :location="$location"/>
Again, just putting that in there in case it helps with a solution. I don't know if I'm doing some kind of a circular thing that's not going to work right? Grasping at straws at this point..
RESOLUTION
Ok, this probably won't help anyone else, but it seems the theme I am using had some left over Laravel 7 things that weren't removed (I think). Per the Livewire docs about upgrading (https://laravel-livewire.com/docs/2.x/upgrading) you're supposed to remove the ->namespace($this->namespace) line from the RouteServiceProvider if you're on version 7 of Laravel. Version 8 already has it removed. I thought I was using Laravel 8, but maybe there was an upgrade done on this theme and it was never removed.
The other problem was I was using #yield('content') in my layout, and that's apparently not supported anymore. With version 2 of livewire, it should use the {{ $slot }} syntax.
Are you sharing your actual code here? I'm a bit confused since neither your LocationComponent or LocationEditModal components ever actually sets a value to $this->location.
Either way, there is no need to re-render the whole LocationComponent modal, you can just refresh the $this->location to the updated version of the location to update the values.
Something like this should work:
class LocationComponent extends Component {
protected $listeners = [
'locationUpdated'
];
public function locationUpdated()
{
$this->location->refresh();
}
}
class LocationEditModal extends Component {
public function save()
{
$this->emit('locationUpdated');
}
}
I don't believe you shared you're actual code, so it's hard to make an accurate recommendation, but it doesn't seem like your modal needs be a separate component at all. Your LocationComponent is already isolated to a single Location anyways; I would just have the save/update logic on the LocationComponent directly instead of a whole separate component.
Related
I need to send the same result to almost every view page, so I need to bind the variables and return with every controller.
My sample code
public function index()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.index', compact('drcategory','locations'));
}
public function contact()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.contact', compact('drcategory','locations'));
}
But as you see, I need to write same code over and over again. How can I write it once and include it any function whenever I need?
I thought about using a constructor, but I cannot figure out how I can implement this.
You are able to achieve this by using the View::share() function within the AppServicerProvider:
App\Providers\AppServiceProvider.php:
public function __construct()
{
use View::Share('variableName', $variableValue );
}
Then, within your controller, you call your view as normal:
public function myTestAction()
{
return view('view.name.here');
}
Now you can call your variable within the view:
<p>{{ variableName }}</p>
You can read more in the docs.
There are a few ways to implement this.
You can go with a service, a provider or, like you said, within the constructor.
I am guessing you will share this between more parts of your code, not just this controller and for such, I would do a service with static calls if the code is that short and focused.
If you are absolutely sure it is only a special case for this controller then you can do:
class YourController
{
protected $drcategory;
public function __construct()
{
$this->drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
}
// Your other functions here
}
In the end, I would still put your query under a Service or Provider and pass that to the controller instead of having it directly there. Maybe something extra to explore? :)
For this, you can use View Composer Binding feature of laravel
add this is in boot function of AppServiceProvider
View::composer('*', function ($view) {
$view->with('drcategory', DoctorCategory::orderBy('speciality', 'asc')->get());
$view->with('locations', Location::get());
}); //please import class...
when you visit on every page you can access drcategory and location object every time
and no need to send drcategory and location form every controller to view.
Edit your controller method
public function index()
{
return view('visitor.index');
}
#Sunil mentioned way View Composer Binding is the best way to achieve this.
I have a Laravel app which has an object, Position, which is created via a form.
class Position extends Model
{
protected $dispatchesEvents = [
'creating' => PositionCreating::class,
];
And this calls an event of the PositionCreating class, which I've tested, and is correctly firing. The underlying code also works to give me success or fail criteria.
class PositionCreating
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(Position $position)
{
if (some_good_stuff())
{
//keep creating the object
} else {
//stop creating the object
}
}
If it works, that's fine, I just let the __construct() function finish executing and everything, including the pre-execution code I want, works perfectly.
But I don't know how to actually stop the creation of the object. I can, of course use the dd() function or something (which works and stops creation of the object as expected), but I want to present a readable error to the user in a friendly manner. What function or commands should I be using to cancel the creation of the object to return back to my position.create method?
A bit late answer but this is a way to do it. Models fire several events. The one you're looking for is probably the "created" event. Each model event receive an instance of the model so you could just attach an event on your model, just like this:
protected $dispatchesEvents = [
'created' => PositionCreated::class,
];
Inside your "PositionCreated" event, add a public property to get the model instance,like this:
public $position;
public function __construct(Position $position)
{
$this->position=$position;
}
Finally just add the logic on your "handle" method inside your event listener.
public function handle($event)
{
if($something)
{
$event->position->delete();
}
}
This should do the work.You can check for the other events and see wich one suits you the most.
I have setup my navigation menu from a ViewComposer (see laravel view composers: https://laravel.com/docs/5.6/views#view-composers) like this
View::composer('partials.nav', function ($view) {
$view->with('menu', Nav::all());
});
What I need is that from some controllers to setup which navigation item is active, ie "current section".
Question:
How do I send from some controllers a variable to "partials.nav" like currentNavItem?
Do I send it with the rest of the variables for returned view?
like
return view('page.blade.php",$viewVariables + $optionalVariablesForPartialsViews);
It looks spammy
Side notes:
I use laravel 5.6
Later edit
It looks Laravel 5.1 : Passing Data to View Composer might be an options. I will try and get back .
Because the $variable you want to send differs in different controller's actions yes you need to specify the $variable
return view('page.blade.php",$viewVariables,$variablesForPartialsViews);
of course you might need to set a default value for the $variable in order to avoid undefined variable error
You should handle the parameters.
for exemple:
public function compose(View $view)
{
$view->with('page', $this->getPage());
}
public function getPage()
{
$viewVariables = 2;
$optionalVariablesForPartialsViews = 1;
return $viewVariables + $optionalVariablesForPartialsViews;
}
Under your app folder make a class named yourClassNameFacade. Your class would look like this.
class yourClassNameFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'keyNameYouDecide';
}
}
Then go to the file app/Providers/AppServiceProvider.php and add to the register function
public function register()
{
$this->app->bind('keyNameYouDecide', function (){
//below your logic, in my case a call to the eloquent database model to retrieve all items.
//but you can return whatever you want and its available in your whole application.
return \App\MyEloquentClassName::all();
});
}
Then in your view or any other place you want it in your application you do this to reference it.
view is the following code:
{{ resolve('keyNameYouDecide') }}
if you want to check what is in it do this:
{{ ddd(resolve('keyNameYouDecide')) }}
anywhere else in your code you can just do:
resolve('keyNameYouDecide'))
I have a Class Post. Created the Model, Restfull Resource Controller.
Now i implemented the show function in my Resource Controller.
So if someone routes to /post/234234 he will automatically see the post.
But now I want to create a new static function on myself. Let's call it myFunction(). I don't like to hand over the id all the time. Isn't there a possibilty to create a object or something like that, so I can use this like that Post::myFunction(), without hand over the id all the time.
It's similar to the Auth Class. I mean I can check if the user is logged in just like that Auth::check().
Update
Let's describe it more clearly. This is my PostController#show:
public function show($id)
{
//Get board information
$post = Post::find($id);
//Return view
return view('post')->with(array(
'posts' => $post
));
}
It shows just the post. In my view I have a button called Follow up. I only want to show it, if he didn't follow up the post yet. Now i thought about to edit the Model Post.php and add a static function followed() so i can just do this in the view:
#if(Post::followed())
<button>Follow up</button>
#endif
Yes. You can simply create a static function:
public static function myFunction(){
}
Just note that this function will be called in static context so you there's no $this available.
Alternatively you can create a Facade (Auth is a Facade). The process is described pretty well in the documentation about facades
As for your specific case I don't even think a static method is needed. Since you pass $post to the view you can just add a regular public method:
public function isFollowing(){
}
And then do this in your view:
#if(!$post->isFollowing())
<button>Follow up</button>
#endif
I'm trying to generate ajax specific responses from my controllers by using the Request::ajax() method, which is working just fine. The only problem is that the way I have it set up right now isn't really a nice looking solution.
My controller:
class HomeController extends BaseController {
protected $layout = 'layouts/main';
public function __construct()
{
$this->beforeFilter('auth');
}
public function getIndex()
{
$view = View::make('content.home.index');
if(Request::ajax()) return $view; //For ajax calls we only want to return the content to be placed inside our container, without the layout
$this->layout->menu = 'content.menu';
$this->layout->content = $view;
}
}
So right now, for every method I define within my controllers I need to add the code snippet that checks for an AJAX request and returns a single view if the statement returns true.
This leads to my question that is probably more PHP related than it is to the framework;
Is there a way of executing my AJAX check on every method call, without actually placing it inside the method? Or is there some other solution to keep my code DRY?
Thanks in advance!
PS: This is my first post on stackoverflow, so feel free to correct me if I made any mistakes
Create a new barebone layout named 'layouts/ajax' (or any name you like).
<?php echo $content ?>
In your Base controller, override this setupLayout() function.
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$layout = Request::ajax() ? 'layouts/ajax' : $this->layout;
$this->layout = View::make($layout);
}
}
Change your getIndex() function to this.
public function getIndex()
{
$view = View::make('content.home.index');
$this->layout->menu = 'content.menu';
$this->layout->content = $view;
}
Now non-ajax requests will be rendered using layout set in the controller, where as ajax requests will receive whatever set to $this->layout->content.
Note : Controller will neglect the layout setup in setupLayout(), if the called method returns truthy value. So this method will not work for functions like below.
public function getIndex()
{
return View::make('content.home.index');
}
You could just change the layout property, in the constructor, if it's an ajax request:
public function __construct()
{
$this->beforeFilter('auth');
if(Request::ajax()) {
$this->layout = '';
}
}
If it doesn't work try setting it to NULL instead.
Why would you return a VIEW via ajax? Are you using it to create a SPA? If so there are better ways. I'm generally against returning HTML via AJAX.
The route I'd go in your position is probably opposite of how you're doing it. Render the view no matter what, if the request is ajax, pass the extra data back and have JS render the data on the page. That's essentially how most Javascript MVC frameworks function.
Sorry if I am totally missing the point here, just going on an assumption of your end goal with the info you provided.