Calling Komponents
Create a beautiful komposition of chained Komponents...

Komponent types

Before we start writing Komposers, we need to learn about Komponents and how to instantiate them. There are 4 categories of komponents:

1 Fields

A Field represents the user's input in the form. It send values to the back-end, performs its specific data transformation duties and syncs attributes and/or relationships with the database.

Select::form('Tags')

Input::form('Enter your phone number')->name('phone_number')

2 Layouts

A `Layout` has child Komponents and allows us to display them in different arrangements, such as columns, tabs or flex boxes. They accept as many arguments as desired in the layout.

//Displays into equal-width columns
Columns::form(
   Input::form('First name'),
   Input::form('Last name')
   //... 
)

3 Triggers

A `Trigger` allows users to interact with the form and perform AJAX requests.

//Performs a POST request
Button::form('Save')->post('route', ['id' => 1])

//Redirects to login page
Link::form('Login')->href('login')

4 Blocks

A `Block` does nothing but display HTML. However, you will use them a lot. Most common ones are Html, Title, Badge or Alert.

Html::form('<i class="icon"><i> By clicking, you agree to sell your soul.')

Title::form('Edit your post')

Instantiation

The `::form` method

To instantiate the komponent, we call static function `::form` on the desired class. The reason behind this technique is that it allows us to continuously chain methods that enrich the komponent (see Method Chaining in next section).

//Namespaces always start with Kompo\...
use Kompo\Form;
use Kompo\Input;
use Kompo\Button;
use Kompo\Columns;

//or shorthand version:
//use Kompo\{Form, Input, Button, Columns};

class MyForm extends Form
{
   public function komponents()
   {
      return [
         //Non-layout components: the first parameter is always the label.
         Input::form('Enter your phone number'),
         Button::form('<span>Save</span>'),  //<--You may pass it HTML too.

         //Layout components: you may add many child komponents as arguments.
         Columns::form(
            Input::form('First Name'),
            Input::form('Last Name'),
            ...
         )
      ];
   }
}
This is the recommended way of instantiating Komponents since it creates NO naming conflicts and is the most IDE-friendly.

Prefixed helpers

There's another way of calling Komponents, depending on developer preference, that provides some concision advantages. You may use underscore '_' prefixed helper functions, which are constructed by prefixing an underscore to the Komponent's base class name.

//No need to import namespaces or static ::form call
_Input('Enter your phone number')
_Button('<span>Save</span>')
_Columns(
   _Input('First Name'),
   _Input('Last Name')
)

These functions offer two advantages:

  • They rid us of the need of importing namespaces on top of our classes, which can sometimes become a crowded place.
  • And remove the need to call the static `::form` method every time.
To use these functions, you have to be certain that your project has no already existing function with _ + Komponent name.

If your app already has such a named function, the kompo function will simply not be declared and will not be available.

Rest assured: A fresh installation of Laravel does not create any conflicts with these functions, neither do PHP internal functions.

Komponents API

There's a whole library of chainable Komponent methods that help playing with their HTML, create Front-end interactions or perform AJAX requests.

Each komponent has it's own set of well-documented methods that can be specific to it, to it's type (field, layout, ...) or shared across all komponents.

Check out all the available methods by Komponent

Method Chaining

The core principle of Kompo is to be able to declare komponents and all their required configuration in a single PHP expression. Here are some examples of how method chaining allows us to enrich komponents' properties and features:


Input::form('Your full name')
   ->name('full_name') //Setting the name attribute, 
   ->icon('icon-plus') //adding an icon,
   ->required()        //and making the field required.

Country::form('Pick a country')
   ->name('country')
   ->`default`('CA')   //Setting a default country.

//Image upload with automatic thumbnail creation.
Image::form('Profile pic')
   ->withThumbnail()
   ->extraAttributes(['collection' => 'profile'])

//Columns example with different grid widths...
Columns::form(

   Date::form('Contact date')->col('col-4'),
   Textarea::form('Subject')->col('col-8')

)->alignStart() //... and top alignment.

Fields in depth

Label & Name

When a Field is instantiated, two things happen:
1) it is assigned a default label using the double underscore `__` helper function in Laravel;
2) set the name attribute of the field to the `snake_case($label)`.

// This will have a label of __('First Name')
// and a name attribute of 'first_name'
Input::form('First Name')

If however, the desired name attribute doesn't correspond to the snake cased version of the label, you may explicitely set it by chaining the `name()` method to the component:

Input::form('Enter your phone number')->name('phone_number')

Assigning a value

To set a certain value to a field, for example, when dealing with hidden fields, you may chain the `->value($value)` method to the komponent:

Hidden::form('category_id')->value(1)

There are many other useful methods for fields including toggling and backend requests. To see the full list, check out the form components api.

Attributes & Relationships

In Eloquent Forms, the names of the fields drive the DB saving process. The field names should either match an attribute of the Model (DB column name) or a relationship method. This allows us to automatically:

  • On display - assign the model's attributes and load the relationships into the fields values.
  • On submit - save the fields attributes to the model's DB table and sync/associate/save the relationships according to the methods defined in your Eloquent Model.

For example, our Eloquent Model has a title attribute and a tags relationship:

class Post extends Model
{
   //'title' is a table column

   public function tags() 
   {
      return $this->belongsToMany(Tag::class);
   }
}

Our Form can now automatically save the title and sync the belongsToMany tags. Note that there is NO need to specify the type of relationship in the Form. Kompo infers them from your Model.

class EloquentForm extends Form
{
   public static $model = Post::class;
   
   public function komponents()
   {
      return [
         Input::form('Title'), //<-- this field has a name of title, like the attribute.

         MultiSelect::form('Enter one or more tags')
            ->name('tags')  //<-- this name matches the belongsToMany tags() method in the model.
            ->optionsFrom('id','name'),

         SubmitButton::form('Save')
      ];
   }
}

Nested relationships

You may also modify a Model's child attribute directly from its' Form (as long as it is a One-to-One relationship). To do so, you may use a nested `{relationship}.{attribute}` syntax (separated by a dot).

Let's say a User Model has one Profile Model. In the User Form, you may directly modify one of the Profile's attributes.

class UserForm extends Form
{
   public static $model = User::class;
   
   public function komponents()
   {
      return [
         Textarea::form('profile.about_me') // 'profile()' is the HasOne relationships
                                            // 'about_me' is a Profile attribute
         SubmitButton::form('Save')
      ];
   }
}

Interactions & AJAX

To perform an interaction, we need to assign two things: `1) an event trigger` and `2) an Action`. Here is a list of currently available event triggers:

  • `onClick`
  • `onChange`
  • `onFocus`
  • `onBlur`
  • `onInput` (debounced 500ms by default)
  • `onLoad`
  • `onEmit` (when a Vue event is emitted)
  • `onSuccess` (when an AJAX request returns a success response)
  • `onError` (when an AJAX request returns an error response)

Writing interactions

Let's say we have a Select and we want to submit the Form when it's value changes. We may write this multiple ways:

//The short way: Higher Order Interactions
Select::form('Pick an option')->onChange->submit()

//Even shorter way: Default interactions
Select::form('Pick an option')->submit() //because Select's default interaction is onChange

//A longer way
Select::form('Pick an option')->onChange(function($e){
   $e->submit();
   //Allows us to add more instructions
})

//The longest way - multiple event trigger
Select::form('Pick an option')->on(['change', 'blur'], function($e){
   $e->submit();
   //Allows us to add more instructions
})

Default interactions

Each Komponent (and Komposer - see later) have a default trigger which is the most used way the Komponent interacts. For example, the default interaction of a :

  • A Field is `onChange` by default.
  • A Trigger (Button, Link) is `onClick` by default.
  • A Panel is `onLoad` by default.
  • An Input is `onInput` by default (debounced by 500ms).
  • A Komposer (Form, Query) is `onSuccess` by default.

Front-End Actions

Actions can either be pure Front-end interactions, for example: hiding, toggling...

Button::form('Toggle')
   ->toggleId('some-id') //toggles #some-id when clicked

Rows::form()->id('some-id') //the id of the toggled element

If you chain them, they will execute synchronously.

AJAX Actions & Chaining

Or they can be AJAX interactions (asynchronous requests to the backend). For example, let's say we want to make a POST request to the backend and handle the response.

//The short way: Default + Higher Order Interactions
Select::form('Pick an option')
   ->post('option-selected')
   ->onSuccess->inModal()
   ->onError->alert('Something went wrong')

//The long way - full control
Select::form('Pick an option')->on('change', function($e){
   $e->post('option-selected') //makes a POST request to url('option-selected')
     ->on('success', function($e){
        $e->inModal(); //displays the response in a Modal
     })
     ->on('error', function($e){
        $e->alert('Something went wrong'); //opens an alert
     });
})

When you chain them, they execute asynchronously.

The full list of actions each Komponent can do is available in the Komponents API and there is an post that explains AJAX interactions in depth in the Examples section.