Blade Component to create Menus or Lists - laravel

I was looking for a while a way to create a component but to create as many slots as needed dinamically.
For example, you want to repeat a menu list style in your project multiple times, the best option is to have a component, but what if i need custom html options each element, that makes it tricky.

The best option so far i get is the following, hope someone finds it helpfull
In your view file:
#component('components.radioList',['values'=>[1,2,3,4],'name'=>'myname'])
#slot('html_0')
mi html item 0
#endslot
#slot('html_1')
mi html item 1
#endslot
#slot('html_2')
mi html item 2
#endslot
#slot('html_3')
mi html item 3
#endslot
#endcomponent
In your component blade:
<ul class="list-group">
#foreach($values as $value)
#isset(${"html_{$loop->index}"})
<li class="list-group-item custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="customCheck_{{$loop->index}}" name="{{$name}}" value="{{$value}}">
<label class="custom-control-label" for="customCheck_{{$loop->index}}">
{{ ${"html_{$loop->index}"} }}
</label>
</li>
#endisset
#endforeach
</ul>
The only thing is that the values, and the amount of slots needs to match.
If you dont need extra values for each slot, you can create as a count:
#component('components.radioList',['count'=>4,'name'=>'myname'])
And change the Foreach to a For
Cheers!

Related

Laravel Components: default content in {{ slots }}

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>

Display results of many-to-many relationships and separate results regardless of their id

How do you display results of many to many relationships and separate results regardless of their id?
#forelse ( $papers as $paper )
<article class="article-feeds mb-4">
#foreach ($paper->activityTypes as $actType )
<div class="flex ml-4" id="activitiesInformation">
<div class="flex-1">
<div>
<p class="font-bold">{{ $actType->activity_type }}</p> for {{ $paper->title }}
</div>
</div>
</div>
#endforeach
</article>
#empty
<p class="text-xl font-display">You don't have any activities listed for you</p>
</div>
#endforelse
I have a blade file that displays paper's activities, but what I need is to display each of them like in a separate activity feed, regardless of their id, as they are grouped by their paper_id key, I want it to be separated
For this example, I want to see
Recommendation for Expedita sit.
Publish Paper for Expedita sit.
to be separated from one another since they are different activity but they have the same paper id..
Furthermre,,
this is the activityTypes relationship, I'm talking about
/*success here.*/
public function activityTypes()
{
return $this->belongsToMany(ActivityType::class,
'paper_activities',
'paper_id',
'activity_type_id')
->withPivot('creator_id','display_name','body','status')
->withTimestamps() ;
}
I hope I get your idea. What you want is to get all the activity types related to a paper and each activity type on its own card.
To achieve that, from your Controller you can pass a new variable called $activity_types that you will fill like this:
//assuming you are returning it from the index method
public function index()
{
$papers = Paper::all();
$activity_types = $papers->flatMap->activityTypes;
return view('papers.index')
->with('activity_types', $activity_types);
}
also your blade should look something like this
#forelse ( $activity_types as $activity_type )
<article class="article-feeds mb-4">
<div class="flex ml-4" id="activitiesInformation">
<div class="flex-1">
<div>
<p class="font-bold">{{ $activity_type->activity_type }}</p> for {{ $activity_type->paper->title }}
</div>
</div>
</div>
</article>
#empty
<p class="text-xl font-display">You don't have any activities listed for you</p>
#endforelse
Make sure you have a relation to Paper from the ActivityType model.

Laravel: How to create link buttons on a view dynamically?

I'm making a College Administration website where a professor can log in.
I have a dashboard, where my dynamically generated button should be placed: (right now it just has dummy buttons!)
Generated by this view file, which I will have to modify soon:
<div class="container d-flex flex-column align-items-center justify-content-center">
<h1>IA DASHBOARD</h1>
<br>
<div class="grid2">
SUBCODE 1</button>
SUBCODE 2</button>
SUBCODE 3</button>
</div>
Tables in the Database:
the table iamarks contains the data (student info, and marks) that is to be displayed after /subcode/{subcode} narrows it down to records of just the students that are in the class assigned to current logged-in professor.
classroom_mappers is a table used to map a professor to a classroom with a subject. It makes sure that one classroom only has one professor for a particular subject.
the routes currently in my web.php:
route::get('/ia', 'IAController#show')->middleware('auth');
Route::get('/subcode/{subcode}', 'IAController#showTable')->middleware('auth');
...and these are the methods inside my controller:
//shows buttons to the user:
public function show(){
$subcodes = DB::table('classroom_mappers')
->select('subcode')
->where([['PID','=', auth()->user()->PID]])
->get();
return view('ia',compact('subcodes'));
}
//when user clicks a button, subcode is to be generated and a table is to be shown:
//it works, I tried it by manually typing in subcode value in URL.
public function showTable($subcode){
$sem = DB::table('classroom_mappers')
->where([['PID','=', auth()->user()->PID],
['subcode','=',$subcode]])
->pluck('semester');
$division = DB::table('classroom_mappers')
->where([['PID','=', auth()->user()->PID],
['semester','=',$sem],
['subcode','=',$subcode]])
->pluck('division');
$data = DB::table('iamarks')
->where([['semester','=',$sem],
['division','=',$division],
['subcode','=',$subcode]])
->get();
return view('subcode',compact('data'));
}
My Problem:
To be able to generate the {subcode} in the URL dynamically, I want to create buttons in the dashboard using the data $subcodes. The controller hands over the $subcodes (an array of subject codes which belong to logged in professor) which are to be made into buttons from the show() method.
The buttons should have the name {subcode} and when clicked, should append the same subject code in the URL as {subcode}.
How do I make use of $subcodes and make the buttons dynamically?
How do I make sure the buttons made for one user are not visible to another user?
I managed to find the solution, thanks to Air Petr.
Apparently, you can't nest blade syntax like {{some_stuff {{ more_stuff }} }} and it generates a wrong php code. I modified the solution by Air Petr to:
<div class="grid2">
#foreach ($subcodes as $subcode)
<a href="<?php echo e(url('/subcode/'.$subcode->subcode));?>">
<button class="btn btn-outline-primary btn-custom-outline-primary btn-custom">
<?php
echo e($subcode->subcode);
?>
</button>
</a>
#endforeach
</div>
It generates the buttons perfectly. The buttons for one user are not visible to another, since I'm using PID constraint in a query (['PID','=', auth()->user()->PID]).
Pass the passcodes array to view:
$subcodes = []; // Array retrieved from DB
return view('subcode', compact('subcodes'));
And in subcode.blade.php, loop through each subcode:
<div class="grid2">
#foreach($subcodes as $subcode)
<a href="{{ url('/subcode/' . $subcode->subcode) }}">
<button class="btn btn-outline-primary btn-custom-outline-primary btn-custom">SUBCODE {{ $subcode->subcode }}</button>
</a>
#endforeach
</div>
You can loop your codes to create buttons. Something like this (it's for "blade" template engine):
<div class="grid2">
#foreach ($subcodes as $subcode)
{{ $subcode->subcode }}</button>
#endforeach
</div>
Since you're using PID constrain in a query (['PID','=', auth()->user()->PID]), you'll get buttons for that specific PID. So there's no problem.

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