GithubHelp home page GithubHelp logo

protonemedia / inertiajs-tables-laravel-query-builder Goto Github PK

View Code? Open in Web Editor NEW
432.0 20.0 120.0 1.01 MB

Inertia.js Tables for Laravel Query Builder

Home Page: https://protone.media/en/blog/introducing-inertiajs-tables-a-datatables-like-package-for-laravel-query-builder

License: MIT License

JavaScript 3.31% Vue 24.07% PHP 71.84% Shell 0.51% CSS 0.03% Blade 0.24%
laravel vue vuejs inertiajs inertia eloquent eloquent-models php tailwindcss vue3

inertiajs-tables-laravel-query-builder's Introduction

Inertia.js Tables for Laravel Query Builder

Latest Version on NPM npm Latest Version on Packagist Software License run-tests

This package provides a DataTables-like experience for Inertia.js with support for searching, filtering, sorting, toggling columns, and pagination. It generates URLs that can be consumed by Spatie's excellent Laravel Query Builder package, with no additional logic needed. The components are styled with Tailwind CSS 3.0, but it's fully customizable with slots. The data refresh logic is based on Inertia's Ping CRM demo.

Inertia.js Table for Laravel Query Builder

Support this package!

❤️ We proudly support the community by developing Laravel packages and giving them away for free. If this package saves you time or if you're relying on it professionally, please consider sponsoring the maintenance and development. Keeping track of issues and pull requests takes time, but we're happy to help!

Laravel Splade

Did you hear about Laravel Splade? 🤩

It's the magic of Inertia.js with the simplicity of Blade. Splade provides a super easy way to build Single Page Applications using Blade templates. Besides that magic SPA-feeling, it comes with more than ten components to sparkle your app and make it interactive, all without ever leaving Blade.

Features

  • Auto-fill: auto generates thead and tbody with support for custom cells
  • Global Search
  • Search per field
  • Select filters
  • Toggle columns
  • Sort columns
  • Pagination (support for Eloquent/API Resource/Simple/Cursor)
  • Automatically updates the query string (by using Inertia's replace feature)

Compatibility

Note: There is currently an issue with using this package with Vite!

Installation

You need to install both the server-side package and the client-side package. Note that this package is only compatible with Laravel 9, Vue 3.0, and requires the Tailwind Forms plugin.

Server-side installation (Laravel)

You can install the package via composer:

composer require protonemedia/inertiajs-tables-laravel-query-builder

The package will automatically register the Service Provider which provides a table method you can use on an Interia Response.

Search fields

With the searchInput method, you can specify which attributes are searchable. Search queries are passed to the URL query as a filter. This integrates seamlessly with the filtering feature of the Laravel Query Builder package.

Though it's enough to pass in the column key, you may specify a custom label and default value.

use ProtoneMedia\LaravelQueryBuilderInertiaJs\InertiaTable;

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->searchInput('name');

    $table->searchInput(
        key: 'framework',
        label: 'Find your framework',
        defaultValue: 'Laravel'
    );
});

Select Filters

Select Filters are similar to search fields but use a select element instead of an input element. This way, you can present the user a predefined set of options. Under the hood, this uses the same filtering feature of the Laravel Query Builder package.

The selectFilter method requires two arguments: the key, and a key-value array with the options.

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->selectFilter('language_code', [
        'en' => 'Engels',
        'nl' => 'Nederlands',
    ]);
});

The selectFilter will, by default, add a no filter option to the array. You may disable this or specify a custom label for it.

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->selectFilter(
        key: 'language_code',
        options: $languages,
        label: 'Language',
        defaultValue: 'nl',
        noFilterOption: true,
        noFilterOptionLabel: 'All languages'
    );
});

Columns

With the column method, you can specify which columns you want to be toggleable, sortable, and searchable. You must pass in at least a key or label for each column.

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->column('name', 'User Name');

    $table->column(
        key: 'name',
        label: 'User Name',
        canBeHidden: true,
        hidden: false,
        sortable: true,
        searchable: true
    );
});

The searchable option is a shortcut to the searchInput method. The example below will essentially call $table->searchInput('name', 'User Name').

Global Search

You may enable Global Search with the withGlobalSearch method, and optionally specify a placeholder.

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->withGlobalSearch();

    $table->withGlobalSearch('Search through the data...');
});

If you want to enable Global Search for every table by default, you may use the static defaultGlobalSearch method, for example, in the AppServiceProvider class:

InertiaTable::defaultGlobalSearch();
InertiaTable::defaultGlobalSearch('Default custom placeholder');
InertiaTable::defaultGlobalSearch(false); // disable

Example controller

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Collection;
use Inertia\Inertia;
use ProtoneMedia\LaravelQueryBuilderInertiaJs\InertiaTable;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

class UserIndexController
{
    public function __invoke()
    {
        $globalSearch = AllowedFilter::callback('global', function ($query, $value) {
            $query->where(function ($query) use ($value) {
                Collection::wrap($value)->each(function ($value) use ($query) {
                    $query
                        ->orWhere('name', 'LIKE', "%{$value}%")
                        ->orWhere('email', 'LIKE', "%{$value}%");
                });
            });
        });

        $users = QueryBuilder::for(User::class)
            ->defaultSort('name')
            ->allowedSorts(['name', 'email', 'language_code'])
            ->allowedFilters(['name', 'email', 'language_code', $globalSearch])
            ->paginate()
            ->withQueryString();

        return Inertia::render('Users/Index', [
            'users' => $users,
        ])->table(function (InertiaTable $table) {
            $table
              ->withGlobalSearch()
              ->defaultSort('name')
              ->column(key: 'name', searchable: true, sortable: true, canBeHidden: false)
              ->column(key: 'email', searchable: true, sortable: true)
              ->column(key: 'language_code', label: 'Language')
              ->column(label: 'Actions')
              ->selectFilter(key: 'language_code', label: 'Language', options: [
                  'en' => 'English',
                  'nl' => 'Dutch',
              ]);
    }
}

Client-side installation (Inertia)

You can install the package via either npm or yarn:

npm install @protonemedia/inertiajs-tables-laravel-query-builder --save

yarn add @protonemedia/inertiajs-tables-laravel-query-builder

Add the repository path to the content array of your Tailwind configuration file. This ensures that the styling also works on production builds.

module.exports = {
  content: [
    './node_modules/@protonemedia/inertiajs-tables-laravel-query-builder/**/*.{js,vue}',
  ]
}

Table component

To use the Table component and all its related features, you must import the Table component and pass the users data to the component.

<script setup>
import { Table } from "@protonemedia/inertiajs-tables-laravel-query-builder";

defineProps(["users"])
</script>

<template>
  <Table :resource="users" />
</template>

The resource property automatically detects the data and additional pagination meta data. You may also pass this manually to the component with the data and meta properties:

<template>
  <Table :data="users.data" :meta="users.meta" />
</template>

If you want to manually render the table, like in v1 of this package, you may use the head and body slot. Additionally, you can still use the meta property to render the paginator.

<template>
  <Table :meta="users">
    <template #head>
      <tr>
        <th>User</th>
      </tr>
    </template>

    <template #body>
      <tr
        v-for="(user, key) in users.data"
        :key="key"
      >
        <td>{{ user.name }}</td>
      </tr>
    </template>
  </Table>
</template>

The Table has some additional properties to tweak its front-end behaviour.

<template>
  <Table
    :striped="true"
    :prevent-overlapping-requests="false"
    :input-debounce-ms="1000"
    :preserve-scroll="true"
  />
</template>
Property Description Default
striped Adds a striped layout to the table. false
preventOverlappingRequests Cancels a previous visit on new user input to prevent an inconsistent state. true
inputDebounceMs Number of ms to wait before refreshing the table on user input. 350
preserveScroll Configures the Scroll preservation behavior. You may also pass table-top to this property to scroll to the top of the table on new data. false

Custom column cells

When using auto-fill, you may want to transform the presented data for a specific column while leaving the other columns untouched. For this, you may use a cell template. This example is taken from the Example Controller above.

<template>
  <Table :resource="users">
    <template #cell(actions)="{ item: user }">
      <a :href="`/users/${user.id}/edit`">
        Edit
      </a>
    </template>
  </Table>
</template>

Multiple tables per page

You may want to use more than one table component per page. Displaying the data is easy, but using features like filtering, sorting, and pagination requires a slightly different setup. For example, by default, the page query key is used for paginating the data set, but now you want two different keys for each table. Luckily, this package takes care of that and even provides a helper method to support Spatie's query package. To get this to work, you need to name your tables.

Let's take a look at Spatie's QueryBuilder. In this example, there's a table for the companies and a table for the users. We name the tables accordingly. So first, call the static updateQueryBuilderParameters method to tell the package to use a different set of query parameters. Now, filter becomes companies_filter, column becomes companies_column, and so forth. Secondly, change the pageName of the database paginator.

InertiaTable::updateQueryBuilderParameters('companies');

$companies = QueryBuilder::for(Company::query())
    ->defaultSort('name')
    ->allowedSorts(['name', 'email'])
    ->allowedFilters(['name', 'email'])
    ->paginate(pageName: 'companiesPage')
    ->withQueryString();

InertiaTable::updateQueryBuilderParameters('users');

$users = QueryBuilder::for(User::query())
    ->defaultSort('name')
    ->allowedSorts(['name', 'email'])
    ->allowedFilters(['name', 'email'])
    ->paginate(pageName: 'usersPage')
    ->withQueryString();

Then, we need to apply these two changes to the InertiaTable class. There's a name and pageName method to do so.

return Inertia::render('TwoTables', [
    'companies' => $companies,
    'users'     => $users,
])->table(function (InertiaTable $inertiaTable) {
    $inertiaTable
        ->name('users')
        ->pageName('usersPage')
        ->defaultSort('name')
        ->column(key: 'name', searchable: true)
        ->column(key: 'email', searchable: true);
})->table(function (InertiaTable $inertiaTable) {
    $inertiaTable
        ->name('companies')
        ->pageName('companiesPage')
        ->defaultSort('name')
        ->column(key: 'name', searchable: true)
        ->column(key: 'address', searchable: true);
});

Lastly, pass the correct name property to each table in the Vue template. Optionally, you may set the preserve-scroll property to table-top. This makes sure to scroll to the top of the table on new data. For example, when changing the page of the second table, you want to scroll to the top of the table, instead of the top of the page.

<script setup>
import { Table } from "@protonemedia/inertiajs-tables-laravel-query-builder";

defineProps(["companies", "users"])
</script>

<template>
  <Table
    :resource="companies"
    name="companies"
    preserve-scroll="table-top"
  />

  <Table
    :resource="users"
    name="users"
    preserve-scroll="table-top"
  />
</template>

Pagination translations

You can override the default pagination translations with the setTranslations method. You can do this in your main JavaScript file:

import { setTranslations } from "@protonemedia/inertiajs-tables-laravel-query-builder";

setTranslations({
  next: "Next",
  no_results_found: "No results found",
  of: "of",
  per_page: "per page",
  previous: "Previous",
  results: "results",
  to: "to"
});

Table.vue slots

The Table.vue has several slots that you can use to inject your own implementations.

Slot Description
tableFilter The location of the button + dropdown to select filters.
tableGlobalSearch The location of the input element that handles the global search.
tableReset The location of the button that resets the table.
tableAddSearchRow The location of the button + dropdown to add additional search rows.
tableColumns The location of the button + dropdown to toggle columns.
tableSearchRows The location of the input elements that handle the additional search rows.
tableWrapper The component that wraps the table element, handling overflow, shadow, padding, etc.
table The actual table element.
head The location of the table header.
body The location of the table body.
pagination The location of the paginator.

Each slot is provided with props to interact with the parent Table component.

<template>
  <Table>
    <template v-slot:tableGlobalSearch="slotProps">
      <input
        placeholder="Custom Global Search Component..."
        @input="slotProps.onChange($event.target.value)"
      />
    </template>
  </Table>
</template>

Testing

A huge Laravel Dusk E2E test-suite can be found in the app directory. Here you'll find a Laravel + Inertia application.

cd app
cp .env.example .env
composer install
npm install
npm run production
touch database/database.sqlite
php artisan migrate:fresh --seed
php artisan dusk:chrome-driver
php artisan serve
php artisan dusk

Upgrading from v1

Server-side

  • The addColumn method has been renamed to column.
  • The addFilter method has been renamed to selectFilter.
  • The addSearch method has been renamed to searchInput.
  • For all renamed methods, check out the arguments as some have been changed.
  • The addColumns and addSearchRows methods have been removed.
  • Global Search is not enabled by default anymore.

Client-side

  • The InteractsWithQueryBuilder mixin has been removed and is no longer needed.
  • The Table component no longer needs the filters, search, columns, and on-update properties.
  • When using a custom thead or tbody slot, you need to provide the styling manually.
  • When using a custom thead, the showColumn method has been renamed to show.
  • The setTranslations method is no longer part of the Pagination component, but should be imported.
  • The templates and logic of the components are not separated anymore. Use slots to inject your own implementations.

v2.1 Roadmap

  • Boolean filters
  • Date filters
  • Date range filters
  • Switch to Vite for the demo app

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Other Laravel packages

  • Laravel Analytics Event Tracking: Laravel package to easily send events to Google Analytics.
  • Laravel Blade On Demand: Laravel package to compile Blade templates in memory.
  • Laravel Cross Eloquent Search: Laravel package to search through multiple Eloquent models.
  • Laravel Eloquent Scope as Select: Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.
  • Laravel Eloquent Where Not: This Laravel package allows you to flip/invert an Eloquent scope, or really any query constraint.
  • Laravel FFMpeg: This package provides an integration with FFmpeg for Laravel. The storage of the files is handled by Laravel's Filesystem.
  • Laravel Form Components: Blade components to rapidly build forms with Tailwind CSS Custom Forms and Bootstrap 4. Supports validation, model binding, default values, translations, includes default vendor styling and fully customizable!
  • Laravel Mixins: A collection of Laravel goodies.
  • Laravel Verify New Email: This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
  • Laravel Paddle: Paddle.com API integration for Laravel with support for webhooks/events.
  • Laravel WebDAV: WebDAV driver for Laravel's Filesystem.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

inertiajs-tables-laravel-query-builder's People

Contributors

coclav avatar donmbelembe avatar laravel-shift avatar pascalbaljet avatar prey87 avatar pyiter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

inertiajs-tables-laravel-query-builder's Issues

Vuetify Datatable

Can you create for vuetify?

Or how to use InteractsWithQueryBuilder with vuetify table?

no perPage dinamic

There is dont have perPage property when i add requsest perPage=20, each time i change a filter etc, the page will be resset pePage query be default behavior.

Conflicting peer dependency: @vue/[email protected]

Trying to install @protonemedia/inertiajs-tables-laravel-query-builder in a Vue3 project results in peer dependency conflict. I was able to install by using the --force option. Below is the full NPM log.

# npm resolution error report

2021-04-12T00:26:11.826Z


Could not resolve dependency:
@protonemedia/inertiajs-tables-laravel-query-builder@"*" from the root project

Conflicting peer dependency: @vue/[email protected]
node_modules/@vue/composition-api
  peerOptional @vue/composition-api@"^1.0.0-rc.6" from @protonemedia/[email protected]
  node_modules/@protonemedia/inertiajs-tables-laravel-query-builder
    @protonemedia/inertiajs-tables-laravel-query-builder@"*" from the root project

Fix the upstream dependency conflict, or retry
this command with --force, or --legacy-peer-deps
to accept an incorrect (and potentially broken) dependency resolution.

Raw JSON explanation object:

{
  "code": "ERESOLVE",
  "current": null,
  "currentEdge": null,
  "edge": {
    "type": "prod",
    "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
    "spec": "*",
    "error": "MISSING",
    "from": {
      "location": ""
    }
  },
  "peerConflict": {
    "name": "@vue/composition-api",
    "version": "1.0.0-rc.6",
    "whileInstalling": {
      "path": ""
    },
    "location": "node_modules/@vue/composition-api",
    "dependents": [
      {
        "type": "peerOptional",
        "name": "@vue/composition-api",
        "spec": "^1.0.0-rc.6",
        "from": {
          "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
          "version": "1.1.0",
          "whileInstalling": {
            "path": ""
          },
          "location": "node_modules/@protonemedia/inertiajs-tables-laravel-query-builder",
          "dependents": [
            {
              "type": "prod",
              "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
              "spec": "*",
              "from": {
                "location": ""
              }
            }
          ]
        }
      }
    ]
  },
  "strictPeerDeps": false,
  "force": false
}

Vue 3 shown as supported?

I think your blog post mentioned Vue3 support was coming but the repo shows it’s already available? Can you clarify?

ButtonWithDropdown width must match content

You need to change this line
<div class="mt-2 w-64 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
to be
<div class="mt-2 container rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">

this will allow the dropdown to have labels wider than the 64 pixels width

Spaces get deleted when using the search filter

When using the search filter, if you have a space in your search string it gets deleted initially. If you hit space again slowly, it will stay. When another space is attempted, both spaces are removed. For me, this bug makes the filter unusable.

I believe it has something to do with the URL encoding of the search terms as you type.

Issue with addColumn method (default false)

When i use addColumn with default = false, initial table setup is correct, but, if i try to toggle up that column, button stops working without displaying the new column.
If i set default = true the button works as usual.
Nobody else encountered this problem?

Custom head/body v-show="show('colume')" does not work

Hi,

I just upgraded to 2.0 and everythink works fine except the v-show="show('colume')" on a custom head/body. Renamed showColumn to show as described.

[Vue warn]: Property "show" was accessed during render but is not defined on instance.
Uncaught (in promise) TypeError: _ctx.show is not a function

Any idea or example?

Vue component doesn't work when route has parameters

The Vue component throws an error when used on a route with parameters. For example, say you were using the table on the route example.com/users/1/tasks. When you update any of the filters or search fields and it updates the route with the new query string, it would throw the error Error: Ziggy error: 'user' parameter is required for route 'tasks'.

Conflicting peer dependency

Trying to install @protonemedia/inertiajs-tables-laravel-query-builder in a Vue3 project results in peer dependency conflict.
Below is the full NPM log.

# npm resolution error report

2022-06-27T09:51:53.078Z

While resolving: undefined@undefined
Found: [email protected]
node_modules/vue
  peer vue@"^3.0.0" from @inertiajs/[email protected]
  node_modules/@inertiajs/inertia-vue3
    @inertiajs/inertia-vue3@"^0.6.0" from the root project
  peer vue@"3.2.37" from @vue/[email protected]
  node_modules/@vue/server-renderer
    @vue/server-renderer@"3.2.37" from [email protected]

Could not resolve dependency:
@protonemedia/inertiajs-tables-laravel-query-builder@"*" from the root project

Conflicting peer dependency: [email protected]
node_modules/vue
  peer vue@">= 2.5 < 3" from @vue/[email protected]
  node_modules/@vue/composition-api
    peerOptional @vue/composition-api@"^1.0.0-rc.6" from @protonemedia/[email protected]
    node_modules/@protonemedia/inertiajs-tables-laravel-query-builder
      @protonemedia/inertiajs-tables-laravel-query-builder@"*" from the root project

Fix the upstream dependency conflict, or retry
this command with --force, or --legacy-peer-deps
to accept an incorrect (and potentially broken) dependency resolution.

Raw JSON explanation object:

{
  "code": "ERESOLVE",
  "edge": {
    "type": "prod",
    "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
    "spec": "*",
    "error": "MISSING",
    "from": {
      "location": ""
    }
  },
  "dep": {
    "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
    "version": "1.3.4",
    "whileInstalling": {
      "path": ""
    },
    "location": "node_modules/@protonemedia/inertiajs-tables-laravel-query-builder",
    "isWorkspace": false,
    "dependents": [
      {
        "type": "prod",
        "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
        "spec": "*",
        "error": "MISSING",
        "from": {
          "location": ""
        }
      }
    ]
  },
  "current": null,
  "peerConflict": {
    "current": {
      "name": "vue",
      "version": "3.2.37",
      "location": "node_modules/vue",
      "isWorkspace": false,
      "dependents": [
        {
          "type": "peer",
          "name": "vue",
          "spec": "^3.0.0",
          "from": {
            "name": "@inertiajs/inertia-vue3",
            "version": "0.6.0",
            "location": "node_modules/@inertiajs/inertia-vue3",
            "isWorkspace": false,
            "dependents": [
              {
                "type": "prod",
                "name": "@inertiajs/inertia-vue3",
                "spec": "^0.6.0",
                "from": {
                  "location": ""
                }
              }
            ]
          }
        },
        {
          "type": "peer",
          "name": "vue",
          "spec": "3.2.37",
          "from": {
            "name": "@vue/server-renderer",
            "version": "3.2.37",
            "location": "node_modules/@vue/server-renderer",
            "isWorkspace": false,
            "dependents": [
              {
                "type": "prod",
                "name": "@vue/server-renderer",
                "spec": "3.2.37",
                "from": {
                  "name": "vue",
                  "version": "3.2.37"
                }
              }
            ]
          }
        }
      ]
    },
    "peer": {
      "name": "vue",
      "version": "2.6.14",
      "whileInstalling": {
        "path": ""
      },
      "location": "node_modules/vue",
      "isWorkspace": false,
      "dependents": [
        {
          "type": "peer",
          "name": "vue",
          "spec": ">= 2.5 < 3",
          "from": {
            "name": "@vue/composition-api",
            "version": "1.6.3",
            "whileInstalling": {
              "path": ""
            },
            "location": "node_modules/@vue/composition-api",
            "isWorkspace": false,
            "dependents": [
              {
                "type": "peerOptional",
                "name": "@vue/composition-api",
                "spec": "^1.0.0-rc.6",
                "from": {
                  "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
                  "version": "1.3.4",
                  "whileInstalling": {
                    "path": ""
                  },
                  "location": "node_modules/@protonemedia/inertiajs-tables-laravel-query-builder",
                  "isWorkspace": false,
                  "dependents": [
                    {
                      "type": "prod",
                      "name": "@protonemedia/inertiajs-tables-laravel-query-builder",
                      "spec": "*",
                      "error": "MISSING",
                      "from": {
                        "location": ""
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  },
  "strictPeerDeps": false,
  "force": false,
  "isMine": true
}

Refactor Required for Inertia's Page Link Component

Hey! Thanks for the great package.

I have detected pagination links doesn't work well on my latest JetStream and Inertia env.

Inertia changed the Link component usage with [email protected] (ref: https://inertiajs.com/releases/inertia-vue-0.7.0-2021-07-13)

inertia-link usages should be refactored as Link, and the component should be imported on here https://github.com/protonemedia/inertiajs-tables-laravel-query-builder/blob/main/js/Tailwind2/Pagination.vue#L9

Usage of the links: https://inertiajs.com/links

I wanted to fix it with a PR but I could not create a new branch due to I don't have permission

Thanks!

Table Styling

Hi,
What is the best way to control styling.

For example, reducing font size, line padding and so on.

What about per column styling, for example left align, right align depending on controls?

Would be good if styling can be "passed" to the component.

Many thanks.

Cheers.

Checkbox state looks weird enabled/disabled (visible/hidden)

image

So the first one is disabled but why is the switch for the others also on the left?

And as mentioned here: #14 (comment)
If the id-column is set to enabled=false (which I think should be "hidden=true" or "visible=false") I'm not able to display the id column. All other columns can be hidden and displayed again except for the ones hidden (disabled) by default through the new third parameter.

Filter select is not working

I did not know if it is an issue or just a edge case or a bad implementation of mine.

I can not make work the filter select:

Backend code:

    public function index(): \Inertia\Response
    {
        $globalSearch = AllowedFilter::callback('global', function ($query, $value) {
            $query->where(function ($query) use ($value) {
                $query->where('name', 'LIKE', "%{$value}%")->orWhere('email', 'LIKE', "%{$value}%");
            });
        });

        $users = QueryBuilder::for(User::class)
            ->defaultSort('name')
            ->allowedSorts(['name', 'email', 'id'])
            ->allowedFilters(['name', 'email', 'id', $globalSearch])
            ->paginate()
            ->withQueryString();

        return Inertia::render('Users', [
            'users' => $users,
        ])->table(function (InertiaTable $table) {
            $table->addSearchRows([
                'name' => 'Name',
                'email' => 'Email address',
            ])->addFilter('name', 'Name', [
                'Ariel' => 'ariel',
            ])->addColumns([
                'email' => 'Email address',
                'id' => 'ID',
            ]);
        });
    }

Vue component

<template>
    <breeze-authenticated-layout>
        <template #header>
            <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                Users
            </h2>
        </template>

        <div class="py-12">
            <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">

                <Table
                    :filters="queryBuilderProps.filters"
                    :search="queryBuilderProps.search"
                    :columns="queryBuilderProps.columns"
                    :on-update="setQueryBuilder"
                    :meta="users"
                >
                    <template #head>
                        <tr>
                            <th @click.prevent="sortBy('name')">Name</th>
                            <th v-show="showColumn('email')" @click.prevent="sortBy('email')">Email</th>
                            <th v-show="showColumn('id')" @click.prevent="sortBy('id')">ID</th>
                        </tr>
                    </template>

                    <template #body>
                        <tr v-for="user in users.data" :key="user.id">
                            <td>{{ user.name }}</td>
                            <td v-show="showColumn('email')">{{ user.email }}</td>
                            <td v-show="showColumn('id')">{{ user.id }}</td>
                        </tr>
                    </template>
                </Table>

            </div>
        </div>

    </breeze-authenticated-layout>
</template>

<script>
    import BreezeAuthenticatedLayout from '@/Layouts/Authenticated'
    import { InteractsWithQueryBuilder, Tailwind2 } from '@protonemedia/inertiajs-tables-laravel-query-builder';

    export default {
        components: {
            BreezeAuthenticatedLayout,
            Table: Tailwind2.Table,
        },

        mixins: [InteractsWithQueryBuilder],

        props: {
            auth: Object,
            errors: Object,
            users: Object | Array,
        },
    }
</script>

I am using Breeze, but maybe there is something that I miss.

Auto generation of table columns

Great library, thank you!

Am I missing something or is there a way of auto generating the columns and headers based on the controller's output? The information is all in the AJAX response. This is to avoid having to statically add the template for header and body.

Many thanks,

James

Add support for localization

Right now there's no way to customize messages like "No results found", "Previous", "Next" , "X to Y of N results", "Remove Search", etc...

Request to add an option to "addColumn" to disable as default

Hi,
Wondering if it would be possible to add the option to add columns, but add them disabled by default.

Would be a bonus if the state can be saved, so that when returning to the table, that the selected columns visibility is maintained.

Might be nice to extend to saving sort order etc.

    /**
     * Add a column to the query builder.
     *
     * @param string $key
     * @param string $label
     * @param bool $enabled
     * @return self
     */
    public function addColumn(string $key, string $label, bool enabled = true): self
    {
        $this->columns->put($key, [
            'key'     => $key,
            'label'   => $label,
            'enabled' => $enabled,
        ]);

        return $this;
    }

Weird look for filters

image

When pay layout is small because there is only one item being displayed in the table, the related filters on the top left look weird by default. Maybe it's a responsive issues?

no way to preserveScroll

There is no way to set the "preserveScroll" property of $inertia.get() [queryBuilderData], each time i change a filter etc, the page scrolls to the top. preserveScroll should be default behavior.

this is resolved in PR #45 in the mean time i will use my own repo. Anyway awsome package!

Can I able to add query the table based on groupBy

I need to create a table based on groupby, count it based on the value and show it in the table

` $globalSearch = AllowedFilter::callback('global', function ($query, $value) {
$query->where(function ($query) use ($value) {
$query->orWhere('term', 'LIKE', "%{$value}%");
});
});

    $activities = QueryBuilder::for(Activity::select('term', DB::raw('count(*) as total'))
        ->groupBy('term'))
        ->defaultSort('term')
        ->allowedSorts(['term'])
        ->allowedFilters(['term', $globalSearch])
        ->paginate()
        ->withQueryString();

    return Inertia::render('Dashboard', [
        'activities' => $activities,
    ])->table(function (InertiaTable $table) {
        $table->withGlobalSearch()
        ->defaultSort('term')
        ->column(key: 'term', searchable: true, sortable: true, canBeHidden: false);
    });`

Style broken on production build

Hello,

Seems like there's an issue when building assets to production. Everything looks good when running mix in the development

Screenshot 2021-04-27 at 09 45 06

But when assets are compiled to production, it seems broken

Screenshot 2021-04-27 at 09 44 42

Mix also throws warning which might be related to this (Using vue3)

[@vue/compiler-sfc] the >>> and /deep/ combinators have been deprecated. Use :deep() instead.

Mixed content when changing page in table

In my prod server i'm getting this error "Mixed Content: The page at '' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint ''. This request has been blocked; the content must be served over HTTPS."

Probably not a bug, but can someone help me with this?

[2.x] Production build not working in Vite projects

When using the library in a Laravel project that uses Vite, you'll find this error in the browser console: currentRenderingInstance is null. The test suite, a regular Laravel app with Laravel Mix, uses Webpack and works fine.

From searching the web, it seems the library is using the wrong Vue instance (at least not the one from the app). On the other hand, it looks like the Vite config is set up correctly.

From inspecting the build manually, I can see Vue is not included in the library, which is good.

Any help would be appreciated!

TypeScript support

Hey,

Currently I can't import { Table } from '@protonemedia/inertiajs-tables-laravel-query-builder' without using @ts-ignore, because the package provides no typings.

Would be nice if the package provided TypeScript typings, so Table attributes and all slots would have autocompletion support.

Suggestions: Add a "Remove Filter" button that removes all applied filters.

#1 - Sorry for the English, I'm not very familiar with the language.

#2 - During my use, I needed to implement a button that removes all filters applied to the table. So I thought it would be a good idea to suggest this implementation.

#3 - I would like to help in some way, but I'm not that good with VueJs. But if you want, I can try to help in some way.

Unable to render the table component in the vue frontend

This package seems to do wonders, but I am unable to render the page as soon as I import the component. I am on Laravel 8, Inertia JS and Vue 3. Please help me if incase I am doing it incorrectly. Thank you for your time.

Here is the console error which is getting thrown.

Uncaught (in promise) TypeError: this.$once is not a function
at Proxy.mounted (app.js:66560)
at callWithErrorHandling (app.js:24930)
at callWithAsyncErrorHandling (app.js:24939)
at Array.hook.__weh.hook.__weh (app.js:26707)
at flushPostFlushCbs (app.js:25128)
at flushJobs (app.js:25164)

Here is my index method in the controller through which I am rendering the component.

public function index()
{
    $users = QueryBuilder::for(User::class)
        ->allowedFields(['id', 'name', 'email', 'role_id', 'created_at'])
        ->allowedSorts(['id', 'name', 'email', 'role_id', 'created_at'])
        ->allowedFilters(['name', 'email', 'role_id'])
        ->paginate()
        ->withQueryString();

    return Inertia::render('Backend/Management/AudienceManagement/Users/Data', [
        'users' => $users
    ])->table(function (InertiaTable $table) {
        $table->addSearchRows([
            'name' => 'Name',
            'email' => 'Email Address'
        ])->addFilter('role_id', 'Role', [
            '1' => 'Administrator',
            '2' => 'Employee',
            '3' => 'Client',
        ])->addColumns([
            'id' => 'ID',
            'name' => 'Name',
            'email' => 'Email',
            'role_id' => 'Role',
            'created_at' => 'Date',
        ])->disableGlobalSearch();
    });
}

Here is the table component imported in the frontend.

<Table :filters="queryBuilderProps.filters" :search="queryBuilderProps.search" :columns="queryBuilderProps.columns" :on-update="setQueryBuilder" :meta="users">
    <template #head>
        <tr>
            <th @click.prevent="sortBy('id')">ID</th>
            <th @click.prevent="sortBy('name')">Name</th>
            <th v-show="showColumn('email')" @click.prevent="sortBy('email')">Email</th>
            <th v-show="showColumn('role_id')" @click.prevent="sortBy('role_id')">Role</th>
            <th v-show="showColumn('created_at')" @click.prevent="sortBy('created_at')">Date</th>
        </tr>
    </template>

    <template #body>
        <tr v-for="user in users.data" :key="user.id">
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <th v-show="showColumn('email')">{{ user.email }}</th>
            <th v-show="showColumn('role_id')">{{ user.role_id }}</th>
            <th v-show="showColumn('created_at')">{{ user.created_at }}</th>
        </tr>
    </template>
</Table>

<script>
    import BreezeAuthenticatedLayout from '@/Layouts/Authenticated';
    import {
        InteractsWithQueryBuilder,
        Tailwind2
    } from '@protonemedia/inertiajs-tables-laravel-query-builder';

    export default {
        mixins: [InteractsWithQueryBuilder],

        components: {
            Table: Tailwind2.Table,
            BreezeAuthenticatedLayout
        },

        props: {
            users: Object
        }
    }
</script>

Cannot use import statement outside a module

Is this package does not work with Inertia JS?

E:\Projects\dymlab>node public/js/ssr.js
Starting SSR server...
Server started.
E:\Projects\dymlab\node_modules\@protonemedia\inertiajs-tables-laravel-query-builder\js\index.js:1
import iwqb from './InteractsWithQueryBuilder.vue';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:1054:16)
    at Module._compile (internal/modules/cjs/loader.js:1102:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Module.require (internal/modules/cjs/loader.js:1026:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.@protonemedia/inertiajs-tables-laravel-query-builder (E:\Projects\dymlab\public\js\ssr.js:13219:18)
    at __webpack_require__ (E:\Projects\dymlab\public\js\ssr.js:13287:41)
    at Object../node_modules/babel-loader/lib/index.js??clonedRuleSet-5.use[0]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./resources/js/Pages/Dashboard.vue?vue&type=script&lang=js (E:\Projects\dymlab\public\js\ssr.js:1466:110)
ReferenceError: Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization
    at Module.default (E:\Projects\dymlab\public\js\ssr.js:11202:42)
    at E:\Projects\dymlab\node_modules\@inertiajs\inertia-vue3\dist\index.js:1:9011
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
ReferenceError: Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization
    at Module.default (E:\Projects\dymlab\public\js\ssr.js:11202:42)
    at E:\Projects\dymlab\node_modules\@inertiajs\inertia-vue3\dist\index.js:1:9011
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

Suggestion for php8.0

Hey @pascalbaljet,

Just a suggestion for anyone looking to clean up a simple datatable:

$languages = QueryBuilder::for(Language::class)
    ->defaultSort('id')
    ->allowedSorts($this->datatableColumns)
    ->paginate()
    ->withQueryString();

$callback = fn (InertiaTable $table) =>
    $table->addSearchRows($this->datatableSearchRows)
         ->addColumns($this->datatableColumns);

return Inertia::render(
    'Languages/Index',
    [
         'languages' => $languages,
    ]
)->table($callback);

QueryBuilder error on search

Hey there,

Users are displayed properly in the table, but I'm getting the following error when I type something in the search field:
Argument 1 passed to Spatie\QueryBuilder\QueryBuilder::Spatie\QueryBuilder\Concerns\{closure}() must be of the type string, array given

Using the example controller:

            ->defaultSort('name')
            ->allowedSorts(['name', 'email'])
            ->allowedFilters(['name', 'email', $globalSearch])
            ->paginate()
            ->withQueryString();

as well as the example page component with the table from the README.

Vue 3, Laravel Jetstream(Inertia).

Feature Enhancement - Access row index on cell slot

Hi @pascalbaljet , I have some issue when accessing row index on cell slot. I suggest you to add slot props (index). My reason is I want to process the index in table.

Please refer to this code:
https://github.com/protonemedia/inertiajs-tables-laravel-query-builder/blob/main/js/Components/Table.vue

please check line code 159, can you add this code :index="key" ?

<slot
      :name="`cell(${column.key})`"
      :item="item"
      :index="key"
    >
      {{ item[column.key] }}
</slot>

Column Sort Icons

Hi,
Wondering if you have considered adding column sort direction icon (svg)?

Or did I miss it?

Many thanks.

include relationships in queries

I think this might be beyond the scope of this project but I hope you don't mind me asking:

Would there be a way to dig down into relations for the table data via the allowedIncludes() method of QueryBuilder?

E.g. if I have a model with a belongsTo relation to User, I want to be able to display the user's name, not only his id.

So, a query like

QueryBuilder::for(SomeModel::class)
            ->allowedIncludes('user')
            ->defaultSort('id')
            ->allowedSorts(['id', 'user_id'])
            ->allowedFilters(['user_id', $globalSearch])
            ->paginate()
            ->withQueryString();

and probably some setting to have that include called by default?

Best regards,
Martin

Tailwind 3 support

Hi,

I am trying to use this library in Laravel Jetsream with Inertia JS. The issue is that Jetsream comes with 3.x version, Your tables use the previous one - 2.0.

image

Is it possible to use this library 3.x? Only by creating a custom component?

Error when importing javascript modules (ViteJs?)

Using Laravel 9 (jetstream), Inertia.js, Vue3, ViteJs.

I used this package before, and got it working. The main difference now is that i use ViteJS. Not sure if this is related.

I get the following error when importing this (nice) package:

image

Uncaught (in promise) SyntaxError: The requested module '/node_modules/qs/lib/index.js?v=57eb2887' does not provide an export named 'default' (at InteractsWithQueryBuilder.vue:2:8)

I`m using the example from de documentation:

<script>
import { InteractsWithQueryBuilder, Tailwind2 } from '@protonemedia/inertiajs-tables-laravel-query-builder';

export default {
  mixins: [InteractsWithQueryBuilder],

  components: {
    Table: Tailwind2.Table
  },

  props: {
    users: Object
  }
};
</script>

Am i doing something wrong or is this a bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.