On building the ideal Livewire tables package

I think Livewire is a perfect fit for replacing Vue/React tables that offer search/sort/pagination/etc. Almost all of my apps have the need for tables like this and before now, Vue/React was perpetually a necessity for that reason.

Lately I have been working on Livewire Tables, which is my initial attempt at building a Livewire package for “datatables”.

Quick preview someone made with Bulma:

The vision for this is a table mostly configured by options available in the Livewire component, with minimal setup needed on the view side of things. It is style-agnostic, with CSS classes passed in via config (global) or in the PHP component (per-table). The PHP component is created with a livewire-tables:make command, and once configured, the user runs a livewire-tables:scaffold command which parses the component and builds out a view file containing the table.

May sound complex, actually super easy-to-use in practice.

I would consider what I released so far as Proof-of-Concept. At this point, I think there are a couple different directions we could take in the vein of expanding functionality (some of which are represented in open Github issues).

I know that there are far more proficient Livewire experts out there than me, and I want to make this a package that both shows off how effortless and easy Livewire can be, as well as providing everything a user could need in a table.

I’m open to any discussion on the package or idea, but I want to bring up one core focus that has been bugging me and honestly stopping me from making progress as I think about it: right now the user is encouraged to run the scaffold command to re-build the table view as options are changed in the PHP component.

The obvious pitfall of this is wiping away any customization the user has entered into the view file.

In my mind, it just doesn’t seem possible to offer all the configuration options a user might need to build their intended view. Some people will want a dropdown filter on the top right of the table. Some will want the search bar there. Surely custom buttons will be needed. With that in mind, it seems to make sense building the initial view file and then allowing the user to customize/expand as needed. This then becomes a problem when options are changed in the config/PHP class and the view needs to be scaffolded again.

I hope that all makes sense. I’m stuck between two paradigms, and neither seem to offer a full solution. I’d be really happy to hear any thoughts!

1 Like

This is great! Thanks for sharing Cory.

I think you are right that people are going to want to customize their blade template / HTML. I also think that most people are going to customize their table they like it, and then use the same type of markup throughout the rest of their app and just change based on what data they are sending through.

This type of functionality is why I PRed my Class and View Stubs functionality. I think there may be a natural tie in with what you’re building and that functionality. You can allow the user to create their standard view template with whatever customization they like and then every new table they create will start from that same template. Or the user could create multiple templates and pass in a stub parameter when they run your scaffold command. This way they get their custom markup mixed in with the scaffolding from the Component.

1 Like

Thanks Kevin!

Woah - you may be right and these custom stubs could be the best way to handle this. Regardless I can see that it’s a great idea for Livewire.

If your PR is pulled in, could the stub command be leveraged within my package so that rather than creating “base Livewire” stubs they create “base Livewire Tables” stubs? I.e. using a livewire-tables:stub command that extends your functionality.

One consideration is that the same PHP stub may want to be used with many different view stubs - it could be the case that the PHP stub contains all the functionality needed for the table, but the user wants to have several different views that work with it with miscellaneous changes like where the search bar is or an added button.

Additionally, I’m trying to weigh the value between obfuscating some of the more verbose HTML parts, vs leaving everything in one large view file. To give one example, here’s a WIP table header cell that detects sort and populates CSS classes.

<th
    @if (array_key_exists('sortable', $field) && $field['sortable'])
    @if (!is_null($sortField) && $sortField == $field['name'])
    class="{{ trim($field['header_class'] . ' ' . $css['th'] . ' ' . $css['sorted']) }}"
    @else
    class="{{ trim($field['header_class'] . ' ' . $css['th']) }}"
    @endif
    style="cursor: pointer;"
    wire:click="$emit('sortColumn', {{ $colNum }})"
    @else
    class="{{ trim($field['header_class'] . ' ' . $css['th']) }}"
    @endif
>
    {{ $slot }}
    @if (array_key_exists('sortable', $field) && $field['sortable'] && !is_null($sortField) && $sortField == $field['name'])
        @if ($sortDir == 'asc')
            <span style="float: right">&#9650;</span>
        @elseif ($sortDir == 'desc')
            <span style="float: right">&#9660;</span>
        @endif
    @endif
</th>

Prior to this discussion the plan was to use a view component like so:

@component('livewire-tables::th-cell', ['colNum' => 0, 'field' => $fields[0], 'css' => $css, 'sortField' => $sortField, 'sortDir' => $sortDir])
    ID
@endcomponent

This is sort of similar to your idea as the user could create a new view component vs a stub. I’d like everything to be customizable down to the sort icons and the position they’re in in the header cell, etc.

Your PR is super intriguing! I know you can’t answer all of these questions and they are just food for thought - so I appreciate your first post as is! I think I just need to play around with it to really wrap my head around the best way to implement.

Yeah, there’s a lot to unpack there. I can tell you that my PR has been merged to master, so if you want to play with it you can download dev-master and give it a spin.

You could look at the PR I wrote and include your own command for livewire-tables:stub.

Generally my hope for the stubs is to be a starting point for how third party packages like tables or selectpickers interact with Livewire. I’m sure there’s more we can do with the Livewire core to make that easier better.

I like your idea of allowing the user to define a different view stub and class stub. Right now when you issue the make:livewire command, you can just pass in a --stub=modal option. It might be good to have php artisan make:livewire post-table --class-stub=Table --view-stub=sortable_table and you could swap sortable_table for any number of custom view stubs you’ve created. Maybe we can run that by Caleb and see if he would accept that PR.

With that PR, then you could probably also extract that in your package too. So your package comes preloaded with stubs, and you also include some custom commands. For example, php artisan make:livewire-sortable-table post-table might actually execute php artisan make:livewire post-table --class-stub=Table --view-stub=sortable_table.

It a bit verbose so I don’t LOVE it, but I think we are getting somewhere.