How to structure views for admin layout in laravel blade? - laravel

The goal is to create an admin area in my laravel project. I have a question about how to structure views and controllers to create the admin layout using laravel components.
Say I have a single controller (app/Http/Controllers/CategoryController.php) at the moment:
class CategoryController extends Controller
{
public function index()
{
return view('category.index');
}
}
View (resources/views/categories/index.blade.php):
<x-app-layout>
Categories Index Page
</x-app-layout>
The base layout is defined in resources/views/layouts/app.blade.php.
Functionality that is inside CategoryController is for admins only. I want to create an admin layout that inherits from the base layout. The category views then go inside the admin layout. The admin layout has a sidebar on the left with a navigation. The navigation points to controllers like CategoryController and UserController. The admin layout will expose various admin functionality.
What I've tried:
views/admin/admin.blade.php:
<x-app-layout>
<h1>TEST</h1>
{{$slot}}
</x-app-layout>
and views/admin/categories/index.blade.php:
<x-admin>
Categories Index Page
</x-admin>
Error:
Unable to locate a class or view for component [admin].
Any ideas what I'm doing wrong here? How should one structure the views for suche use case?

Create a resources/views/layouts/admin.blade.php that looks something like this:
#extends('layouts.app')
#section('content')
<div class="navigation">
...YourNavbar...
</div>
<div class="content">
#yield('content')
</div>
#overwrite
(Edit the code inside to match how you want your navbar/content to look like)
Now your resources/views/categories/index.blade.php should use #extends('layouts.admin') instead of #extends('layouts.app')
Edit: this is assuming your app.blade.php has a #yield('content') to show content

Related

How to create a new page layout in Laravel/Livewire

This is a complete new application using Laravel 9 and Livewire
By default you have two layouts, guest and app. I want to create a new layout for all my admin things.
So, first I create a component
php artisan make:component AdminLayout
This creates \app\View\Components\AdminLayout.php, it read as:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class AdminLayout extends Component {
public function render() { return view('layouts.admin'); }
}
As any of the other two AdminLayout.php or GuestLayout.php
So now I write app\resources\views\layouts\admin.blade.php which is a stantandard HTML with the {{ $slot }} blade directive, something as...
<html>
<head>
...
<title>Admin Page</title>
...
#livewireStyles
#vite([...])
</head>
<body>
<main class="...">
{{ $slot }}
</main>
#livewireScripts
</body>
</html>
Then I create the admin routes... The Index class is in \app\Livewire\Admin\Index.php and it is correctly imported
Route::get('', Index::class)->name('home');
The Index.php reads as
class Index extends Component {
public function render() { return view('livewire.admin.index'); }
}
The \app\resources\views\livewire\admin\index.blade.php reads:
<x-admin-layout>
Hello
</x-admin-layout>
So everything seems to be right... now I have two problems that I can't spot/solve. When I load the http:://server.test/admin it should load this page. 1) It doesn't change the title in the page for Admin Page, 2) It always loads the #livewire('navigation-menu'), and looks like the followin picture:
If I add things above the {{ $slot }} or below, they are rendered. If I add another #livewire('navigation-menu') it is rendered. But I'm not able to take it out, even when I'm not telling to display it.
Any idea?
I just noticed that if I add a comment in the guest layout or the admin layout, it simply ignores it. If I add a comment in the app layout it somehow "propagates" to both the guest and admin layout. Is this the expected behaivour?

How to prevent loops in laravel global variable

I've created file GlobalVariable.php inside app\Composers
public function compose($view)
{
$categories = \App\Models\Category::all();
$view->with('globCategory', $categories);
}
then register to AppServiceProvider the code view()->composer('*', GlobalVariable::class);
I use global $globCategory for creating dynamic navbar
<ul class="nav nav-tabs border-0 flex-column flex-lg-row">
#foreach ($globCategory as $item)
<li class="nav-item">
{{$item->name}}
</li>
#endforeach
</ul>
the only problem here when I see laravel debuggar it show repetition of categories query.
here is the result
How to avoid this looping query? is there correct way?
The way you're registering your view composer (using '*' instead of a particular view name), it's going to call the compose method for every single rendered view + subview.
What you can do is instead of this:
view()->composer('*', GlobalVariable::class);
Have this:
\View::share('globCategory', \App\Models\Category::all());
This will globally share your categories (within views), and run the query only once.
View composers, as described from the laravel documentation, bind data to a view every time it is rendered. They clean our code by getting fetching data once and passing it to the view.
While it is possible to get the data in every controller method and pass it to the single view, this approach may be undesirable.
Replace the view name with an asterisk wildcard.

How to make a custom landing page in Laravel and how to apply routing?

The "/" (index page as landing page) and "pages/home" (Home page) must be different in that the landing page must not have navigation and footer.
I have tried the redirect method which led both urls showing the same page.
I have also used the standard routing method which works partially. The only problem is I do not want to have the header and footer of the home page on the landing page.
Maybe, one way to achieve this is to use two different blade layout files. Like app.blade.php and landing.blade.php. But the problem again might be of routing between the landing page and home page as both are from two different layout files.
WEB/PHP
Route::redirect('/', '/home', 301);
Route::get('/home', 'PagesController#index');
CONTROLLERS/PagesController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PagesController extends Controller
{
public function index(){
return view('pages.index');
}
public function home(){
return view('pages.home');
}
}
app.blade.php
<body>
<div class="container">
#include('inc.navbar')
#yield('content')
#include('inc.footer')
</div>
</body>
I expect a different page for landing but header and footer appears on the landing page as well.
You can get rid of the controller entirely for this
Route::view('/', 'pages.index');
Route::view('/home', 'pages.home');
Now if you go to visit the site example.com you'll see the landing page
And example.com/home shows the home page
Another solution would be to exclude the header and footer from the app layout if the request path is '/' i.e the landing page
<body>
<div class="container">
#includeWhen(request()->path() != '/', 'inc.navbar')
#yield('content')
#includeWhen(request()->path() != '/', 'inc.footer')
</div>
</body>
Hope this helps

How to render html on a given view brefore RenderBody() from the layout is triggered

I'm working on an ASP.NET MVC3 project. I'm using Twitter Bootstrap for my styling (not sure if that's important). The problem I have is that my Index.cshtml view of the Home controller has slightly different layout from the other pages (additional image navigation at the top which I don't show once the user select where he wants to go) but this is causing problems so I remove this part from the Index view to another partial view _ImageNavigation.cshtml and what I want to do is render the content of this partial view when Home/Index.cshtml is opened and I want to render it before the #RenderBody() also independently from it so I get the page the way I want it.
Right now in my _Layout.cshtml I have:
<div id="main">
<div class="container-fluid">
<div class="row">
<div class="col-md-10">#RenderBody() </div>
<div class="col-md-2">
//some static content
</div>
</div>
</div>
So I have two ideas first - adding #RenderPage("~/Views/Shared/_ImageNavigation.cshtml") right before #RenderBody() like :
<div class="row">
#RenderPage("~/Views/Shared/_ImageNavigation.cshtml")
<div class="col-md-10">#RenderBody() </div>
which produces the effect I want, but as you may guess _ImageNavigation is rendered on every page which is not what I want. I want it only on my Home/Index.cshtml so I guess the maybe some kind of check could be made to see what view is loading and render _ImageNavigation only if it's the correct view. Something like :
if (LoadingView == Home/Index.cshtml)
{
#RenderPage("~/Views/Shared/_ImageNavigation.cshtml")
}
Of course the above is just pseudo code, I don't know if it's possible and how to make such a check. And also I wonder if there is a way to do it in the page itself. I tried to put #RenderPage("~/Views/Shared/_ImageNavigation.cshtml") directly in my Home/Index.cshtml but obviously this way the page is rendered as if the code is written directly in the View and not loaded explicitly.
Maybe there's other way. This seems like pretty standard problem but I don't seem to find a proper solution.
When you have a smaller number of exceptions I like to use Sections. Here is a very simlified example:
Layout:
#if (IsSectionDefined("Nav"))
{
RenderSection("Nav")
}
else
{
<nav>default nav</nav>
}
#RenderBody()
Page with alternative nav:
#section Nav
{
<nav>My alternate nav</nav>
}
<div>This is the body for RenderBody</div>
You can read more on Defining Default Content For A Razor Layout Section - Phil Haacked.

Load a view inside another view

I've been using django for some time and I decided to start a new project but this time in Codeigniter, I used to extend the template file in my views and put content inside the {% block content %} block but it seens to be different in CodeIgniter.
In CodeIgniter I have something like:
<?php
$this->load->view('header');
$this->load->view('form_add_customer');
$this->load->view('footer');
?>
But is there a way to have an unique file with header, content and footer like this?
<html>
<head><title>Test</title></head>
<body>
<div id="header">Welcome</div>
<div id="content">
</div>
<div id="footer"> 2013 - Tectcom Telecom</div>
</body>
</html>
And put a view file with a form inside the content div?
Update your html (layout) file like this:
<div id="content"><?php $this->load->view($content) ?></div>
In your controller, call the view like this:
$view_data = array();
$view_data['content'] = 'form_add_customer';
$this->load->view('path/to/layout', $view_data);
I've used the "Most Simple Template Library for CodeIgniter" in the past with success for smaller projects. I believe it'll provide with the functionality that you require allowing you to have placeholders in a 'template' which you can update in your controller logic.

Resources