Code Monkey home page Code Monkey logo

nova-select-plus's Introduction

Nova Select Plus

Installation

composer require ziffmedia/nova-select-plus

Versions & Compatibility

  • Version ^1.0 supports Nova 2-3
  • Version ^2.0 supports Nova 4 (with Vue 3, dark mode support, etc)

Description & Use Cases

alt text

This Nova component was built to satisfy the use cases just beyond Nova's built-in <select> component. Here are some scenarios where you might want SelectPlus (which uses vue-select) over the simple Select:

Select For BelongsToMany and MorphsToMany On the Form Screen

The default Nova experience for BelongsToMany and MorphsToMany is to have a separate UI screen for attaching/detaching and syncing relationships through a "Pivot" model. For simple relationships (relationships that do not have addition pivot values or the only value in the pivot table is there for ordering), it is benefitial to move this Selection to the Form workflow instead of a separate workflow.

Ajax For Options

For Select's that have between a handful to several 1000 options, it is more peformant to load the full list of options only on the screen that needs it: the Form screen.

There are 2 options for Ajax Options, the default is to load them all on the Form load. The second is to allow for full option searching (in this case you can write you own ajax search resolver).

Reordering Simple Pivot/BelongsToMany Relations

Through ->reorderable(), you can enable a SelectPlus field to be reorderable. This allows, at BelongsToMany->sync() time, to populate a pivot value useful for ordering relations.

Usage

use ZiffMedia\NovaSelectPlus\SelectPlus;
    // setup model like normal:
    public function statesLivedIn()
    {
        return $this->belongsToMany(State::class, 'state_user_lived_in')->withTimestamps();
    }

    // add Nova Resource Field
    SelectPlus::make('States Lived In', 'statesLivedIn', State::class),

Options & Examples

->label(string|closure $attribute) Pick a different attribute to use as the label

Default: 'name'

SelectPlus::make('States Lived In', 'statesLivedIn', State::class)
  ->label('code')

If a closure is provided, it will be called and the value can be utilized. Additionally, the output may be HTML as the component will v-html the output on the frontend:

// Using php 7.4 short functions:
SelectPlus::make('States Visited', 'statesVisited', State::class)
    ->label(fn ($state) => $state->name . " <span class=\"text-xs\">({$state->code})</span>")

->usingIndexLabel() & ->usingDetailLabel()

Default is to produce a count of the number of items on the index and detail screen

alt text

If a string name is provided, the name attribute is plucked and comma joined:

SelectPlus::make('States Lived In', 'statesLivedIn', State::class)
  ->usingIndexLabel('name'),

alt text

If a closure is provided, it will be called, and the value will be utilized. If the value is a string, it will be placed:

SelectPlus::make('States Lived In', 'statesLivedIn', State::class)
  ->usingIndexLabel(fn($models) => $models->first()->name ?? ''),

alt text

If an array is returned, the Index and Detail screens will produce a <ol> or <ul> list:

SelectPlus::make('States Lived In', 'statesLivedIn', State::class)
  ->usingIndexLabel(fn($models) => $models->pluck('name')),

alt text

->reorderable(string $pivotOrderAttribute) - Ability to reorder multiple selects

    // assuming in the User model:
    public function statesVisited()
    {
        return $this->belongsToMany(State::class, 'state_user_visited')
            ->withPivot('order')
            ->orderBy('order')
            ->withTimestamps();
    }

    // inside the Nova resource:
    SelectPlus::make('States Lived In', 'statesLivedIn', State::class)
        ->reorderable('order'),

alt text

->optionsQuery(closure) - Ability to apply changes to options query object

    // inside the Nova resource (exclude all states that start with C)
    SelectPlus::make('States Lived In', 'statesLivedIn', State::class)
        ->optionsQuery(function (Builder $query) {
            $query->where('name', 'NOT LIKE', 'C%');
        })
  • Note: this will apply before any ajaxSearchable() functionality, it will not replace it but be applied along with ajaxSearchable() if it exists

->ajaxSearchable(string|closure|true) Ajax search for values

Given a string, models will be search the resources via the provided attribute using WHERE LIKE. Given a callback, returning a Collection will populate the dropdown:

    SelectPlus::make('States Visited', 'statesVisited', State::class)
        ->ajaxSearchable(function ($search) {
            return StateModel::where('name', 'LIKE', "%{$search}%")->limit(5);
        })

alt text

nova-select-plus's People

Contributors

dependabot[bot] avatar eugenefvdm avatar justindantzer avatar mstaack avatar nickbelhomme avatar pecuchet avatar ralphschindler 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

Watchers

 avatar  avatar  avatar

nova-select-plus's Issues

Feature request | Categorizable Select

It would be nice to categorize items based on a column on the selectable items.

Example:

SelectPlus::make('Related Products')->categorizeBy('department');

This would output a categorized list of products based on department.

Action events

Hello, for first thanks for package.
When attaching items via "BelongsToMany" Nova field, every action stored as "Attach" event in the event_actions table with the attached value. When do the same action via "SelectPlus", its stored as "Update" action with the empty array value.
Screen Shot 2020-09-14 at 10 48 22 PM

Thanks

Doesn't show pickable options

The field presents already picked options just right, but the list of available options is empty.

Screenshot 2021-03-25 at 06 55 58

I'm able to correctly delete the elements, and also add one weird empty element, though can't save because the id is empty.

Screenshot 2021-03-25 at 06 56 55

return $this->belongsToMany(GrammarInfoEntry::class,  'grammar_info_topic_entry')
            ->using(GrammarInfoTopicEntry::class)
            ->withPivot('sort')
            ->orderBy('sort')
            ->withTimestamps();
SelectPlus::make('Child Entries', 'grammarInfoEntries', '\App\Nova\GrammarInfoEntry')
                ->label('title')

The request for grammarInfoEntries actually returns html for the whole page, including when filtering with ajax.

Nova v3.22.0

How filter belongsToMany ?

I would like to add clausere "where" from select belongsToMany. Now when added join and where show all fields....

Creating a Select Plus Component on a Panel causes the Select Plus Controller to throw errors

Steps to Reproduce:

  1. Create a small nova app.
  2. Create three models.
  3. Add the select plus component to a panel to group it (For my case, I used epartment/nova-dependency-container).
  4. View the nova form for the model.
  5. Observe that the panel as well as the controller throw errors.

Further investigation:

I looked into this, and it looks like the issue is in the controller.
The code below assumes that the SelectPlus Component is at the top level and not on any panels:

  /** @var SelectPlus $field */
        $field = $request->newResource()
            ->availableFields($request)
            ->where('component', 'select-plus')
            ->where('attribute', $relationship)

The correct call may be updateFields($request) but it requires more investigation to be sure.

Feature Request: Create Resource

Hello,

would it be possible to add a "create" field for the attached resource? For example, the default Laravel Nova BelongsTo-field has an option showCreateRelationButton(), which adds that button right to the dropdown-menu and opens a modal for creation when clicked.
So when you want to attach a resource which you forgot to create before, you can simply catch that up.

And thank you for your great package. It already makes the creation of resources a lot easier, because you don't have to open it twice just for relations.

DependsOn: native field options don't work

          But still some native field options don't work, for example:
SelectPlus::make('Product Varieties', 'productVarieties', ProductVariety::class)
    ->usingDetailLabel('name')
    ->resolveUsing(fn($models) => $models->sortBy('pivot.order')->values())
    ->reorderable('order')
    ->required()
    ->onlyOnForms()
    ->dependsOn('productType', function (SelectPlus $field, NovaRequest $request, FormData $formData) {
        if (!$formData->productType) {
           $field->show();
        } else {
          $field->hide();
       }
    })

I would like to be able to add for example: ->required() or disable it depending on the other field.
On all others basic Nova Fields all this methods work as they should.

Originally posted by @Bibesko in #49 (comment)

Using as an Action field

First of all, thanks for sharing this great package!

Is the SelectPlus field compatible with Nova Actions?

I have a simple Nova Action for updating a manytomany relation on a model:

class UpdateConsortia extends Action
{
    use InteractsWithQueue, Queueable;

    /**
     * Perform the action on the given models.
     *
     * @param  \Laravel\Nova\Fields\ActionFields  $fields
     * @param  \Illuminate\Support\Collection  $models
     * @return mixed
     */
    public function handle(ActionFields $fields, Collection $models)
    {
	foreach ($models as $model) {
	    $model->consortia()->sync($fields->consortia);
            $model->save();
        }
    }

    /**
     * Get the fields available on the action.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @return array
     */
    public function fields(NovaRequest $request)
    {
	$consortia = Consortium::pluck('id', 'name');
	$formattedConsortia = $consortia->mapWithKeys(function ($id, $name) {
	    return [$id => ['label' => $name]];
	});

        return [
	    MultiSelect::make('Consortia')->options($formattedConsortia)->displayUsingLabels(),
	];
    }
}

I'd like to swap the MultiSelect field for a SelectPlus field like this:

public function fields(NovaRequest $request)
{
    return [
        SelectPlus::make('Consortia', 'consortia', Consortium::class)
            ->usingDetailLabel('name')
            ->label(fn ($consortium) => $consortium->name . "")
    ];
}

However, although the field renders fine $fields->consortia always returns null. The exact same field works fine used directly on the resource page.

Required fields not supported

I am trying to integrate the select plus component into my project; however, it seems that the validation rules I have set using the rules method are not being correctly interpreted, or rather it seems that they are being completely ignored. I am attaching an example of the code:

SelectPlus::make("My roles", 'roles', Role::class)
                ->rules('required','min:1'),

when instead I select at least one option, it works successfully.

What I'm doing wrong?

Help text is not showing up on the form

To reproduce:
Make a SelectPlus on a Resource and add help text then load the create or update form.

Example:

class Bar extends Resource
{
    public functions fields ($request)
    {
          return [
              SelectPlus::make('Foos', 'foos', Foo::class)
                  ->help('Select all related foos');
          ];
     }
}

I believe this is fixable by adding :show-help-text="showHelpText" to the <default-field โ€ฆ> tag here

Enhancement - Ability to create a new related item.

Love this inline relation field. I was thinking it would be awesome if you could make create a new relational item inline as well.

As an example:
Say you are relating tags and you want to create a new one. Add a button to load in required fields in a modal, create the new item then add it as a relation.

closeOnSelect

Should have an option to toggle this for the vue-select

Callable in SelectPlus::label()

Hi thanks for the useful package!
I would like to customize label in the vue-select field, much in the same way as for usingDetailLabel and usingIndexLabel. That is, passing a callback to SelectPlus::label() and being able to build a custom string from it. Field::displayUsing() does not seem to work here.
Any ideas?

I played around by adding this method to SelectPlus:

protected function getValueFromLabel($model){
        return is_callable($this->label)
            ? call_user_func($this->label, $model)
            : $model->{$this->label};
}

and changing $model->{$this->label} to $this->getValueFromLabel($model) in SelectPlus::mapToSelectionValue(). It works, except when doing an ajax search...

Dropdown options hidden by `overflow-hidden`

When the SelectPlus Nova field is used near the end of a Nova panel, most dropdown options are hidden by the overflow-hidden on the container. Is this something you can change, or do you know a work-around?

overflow-hidden

Call to undefined method ... newModel()

I have this error: "Call to undefined method App \ Models \ Tags:: newModel () "

I need this: associate tags (already present in the tags table) to the user, storing the association in the pivot_user_tags table

this is my code:

  • user table
  • tags table
    Schema::create('ad_tag_pivot', function (Blueprint $table) { $table->id(); $table->text('title', 255); $table->timestamps(); });
  • pivot_user_tags table
    Schema::create('pivot_user_tags table', function (Blueprint $table) { $table->id(); $table->integer('user_id')->unsigned(); $table->integer('tag_id')->unsigned(); $table->timestamps(); });

in app/Nova/User.php fields:
SelectPlus::make('Tags', 'tags', 'App\Models\Tags')->label('title'),

in app/Models/Tags.php

public function tags()
    {
        return $this->belongsToMany('App\Models\Tags', 'pivot_user_tags table', 'user_id', 'tag_id');
    }

when i go to edit user, i get the error: Call to undefined method App \ Models \ Tags:: newModel ()

can you help me?

Implement authorizable

Hello,

Is it possible to implement the Laravel\Nova\Authorizable trait so the attachModelName policy methods are respected? Or implement the authorize function from the BelongsToManyField?

Now I'm able to see all the possible relationship values even if I have no acces to see them.

Thanks in advance!

Not working with UUID's

Hi,
In my relation table I have both origin model and related model identified by UUIDs.

create table `607585c0a06311e9bf656ba9c86393fd`.microregionscities
(
	idmicroregioncities bigint unsigned auto_increment
		primary key,
	idmicroregions char(36) not null,
	idcities char(36) not null,
	created_at timestamp null,
	updated_at timestamp null,
	constraint MicRegsMunics_MicRegs_fk
		foreign key (idmicroregions) references `607585c0a06311e9bf656ba9c86393fd`.microregions(idmicroregions),
	constraint MicRegs_Munics_fk
		foreign key (idcities) references cities (idcities)
);

I'm populating my data from microregions resource and when I do so, idcities always remain empty.

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`607585c0a06311e9bf656ba9c86393fd`.`microregionscities`, CONSTRAINT `MicRegs_Munics_fk` FOREIGN KEY (`idcities`) REFERENCES `redditumdb`.`cities` (`idcities`)) (SQL: insert into `607585c0a06311e9bf656ba9c86393fd`.`microregionscities` (`updated_at`, `created_at`, `idmicroregions`, `idcities`) values (2020-04-20 01:01:25, 2020-04-20 01:01:25, bc06fba0-82a7-11ea-967e-a1507a7c70b6, ))

Thanks!

Feature Request: option to customize the result text

Hi,

I have terms that are written the same, but different id's and in different categories and I would like to be able to distinguish between them. It would be very helpful if you could add something to the result so you can see which one is which.

Doesn't show option titles in list

Options titles are not shown in select list and on new selected item (last item in image).
If I save, then the name of item is shown.
Screenshot 2021-06-21 at 12 10 14 PM

Select the same items several times

Hello. How I can select the same items several times? I need select item "test" several times, If I select, then it disappears a second time, why? How fix it?

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.