Laravel Components: default content in {{ slots }} - laravel

In old-style Laravel blade templated we used to use #section('section-name') in the following way:
{{-- h1para.blade.php --}}
<h1>
#section('heading')
Heading from Template
#endsection
</h1>
<p>
#yield('details')
</p>
And then extend that template with:
{{-- content.blade.php --}}
#extends('h1para')
#section('details')
Content from content page
#endsection
In the above, my rendered HTML output would look like the following, because the "missing" 'heading' section in the extending file means that we default back to the content in the template:
<h1>Heading from Template</h1>
<p>Content from content page</p>
But in the new components way of doing things, I do:
{{-- .../components/h1para.blade.php --}}
<h1>{{ $heading }}</h1>
<p>{{ $slot }}</p>
In case you haven't gathered, the question is: how do I set a default value for a slot's contents, such that if it isn't supplied in the extending component/template, it falls back to that default?
(I've done my searches, but haven't been able to find the same question asked before)
EDIT:
I should add that I've seen the solution (in the Laravel documentation):
<h1>{{ $heading ?? 'Default Heading Here' }}</h1>
But this seems only to be appropriate if the default value is a short easy to manage string. If the default is a long stream of HTML, then it wouldn't work for my needs.
EDIT 2:
Just to reiterate: the whole point of the question is that the default content could be a long stream of HTML. Solving the problem by passing in a string (be that formatted as HTML or not) wouldn't work for my real-world needs.

I think the solution is this:
{{-- .../component/template.blade.php --}}
<div>
#if (isset($heading))
{{ $heading }}
#else
<h1>Default Heading<span class="subhead">default subheadin and lots of other html content</span></h1>
#endif
<p>{{ $slot }}</p>
</div>
It's not super elegant, but I think it's the only solution. Anyone else have a better answer, I'd love to hear it.

If you pass data like:
<x-h1para header="<span>Header content</span>">
<div>Default slot content here</div>
</x-h1para>
You can display in your component like:
<div>
<h1>{!! $heading ?? 'Default Heading Here' !!}</h1>
{{ $slot }}
</div>

Related

Hugo definition of non-site level variables/parameters query

I am using Hugo Universal Theme. I am new to static site generators. This question is for someone who is familiar with hugo templates.
In layouts/partials/features.html we can see where $element.name and $element.name.description are rendered:
{{ if isset .Site.Params "features" }}
{{ if .Site.Params.features.enable }}
{{ if gt (len .Site.Data.features) 0 }}
<section class="bar background-white">
<div class="container">
{{ range $index, $element := sort .Site.Data.features "weight" }}
{{ if eq (mod $index 3) 0 }}
<div class="col-md-12">
<div class="row">
{{ end }}
<div class="col-md-4">
<div class="box-simple">
<div class="icon">
<i class="{{ .icon }}"></i>
</div>
<h3>{{ $element.name }}</h3>
<p>{{ $element.description | markdownify }}</p>
</div>
</div>
{{ if or (eq (mod $index 3) 2) (eq $index (sub (len $.Site.Data.features) 1 )) }}
</div>
</div>
{{ end }}
{{ end }}
</div>
</section>
{{ end }}
{{ end }}
{{ end }}
The data to be rendered in this case are defined in data/features/consulting.yaml as follows:
weight: 4
name: "Consulting"
icon: "fa fa-lightbulb-o"
description: "Fifth abundantly made Give sixth hath..."
What should I do to add new variable to the yaml file that can later then be rendered through the html file when hugo is compiling the site. I tried to simply add another parameter param1 and then insert a corresponding line in the html file as <p>{{ $element.param1 | markdownify }}</p> just below description paragraph but got error
ERROR 2018/08/23 10:42:42 Error while rendering "home" in "":
template: index.html:22:11: executing "index.html" at <partial
"features.ht...>: error calling partial: template:
partials/features.html:18:56: executing "partials/features.html" at
: wrong number of args for markdownify: want 1 got 0
Clearly it seems I have not been able to define the variable properly, but where should I do that? I can add another site variable to config.toml, but I want to learn how to make page specific variables that can be defined in yaml/frontmatter type entries. I tried reading about hugo variables but got bogged down in what is a variable and what is a shortcode. Many thanks for your help with this example.
Well, I found a working answer, but I still do not fully understand how it fits with Hugo variable system, so a better answer and or comments are highly welcome.
It appears quite simple. I had to define url variable in the yaml file:
name: "History"
position: "Hx"
url: "/blog/2018/08/23/01-history/"
and then use in the html file like this:
{{ if .url }}
<a href="{{ .url }}">
<h5>{{ .name }}</h5>
</a>
{{ else }}
<h5>{{ .name }}</h5>
{{ end }}
What it does is puts the .name in link tag, if .url is defined in .yaml. This works also if an absolute URL is given. So it appears that a page variable is referred to as .myVariable. the template authors used $element.name in another place as above, which confused me.
I also can refer to the parameter defined in the frontmatter as .Params.name
I found pointers at https://github.com/devcows/hugo-universal-theme/pull/166 and tested in adjusting the template; it works well.

Show recent blog activity on main page

Using Lektor, trying to determine how to list the most recent three blogs by published date on the main landing (root) page. I'm feeling like I should be using a macro, but I don't understand how to pass the Blogs to the page template, or if this is an example of a flowblock? I've added a section like the following to the page.ini:
[children]
model = blog-post
order_by = -pub_date, title
but can't seem to loop through them in the template (no error is thrown but doesn't iterate). Quite lost, but still consuming the documentation.
I ended up using the site.query class functionality directly in the layout template (based on the Blog quickstart).
{% for blogpost in site.query('/blog').order_by('pub_date').limit(3) %}
<div class="post col-md-4">
<div class="post-details">
<div class="post-meta d-flex justify-content-between">
<div class="date">{{ blogpost.pub_date }}</div>
</div><a href="post.html"> <!-- fix this one shortly -->
<h3 class="h4">{{ blogpost.title }}</h3></a>
<p class="text-muted">{{ blogpost.teaser }}</p>
</div>
</div>
{% endfor %}

How do we concatenate using blade template engine Laravel

I am new to Laravel and don't know if it is possible at all to concatenate a string to a blade variable.
I want to display the name of the author with the ~ sign concatenated to the author's name from the left. Here is my code.
<div class="author">~{{ $quote->author or '' }}</div>
What I want is if the author is set, it should be displayed with the ~.
How do I concatenate this?
Thanks for any help
This would be better
<div class="author">~{{ isset($quote->author) ? $quote->author : '' }}</div>
UPDATE:
<div class="author">{{ isset($quote->author) ? '~'.$quote->author : '' }}</div>
<div class="author">
#if (isset($quote->author))
~ {{ $quote->author }}
#else
#endif
</div>
https://laravel.com/docs/5.3/blade#control-structures

Too many whitespaces when outputting blade variables?

A bit of background: I'm trying to create user-editable pages in a blog-style web application. The application consists of multiple pages worth of content included into one page; dynamically generated from the back-end and laid out one under the other.
Note: I am a Laravel newbie.
The content of the pages is kept in the DB; one DB record = page title + content. I retrieve all records and send them to the template:
class ContentController extends Controller {
var $current_content;
public function __construct() {
$this->current_content = Content::all();
}
public function serveContent() {
return View::make('home')->with('pages', $this->current_content);
}
}
Over in the template I iterate through the object with the freshly retrieved data and display the contents for each page:
#foreach($pages as $page)
<section class="page">
<div class="editable-page" id="{{ $page->page }}">
{{ $page->content }}
</div>
<div hidden class="editable-page-edit-mode" id="{{ $page->page }}">
<textarea class="page-edit">
{{ $page->content }}
</textarea>
</div>
<input hidden type="button" class="edit-btn" value="Edit {{ $page->page }} section"/>
</section>
#endforeach
(The textarea is for the edit mode, it contains the same content as above; except this time it will be sent back to the DB).
The problem is that when outputting the variable contents with {{ $page->content }}, an absurd amount of whitespaces seem to be introduced.
Here's how the section looks in the browser: http://prntscr.com/bzeu4l
And here's what happens behind the scenes: http://prntscr.com/bzets2
Normally it would be completely and utterly irrelevant to me since the content is displayed properly. But when I unhide the textarea, it's clear that it receives the exact same data, with whitespaces: http://prntscr.com/bzeum8
This is a big problem because I don't want the DB values to be overwritten with spaces.
I could work around this by using regex and stripping what has more than 2 spaces when I save the data. But this feels like a workaround, not a solution.
Does anyone know an elegant solution to this? Since I'm a Laravel newbie, it's very possible that I'm missing something obvious about displaying variable values in Blade.
Thanks in advance.
PS: If anybody has any suggestions about my approach / if my approach with the textarea is flawed, I warmly welcome criticism.
Also, I am terribly sorry for the vague layout of the page, it's still in a very incipient stage. In case it's not evident, 'testhomecontent' and 'testservices' are practically $page[0]->content and $page[1]->content.
In blade when all spaces between the contents are preserved. So, to get rid of spaces change your following code
<div hidden class="editable-page-edit-mode" id="{{ $page->page }}">
<textarea class="page-edit">
{{ $page->content }}
</textarea>
</div>
To this:
<textarea class="page-edit">{{ $page->content }}</textarea>
Change
<textarea class="page-edit">
{{ $page->content }}
</textarea>
To
<textarea class="page-edit">{{ $page->content }}</textarea>

How to reuse a blade partial in a template

I would like to be able to repeat a partial a number of times within a view, with different content in each repetition.
The partial is a simple panel, with a heading, and some content. The content within each panel can vary in complexity, so I would like to be able to use the #section('content') method of passing data.
The set up I have is as follows:
panel.blade.php - The partial to be repeated.
<div class="panel">
<header>
#yield('heading')
</header>
<div class="inner">
#yield('inner')
</div>
</div>
view.blade.php - The view in which the partial is repeated
#extends('default')
#section('content')
{{-- First Panel --}}
#section('heading')
Welcome, {{ $user->name }}
#stop
#section('inner')
<p>Welcome to the site.</p>
#stop
#include('panel')
{{-- Second Panel --}}
#section('heading')
Your Friends
#stop
#section('inner')
<ul>
#foreach($user->friends as $friend)
<li>{{ $friend->name }}</li>
#endforeach
</ul>
#stop
#include('panel')
#stop
I am running into the same issue as this: http://forums.laravel.io/viewtopic.php?id=3497
The first panel is displayed as intended, but the second is simply a repeat of the first.
How can I correct this? If this is a poor method of getting this done, what would be a better way?
For Laravel 5.4, Components & Slots may be useful for you. The following solution is for Laravel 4.x, and likely <= 5.3 as well.
In my opinion, this is a silly use case for the #include syntax. The amount of HTML retyping you're saving is negligible, especially since the only potentially-complicated part of it is the inner content. Keep in mind, the more parsing that needs to be done, the more overhead your application has, also.
Also, I don't know the inner workings of the #yield & #section features, so I can't say how "proper" it is to work your includes this way. Includes typically utilize a key => value pair passed as a parameter in the include call:
#include('panel', ['heading' => 'Welcome', 'inner' => '<p>Some stuff here.</p>'])
Not the most ideal place to pack a bunch of HTML, but that's the "designed" way (as far as I know, at least).
That said...
Use the #section ... #overwrite syntax mentioned in the "Other Control Structures" part of the template docs page.
#extends('default')
#section('content')
{{-- First Panel --}}
#section('heading')
Welcome, {{ $user->name }}
#overwrite
#section('inner')
<p>Welcome to the site.</p>
#overwrite
#include('panel')
{{-- Second Panel --}}
#section('heading')
Your Friends
#overwrite
#section('inner')
<ul>
#foreach($user->friends as $friend)
<li>{{ $friend->name }}</li>
#endforeach
</ul>
#overwrite
#include('panel')
#stop

Resources