Blade Components - So sharp you'll cut yourself
Since this was written Laravel has added @component
This approach works exactly the same with @component('someview', [$someArray])@endcomponent
You can read about components here
One thing worth noting is you can use {{ $slot }}
to handle some cases that couldn't be (neatly) handled with
the below method. Progress is a wonderful thing.
@include('the', $awesome)
One of the interesting things about the blade @include()
function is that it takes an array as a second parameter.
The contents of that array are made available as variables in the included template.
@include('sometemplate', ['foo' => 'somefoo', 'bar' => 'somebar'])
includes sometemplate
but usefully makes $foo
& $bar
(containing 'somefoo' and 'somebar') available.
In my current project:
@include('Core::components.textinput', ['label' => 'Meeting Title', 'name' => 'title'])
becomes
1 2 3 4 5 6 7 8 9 10 11 | <label class="control-label col-lg-6" for="title">Meeting Title</label> <div class="col-lg-12"> <input data-bind="value: title" id="title" name="title" type="text" class="form-control" placeholder=""> </div> |
If you are wondering about the Core::
in template includes that is for another blog post (coming soon hint: you can namespace Views...)
The textinput.blade.php
template would look like this :-
<div data-bind="validationElement: {{ $property or $name }}" class="form-group"> <label class="control-label {{ $labelwidth or 'col-lg-6' }}" for="title">{{ $label or '' }}</label> <div class="{{ $inputwidth or 'col-lg-12' }}"> <input data-bind="value: {{ $property or $name }}" id="{{ $name }}" name="{{ $name }}" type="text" class="form-control" placeholder="{{ $placeholder or '' }}"> </div> </div>
As you can see the minimum required is usually a name and a label however (in this instance) you can also supply a
$property
(I use knockout on this project so that would be the observable name) and a $labelwidth
or $inputwidth
This is a big win, instead of repeating the same 10-20 lines of markup ad nauseum, we extract only the parts that change each time making global changes far easier to accommodate.
In more complex examples you can also pass in extra options :-
@include('Core::components.ckeditor', ['label' => 'Agenda','name' => 'agenda', 'options' => '{ height: 200 }'])
Since you are passing a template and an array you are not limited to what you can pass, in one instance I found it very convenient to json_encode($foo) and bind that to the component as an initial parameter.
Why would you do this?
In projects that are very form heavy or have lots of repeating 'components' the amount of duplicated markup you need to write is large.
While you can use live templates to generate much of that (I use phpstorm and they work well), refactoring becomes tedious and error prone if you need to change all of those inputs/components later, this approach abstracts and makes reusable all the common inputs that you would use throughout your project as well as greatly reducing the likelihood of introducing accidental errors in your markup.
Plus you can turn this :-
@include('Core::components.textinput', ['label' => 'Meeting Title', 'name' => 'title']) @include('Core::components.datetime_picker', ['label' => 'Meeting Date/Time', 'name' => 'meeting_datetime']) @include('Core::components.textarea', ['label' => 'Address/Location', 'name' => 'address']) @include('Core::components.ckeditor', ['label' => 'Agenda','name' => 'agenda', 'options' => '{ height: 200 }']) <hr> @include('Core::components.ckeditor', ['label' => 'Minutes', 'name' => 'minutes', 'options' => '{height: 200}']) <hr/> <!-- start meeting guest list --> @include('Core::meeting.partials.meeting_guest_list') <!-- end meeting guest list -->
Into this (in under 5 minutes)
<div data-bind="validationElement: title" class="form-group"> <label class="control-label col-lg-6" for="title">Meeting Title</label> <div class="col-lg-12"> <input data-bind="value: title" id="title" name="title" type="text" class="form-control" placeholder=""> </div> </div> <div data-bind="validationElement: meeting_datetime, validationOptions: { insertMessages: false}" class="form-group"> <label class="control-label col-lg-6" for="name">Meeting Date/Time</label> <div class="col-lg-12"> <input data-bind="datetimepicker: meeting_datetime, datepickerOptions:" autocomplete="off" id="meeting_datetime" name="meeting_datetime" type="text" class="form-control"> <p data-bind="validationMessage: meeting_datetime" class="help-block has-error"></p> </div> </div> <div data-bind="validationElement: address" class="form-group"> <label class="control-label col-lg-6" for="title">Address/Location</label> <div class="col-lg-12"> <textarea data-bind="value: address" id="address" name="address" type="text" class="form-control" rows="5" placeholder=""></textarea> </div> </div> <div data-bind="validationElement: agenda" class="form-group"> <label class="control-label col-lg-6" for="agenda">Agenda</label> <div class="col-lg-12"> <textarea data-bind='ckEditor: agenda, config: { height: 200 }' class="form-control" name="agenda" id="agenda" placeholder=""></textarea> <p data-bind="validationMessage: agenda" class="help-block has-error"></p> </div> </div> <hr> <div data-bind="validationElement: minutes" class="form-group"> <label class="control-label col-lg-6" for="minutes">Minutes</label> <div class="col-lg-12"> <textarea data-bind='ckEditor: minutes, config: {height: 200}' class="form-control" name="minutes" id="minutes" placeholder=""></textarea> <p data-bind="validationMessage: minutes" class="help-block has-error"></p> </div> </div> <hr/> <!-- start meeting guest list --> <div class="form-group"> <label class="control-label col-lg-6" for="place_by">Find Attendee</label> <div class="col-lg-12"> <div class="control-group"> <div class="control-inline"> <select data-bind=" options: availableAttendees, value: selectedAttendeeId, optionsCaption: 'Choose', optionsValue: 'id', optionsText: function(item){return item.first_name + ' ' + item.last_name;}, select2:{ width: 'resolve' }" name="attendee_id" class="form-control" id="attendee_id"></select> </div> </div> </div> <div class="col-lg-6"> <button data-bind="click: addAttendee, disable: !selectedAttendeeId()" class="btn btn-primary">Add to Meeting </button> </div> </div> <div class="table-responsive"> <table class="table table-bordered"> <thead> <tr> <th>Name</th> <th>Email</th> <th>Mobile</th> <th>Actions</th> </tr> </thead> <tbody data-bind="foreach: attending"> <tr> <td data-bind="text: formatName($data)"></td> <td data-bind="text: email"></td> <td data-bind="text: mobile_no"></td> <td> <button data-bind="click: $parent.removeAttendee" class="btn btn-sm btn-danger">Remove </button> </td> </tr> </tbody> </table> </div> <!-- end meeting guest list -->