Is there an existing way in Blade to mimic Jinja2's block construct?
I understand it is possible to use #yield('content', '<h1>Default content</h1>'), but this is clunky and ugly if you want the default content to be much bigger than a small string.
Yes, you can use #section/#show instead of #yield:
#section('content')
default content
#show
and override as usual:
#section('content')
override
#stop
Related
Laravel 5.4 Blade introduced the concept of components & slots - but I can't see what they add over the traditional #include. As I understand, with component/slots, you do:
In template component-tpl.blade.php:
<div class='container'>
<h1>{{$slot1}}</h1>
<h2>{{$slot2}}</h2>
</div>
Using slots in page template, you do:
#component('component-tpl')
#slot('slot1')
The content of Slot 1
#endslot
#slot('slot2')
The content of Slot 2
#endslot
#endcomponent
What functionality does that provide over the older:
#include('component-tpl',['slot1'=>'The content of Slot 1',
'slot2'=>"The content of Slot 2"])
using the exact same 'component-tpl.blade.php' Blade template?
What am I missing? Thanks for any insights.
Chris
As stated, there's no functional difference I was incorrect - see benjaminhull's answer for details on variable scoping and passing blade syntax code. The following still holds for basic usage, though.
If a slot could contain HTML, then using a component will give a cleaner syntax in your blade files.
#component('test')
<strong>This text has html</strong>
#endcomponent
versus
#include('test', ['slot' => '<strong>This text has HTML</strong>'])
Equally, if a component has no slots, then an include may be preferred:
#include('test')
versus
#component('test')
#endcomponent
There are two key differences.
1. Variable scope
As described in #DavidHyogo's answer, a component only sees variables explicitly passed to it. So you have to give it all variables like so...
#component('my-component', ['foo' => 'bar', 'etc' => 'etc'])
Whereas an include will adopt all variables from the global/current scope by default - unless you define an explicit set of variables to pass it, which then becomes local scope again.
{{-- This include will see all variables from the global/current scope --}}
#include('my-component')
{{-- This include will only see the variables explicitly passed in --}}
#include('my-component', ['foo' => 'bar', 'etc' => 'etc'])
2. Component's {{ $slot }} vs include's {{ $var }}
When using a {{ $slot }} in a component, you can give it blade syntax code e.g...
{{-- alert.blade.php --}}
<div class="alert">{{ $slot }}</div>
#component('alert')
<div>Hello {{ $name }} #include('welcome-message')</div>
#endcomponent
Note how the slot will receive html AND blade syntax code and just deal with it.
This is not possible with includes because you can only pass variables into includes...
{{-- alert.blade.php --}}
<div class="alert">{{ $slot }}</div>
#include('alert', ['slot' => "I CAN'T PASS IN BLADE SYNTAX HERE!"])
It could be done in a more hacky way by grabbing a fresh view() helper and passing it some variables to compile the output we want to pass into the slot, but this is what components are for.
I think I've tracked down another crucial difference. For instance, from the documentation for 5.4:
Blade's #include directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view:
As far as I can tell, components have a different scope from a containing view and so the variables available to the parent view are not available within the component. You need to pass a variable to a component like this:
#component('alert', ['foo' => 'bar'])
#endcomponent
This discussion is related to this problem:
Use variables inside the Markdown Mailables
As the documentation says:
Components and slots provide similar benefits to sections and
layouts; however, some may find the mental model of components and
slots easier to understand.
For me most important thing is component needs a class. So when I need just a simplest reusable part of html (blade) there is no need to create blade file + php file, just simple #include with subview is enough ;)
I need to pass a variable to an included Blade file. I have attempted this two-ways; however, neither have been successful.
Pass a variable, title, to the included file:
#section('left')
#include('modal', ['title' => 'Hello'])
#stop
Use #yield and set the section:
#section('left')
#include('modal')
#section('title')
Hello
#stop
#stop
I am using Laravel 4.2. I am unaware if what I am trying to do is possible, but I imagine it is.
According to the documentation, the include-way should be the way to do it:
Including Sub-Views
#include('view.name')
You may also pass an array of data to the included view:
#include('view.name', array('some'=>'data'))
My hunch is that $title is conflicting with another variable in your nested templates. Just for troubleshooting, try temporarily calling it something else.
pass an array of data to the included view
#include('view.name', array('some'=>'data'))
then use this on view/name folder
{{ $some }}
I just get to know that we can write section with this:
#section('sectionname','data')
What is the difference between the above method with the below method?
#section('sectionname')
'data'
#stop
Is it a proper way to write with the first method? Although, during my testing, it does not need #stop for the first method. But is it the best practice that we need to put it?
Thank you.
For writing small string in the sections you can use
#section('sectionname','data')
and to write multiple lines of html you will use
#section('sectionname')
'data'
#stop
I am using Laravel 4 and blade templates, and running into an issue when I try and extend a template.
In my layout I have
#yield('content')
and in my page I have
#section('content')
Welcome {{ $name }}
#stop
which works fine, I've created another page very similar to my first, and just want to change override the admin content section. The other sections in the template are fine.
so I extend my page, and do
#section('content')
Hello!
#stop
I get an undefined notice with the $name variable.
I tried
#section('content')
Hello!
#overwrite
and same deal, I get the notice error.
I checked my controller and it IS using the correct template. I am not calling #parent so I don't understand, how can I overwrite a section in a template with out notice errors?
Blade layouts work their way up the tree to the route or master view before rendering any of the children. Thus, nested views that extend others must always have their parent rendered before they are. As a result, parent views that contain sections will always be rendered prior to the child.
To overcome the problem you are experiencing it is a good idea to only ever nest pages that don't overwrite parents sections that contain variables, as the parents content will always be rendered before the child.
As the above ideal can't always be adhered to or a parents section content is still required, a good alternative method would be to use view composers. View composers give you an opportunity to declare variables for any specific view whenever they are rendered.
View::composer(array('pages.admin'), function($view)
{
$view->with('name', Auth::user()->username);
});
Another alternative would be to use a view creator. Creators are fired the moment a view is instantiated rather than rendered. This method allows you to overwrite the variable should you so wish prior to the view being rendered.
View::creator(array('pages.admin'), function($view)
{
$view->with('name', Auth::user()->username);
});
You can read up more about these methods in the documentation here. (Or here for the Laravel 5 documentation.)
I can't guarantee support for Laravel 4 but for those looking for a solution that works in Laravel 5.5 (and probably a fair bit further back – hard to check) is to define the variables you need when #extending.
E.g. in the example in the question:
#extend('my.parent.view', ['name' => ''])
This approach can be especially useful if the data needed is available to the child-view, but under a different name.
E.g. if a parent-view needed a $parent variable, but the child view only has a $child variable which has a property referencing the parent, you might do:
#extend('my.parent.view', ['parent' => $child->parent])
I am not sure if this is a bug or intentional, but it seems like Laravel renders the variables before interpreting blade instructions.
The workaround would be this:
views/layouts/testlayout.blade.php:
<html>
<body>
#yield('sidebar', 'This is the master sidebar. {{ $name }}' )
<div class="container">
#yield('content')
</div>
</body>
</html>
actual view: views/test.blade.php
#extends('layouts.testlayout')
#section('sidebar')
No variable in the child
#stop
#section('content')
This is a child content
#stop
This prevents the variable $name to get rendered if there is a section with that name in the actual view. It seems like this is the approach if the content in the layout file contains a variable
In your page try this :
#section('content')
#hasSection('content')
#yield('content')
#else
Welcome {{ $name }}
#endif
#stop
And read this post for more inspiration https://laracasts.com/discuss/channels/laravel/laravel-blade-layouts-inheritence-problem
I found an easy solution for this problem :)
Just add the # symbol before the variable/method you call in the parent view. If there is an error (notice/warning) PHP will ignore that and continue the execution.
It's not perfect, but save us to code view/composers.
Hope this help!
I was wondering if it is possible to nest templates inside of templates in Laravel 4. I would like to have a master wrapper template that would include things like the doctype, header and footer and then be able to load in templates into the master templates body section.
This would give me the flexibility of having a nested template for my application pages without having to duplicate code while giving me the ability to use the master template for non-application pages.
Can someone please provide an example as to how this would be done using blades templating engine? Is it possible to pass in a value from the router and then have that value be pushed down to your nested template?
EDIT:
Here is my code for index.blade.php
#extends('layouts.master')
#section('title')
Some page
#endsection
#section('content')
#include('layouts.app')
#endsection
You can try this for multiple level nesting
//index.blade.php
#extends('layouts.master')
#section('title')
#parent
:: new title
#stop
#section('content')
<p>some static contents here</p>
#stop
//app.blade.php
#section('content')
#parent
<p>Add here what ever you want to add</p>
#stop
now either from your Route or Controller you can nest the index and app, ex-
return View::make('index')->nest('content','layouts.app');
And in case you want to pass data to child view you can do this by passing the data as third parameter to nest()
return View::make('index')->nest('content','layouts.app',$data);