Skip to content

Blade-Components

The User Interface of the application is implemented server side with Blade. CoreUI is used to provide and appealing design. In some places Vue is used for dynamic content. Prefabricated Blade-Components are used for a consistent and productive design of the input and output views of data records.

Concepts

These components form several layers.

  • The Base-Layer integrates simple HTML elements with Laravel.
    • id-attribute based on the name-attribute
    • automatic loading of values based on the name-attribute
    • Translate the name from Dot-Notation (x.y) to Array-Notation (x[y])
    • Load old entries in the event of validation errors
    • in config/view.php configurable CSS-Class according to the validation status
    • in config/view.php configurable CSS-Class for empty fields
    • Presentation for both humans and machines
  • The CoreUI layer builds on this and offers layout and styling.

In addition to the special input and output components in each layer, the base layer also offers a version of generic HTML elements that can be accessed if required. In general, every automatic behavior is overwritten by explicit information.

The available components can be found in the subdirectory src/Support/View/Components. You can also interact with them in the web browser at http://localhost/widget after starting the development environment with the command bin/ptr dev.

Examples

Some application examples can be found below

Empty input form with Request as implicit data source

An input form is presented to a user until it is accepted by the server-side validation. In the event of resubmission due to validation errors, the errors should be displayed and the form should be pre-filled with the user’s previous entries.

Source Code

For example, this can be implemented with the components as follows.

<?php
class Controller extends \Illuminate\Routing\Controller
{
    // Used for demonstration purposes only, to render Template-Strings.
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
    public function create($request)
    {
        return (string) $this->blade(
            <<< 'blade'
                <x-base::form action="/store" method="post">
                    <label>
                        Gender
                        <x-base::select
                            name="person.gender"
                            :options="['diverse', 'male', 'female']"
                            placeholder=""
                        />
                        @error('person.gender')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        First Name
                        <x-base::in.inline-text name="person.first_name"/>
                        @error('person.first_name')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        Last Name
                        <x-base::in.inline-text name="person.last_name"/>
                        @error('person.last_name')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        Birthdate
                        <x-base::in.date name="person.birthdate"/>
                        @error('person.birthdate')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                </x-base::form>
            blade,
        );
    }
    public function store($request)
    {
        $request->validate([
            'person.gender' => ['required', 'in:0,1,2'],
            'person.first_name' => ['required', 'string'],
            'person.last_name' => ['required', 'string'],
            'person.birthdate' => ['required', 'date_format:Y-m-d'],
        ]);
    }
}
return (new Controller())->create(request());

Note that the previous entries are filled in automatically and without additional program logic.

Output

The above PHP Source Code results in the following HTML-Output.

<form enctype="multipart/form-data" method="post" action="/store">
    <input type="hidden" name="_token" value=""/>
    <label>
        Gender
        <select id="person.gender" name="person[gender]">
            <option value=""></option>
            <option value="0">diverse</option>
            <option value="1">male</option>
            <option value="2">female</option>
        </select>
    </label>
    <br/>
    <label>
        First Name
        <input id="person.first_name" name="person[first_name]" type="text"/>
    </label>
    <br/>
    <label>
        Last Name
        <input id="person.last_name" name="person[last_name]" type="text"/>
    </label>
    <br/>
    <label>
        Birthdate
        <input id="person.birthdate" name="person[birthdate]" type="date"/>
    </label>
</form>

Notice the following:

  • Field for CSRF-Protection
  • id-attributes are derived from the name-attribute
  • name-attribute is converted to Array-Format
  • option-elements are created

Note

Elements with an id-attribute which contains dots cannot be compared with an CSS-ID-Selector (for example input#person.birthdate). Everything after the dot would not be interpreted as part of the id-attribute but as part of the class-attribute. Instead, they have to be compared with a CSS-Attribute-Selector (for example input[id="person.birthdate"]).

Pre-filled input form with Request as implicit data source

As with an empty input form, the Request can also serve as a data source for a pre-filled one. The previous entries made by the user are in the Session under the key _old_input. These can also be queried via Request::old(). The session-key _old_input can either be set directly with Session::put() or the input of the Request can be changed and then loaded into the session.

Source Code

In the following, the input of the Requests is pre-assigned with Request::replace() and then loaded into the session with Request::flash().

<?php
class Controller extends \Illuminate\Routing\Controller
{
    // Used for demonstration purposes only, to render Template-Strings.
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
    public function edit($request)
    {
        if ($request->old() === []) {
            $request->replace(['person' => [
                'gender' => '0',
                'first_name' => 'Dana',
                'last_name' => 'Debug',
                'birthdate' => '2001-02-03',
            ]])->flash();
        }
        return (string) $this->blade(
            <<< 'blade'
                <x-base::form action="/update" method="put">
                    <label>
                        Gender
                        <x-base::select
                            name="person.gender"
                            :options="['diverse', 'male', 'female']"
                            placeholder=""
                        />
                        @error('person.gender')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        First Name
                        <x-base::in.inline-text name="person.first_name"/>
                        @error('person.first_name')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        Last Name
                        <x-base::in.inline-text name="person.last_name"/>
                        @error('person.last_name')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        Birthdate
                        <x-base::in.date name="person.birthdate"/>
                        @error('person.birthdate')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                </x-base::form>
            blade,
        );
    }
    public function update($request)
    {
        $request->validate([
            'person.gender' => ['required', 'in:0,1,2'],
            'person.first_name' => ['required', 'string'],
            'person.last_name' => ['required', 'string'],
            'person.birthdate' => ['required', 'date_format:Y-m-d'],
        ]);
    }
}
return (new Controller())->edit(request());

Note that the pre-assignment only takes place if the user has not yet made any entries, as these would otherwise be overwritten.

Output

The above PHP Source Code results in the following HTML-Output.

<form enctype="multipart/form-data" method="post" action="/update">
    <input type="hidden" name="_method" value="put"/>
    <input type="hidden" name="_token" value=""/>
    <label>
        Gender
        <select id="person.gender" name="person[gender]">
            <option value=""></option>
            <option selected="" value="0">diverse</option>
            <option value="1">male</option>
            <option value="2">female</option>
        </select>
    </label>
    <br/>
    <label>
        First Name
        <input id="person.first_name" name="person[first_name]" type="text" value="Dana"/>
    </label>
    <br/>
    <label>
        Last Name
        <input id="person.last_name" name="person[last_name]" type="text" value="Debug"/>
    </label>
    <br/>
    <label>
        Birthdate
        <input id="person.birthdate" name="person[birthdate]" type="date" value="2001-02-03"/>
    </label>
</form>

Notice the following:

  • Field for Method-Spoofing
  • selected-attribute of the corresponding option-element is set
  • value-attributes of the input-elements are set

Pre-filled input form with an explicit data source

Instead of using the Request as an implicit data source, the data to be used can also be specified explicitly.

Source Code

For example, this can be implemented with the components as follows.

<?php
class Controller extends \Illuminate\Routing\Controller
{
    // Used for demonstration purposes only, to render Template-Strings.
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
    public function edit($request)
    {
        return (string) $this->blade(
            <<< 'blade'
                <x-base::form action="/update" method="put">
                    <label>
                        Gender
                        <x-base::select
                            :data="$data"
                            name="person.gender"
                            :options="['diverse', 'male', 'female']"
                            placeholder=""
                        />
                        @error('person.gender')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        First Name
                        <x-base::in.inline-text :data="$data" name="person.first_name"/>
                        @error('person.first_name')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        Last Name
                        <x-base::in.inline-text :data="$data" name="person.last_name"/>
                        @error('person.last_name')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                    <br/>
                    <label>
                        Birthdate
                        <x-base::in.date :data="$data" name="person.birthdate"/>
                        @error('person.birthdate')
                            <x-base::out.inline-text value="{{ $message }}"/>
                        @enderror
                    </label>
                </x-base::form>
            blade,
            [
                'data' => $request->old() ?: ['person' => [
                    'gender' => '0',
                    'first_name' => 'Dana',
                    'last_name' => 'Debug',
                    'birthdate' => '2001-02-03',
                ]],
            ],
        );
    }
    public function update($request)
    {
        $request->validate([
            'person.gender' => ['required', 'in:0,1,2'],
            'person.first_name' => ['required', 'string'],
            'person.last_name' => ['required', 'string'],
            'person.birthdate' => ['required', 'date_format:Y-m-d'],
        ]);
    }
}
return (new Controller())->edit(request());

Note that the data source must be passed to each element individually.

Output

The above PHP Source Code results in the following HTML-Output.

<form enctype="multipart/form-data" method="post" action="/update">
    <input type="hidden" name="_method" value="put"/>
    <input type="hidden" name="_token" value=""/>
    <label>
        Gender
        <select id="person.gender" name="person[gender]">
            <option value=""></option>
            <option selected="" value="0">diverse</option>
            <option value="1">male</option>
            <option value="2">female</option>
        </select>
    </label>
    <br/>
    <label>
        First Name
        <input id="person.first_name" name="person[first_name]" type="text" value="Dana"/>
    </label>
    <br/>
    <label>
        Last Name
        <input id="person.last_name" name="person[last_name]" type="text" value="Debug"/>
    </label>
    <br/>
    <label>
        Birthdate
        <input id="person.birthdate" name="person[birthdate]" type="date" value="2001-02-03"/>
    </label>
</form>

Display

The prefabricated blade components can not only make it easier to enter values, but also to output them.

Source Code

For example, this can be implemented as follows.

<?php
class Controller extends \Illuminate\Routing\Controller
{
    // Used for demonstration purposes only, to render Template-Strings.
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
    public function show($request)
    {
        return (string) $this->blade(
            <<< 'blade'
                <div>
                    <x-base::out.url value="/edit">Edit</x-base::out.url>
                    <br/>
                    <strong>Gender</strong>
                    <x-base::out.inline-text name="person.gender" value="diverse"/>
                    <br/>
                    <strong>First Name</strong>
                    <x-base::out.inline-text name="person.first_name" value="Dana"/>
                    <br/>
                    <strong>Last Name</strong>
                    <x-base::out.inline-text name="person.last_name" value="Debug"/>
                    <br/>
                    <strong>Birthdate</strong>
                    <x-base::out.date locale="de" name="person.birthdate" :value="carbon('2001-02-03')"/>
                </div>
            blade,
        );
    }
}
return (new Controller())->show(request());

Note that the value of the date field is an object and not a simple string.

Output

The above PHP Source Code results in the following HTML-Output.

<div>
    <a href="/edit">Edit</a>
    <br/>
    <strong>Gender</strong>
    <span id="person.gender">diverse</span>
    <br/>
    <strong>First Name</strong>
    <span id="person.first_name">Dana</span>
    <br/>
    <strong>Last Name</strong>
    <span id="person.last_name">Debug</span>
    <br/>
    <strong>Birthdate</strong>
    <time id="person.birthdate" datetime="2001-02-03">03.02.2001</time>
</div>

Note that the date of birth is both machine-readable and localized for humans.

Display with Request as implicit data source

As with the input, the Request can also serve as a data source for the output. The input data can be queried via Request::input(). They can be set using Request::replace().

Source Code

For example, this can be implemented as follows.

<?php
class Controller extends \Illuminate\Routing\Controller
{
    // Used for demonstration purposes only, to render Template-Strings.
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
    public function show($request)
    {
        $request->replace(['person' => [
            'gender' => 'diverse',
            'first_name' => 'Dana',
            'last_name' => 'Debug',
            'birthdate' => carbon('2001-02-03'),
        ]]);
        return (string) $this->blade(
            <<< 'blade'
                <div>
                    <x-base::out.url value="/edit">Edit</x-base::out.url>
                    <br/>
                    <strong>Gender</strong>
                    <x-base::out.inline-text name="person.gender"/>
                    <br/>
                    <strong>First Name</strong>
                    <x-base::out.inline-text name="person.first_name"/>
                    <br/>
                    <strong>Last Name</strong>
                    <x-base::out.inline-text name="person.last_name"/>
                    <br/>
                    <strong>Birthdate</strong>
                    <x-base::out.date locale="de" name="person.birthdate"/>
                </div>
            blade,
        );
    }
}
return (new Controller())->show(request());

Output

The above PHP Source Code results in the following HTML-Output.

<div>
    <a href="/edit">Edit</a>
    <br/>
    <strong>Gender</strong>
    <span id="person.gender">diverse</span>
    <br/>
    <strong>First Name</strong>
    <span id="person.first_name">Dana</span>
    <br/>
    <strong>Last Name</strong>
    <span id="person.last_name">Debug</span>
    <br/>
    <strong>Birthdate</strong>
    <time id="person.birthdate" datetime="2001-02-03">03.02.2001</time>
</div>

Display with explicit data source

Instead of using the Request as an implicit data source, the data to be used can also be specified explicitly.

Source Code

For example, this can be implemented as follows.

<?php
class Controller extends \Illuminate\Routing\Controller
{
    // Used for demonstration purposes only, to render Template-Strings.
    use \Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
    public function show($request)
    {
        return (string) $this->blade(
            <<< 'blade'
                <div>
                    <x-base::out.url value="/edit">Edit</x-base::out.url>
                    <br/>
                    <strong>Gender</strong>
                    <x-base::out.inline-text :data="$data" name="person.gender"/>
                    <br/>
                    <strong>First Name</strong>
                    <x-base::out.inline-text :data="$data" name="person.first_name"/>
                    <br/>
                    <strong>Last Name</strong>
                    <x-base::out.inline-text :data="$data" name="person.last_name"/>
                    <br/>
                    <strong>Birthdate</strong>
                    <x-base::out.date :data="$data" locale="de" name="person.birthdate"/>
                </div>
            blade,
            [
                'data' => ['person' => [
                    'gender' => 'diverse',
                    'first_name' => 'Dana',
                    'last_name' => 'Debug',
                    'birthdate' => carbon('2001-02-03'),
                ]],
            ],
        );
    }
}
return (new Controller())->show(request());

Note that the data source must be passed to each element individually.

Output

The above PHP Source Code results in the following HTML-Output.

<div>
    <a href="/edit">Edit</a>
    <br/>
    <strong>Gender</strong>
    <span id="person.gender">diverse</span>
    <br/>
    <strong>First Name</strong>
    <span id="person.first_name">Dana</span>
    <br/>
    <strong>Last Name</strong>
    <span id="person.last_name">Debug</span>
    <br/>
    <strong>Birthdate</strong>
    <time id="person.birthdate" datetime="2001-02-03">03.02.2001</time>
</div>

Rendering IoElements

The following section explains the architecture of the standard in- and output elements of the admin panel. As an example we would like to render tags consisting of a name and a color:

$myTag = [
    'name' => "My first pill",
    'color' => "#ff0000"
];

The standard in- and output elements of the admin frontend inherit Support\View\Components\Base\IoElement which defines their basic properties and behaviour. Based on this the constructor of every IoElement looks like this:

    public function __construct(
        array|null|object $data = null,
        ?iterable $dataset = null,
        ?string $name = null,
        mixed $value = null,
    )

At it’s core all in and -output elements have a value. The value represents the data which we would like to render. This can be a single tag (if our component renders a single tag) or an iterable of tags (if our component renders a list of tags).

Output Elements

Rendering an Output Element by setting a dedicated value

Suppose we would like to render a single tag which is displayed using a pill by setting a dedicated value. Then this can be done like this:

    new Pill(null, null, null, [
        'name' => 'My first pill',
        'color' => '#ff0000'
    ]);

Rendering a list of tags would be done like this:

    new PillList(null, null, null, $myTags);

Rendering an output element by setting data and name

Let’s take a look at another option. Suppose we have a person which has tags and we would like to render it’s tags. Then we could provide the person to the element using the $data argument and provide the name of the persons attribute which contains the tags using the $name argument:

    new PillList($myPerson, null, 'tags', null);

The IoElement now automatically sets the persons tags-attribute as the elements value. When rendering an output component just setting the value is sufficient most of the times.

The dataset argument

The $dataset argument can be used to provide a set of key/value-pairs which will be assigned as data-*-attributes on the HTML-element. Suppose we would like to assign an additional, non-standard data-index-number-attribute to our html element then this could be done like this:

    new Pill(
        null,
        [
            'index-number' => 2342321828
        ],
        null, 
        [
            'name' => 'My first pill',
            'color' => '#ff0000'
        ]
    );

The output would be like this:

    <span style="background-color: #ff0000;" data-index-number="2342321828">My first pill</span>

Providing configuration options

Suppose we would like to render a list of pills but only show a maximum of 3 pills. In addition we would like to make the maximum configurable. This can be done by just adding another argument $maxPills to our constructor (see Laravel documentation):

class PillList
    public function __construct(
        array|null|object $data = null,
        ?iterable $dataset = null,
        ?string $name = null,
        mixed $value = null,
        int $maxPills = null,
    )

Input Elements

TODO