Code Monkey home page Code Monkey logo

nova-multiselect-field's Introduction

Nova Multiselect

Latest Version on Packagist Total Downloads

This Laravel Nova package adds a multiselect to Nova's arsenal of fields.

Requirements

  • php: >=8.0
  • laravel/nova: ^4.1

Features

  • Multi- and singleselect with search
  • Asynchronous search
  • Reordering functionality with drag & drop
  • Dependency on other Multiselect instances
  • Distinct values between multiple multiselects
  • Fully compatible with light and dark modes

Screenshots

Detail View

Form View

Installation

Install the package in a Laravel Nova project via Composer:

composer require outl1ne/nova-multiselect-field

Usage

The field is used similarly to Nova's native Select field. The field type in the database should be text-based (ie string, text or varchar), selected values are stored as a stringified JSON array.

use Outl1ne\MultiselectField\Multiselect;

public function fields(Request $request)
{
    return [
      Multiselect::make('Football teams')
        ->options([
          'liverpool' => 'Liverpool FC',
          'tottenham' => 'Tottenham Hotspur',
        ])

        // Optional:
        ->placeholder('Choose football teams') // Placeholder text
        ->max(4) // Maximum number of items the user can choose
        ->saveAsJSON() // Saves value as JSON if the database column is of JSON type
        ->optionsLimit(5) // How many items to display at once
        ->reorderable() // Allows reordering functionality
        ->singleSelect() // If you want a searchable single select field
        ->distinct('football') // Disables values used by other multiselects in same distinct group
        ->taggable() // Possible to add values ("tags") on the fly

        // Async model querying
        Multiselect::make('Artists')
          ->asyncResource(Artist::class),

          // If you want a custom search, create your own endpoint:
          ->api('/api/multiselect/artists?something=false', Artist::class),
    ];
}

Option groups

Option groups are supported. Their syntax is the same as Laravel's option group syntax.

In this example (from Nova docs), all values are grouped by the group key:

->options([
    'MS' => ['label' => 'Small', 'group' => 'Men Sizes'],
    'MM' => ['label' => 'Medium', 'group' => 'Men Sizes'],
    'WS' => ['label' => 'Small', 'group' => 'Women Sizes'],
    'WM' => ['label' => 'Medium', 'group' => 'Women Sizes'],
])

Dependencies

You can make a Multiselect depend on another by using optionsDependOn. The value from the optionsDependOn Multiselect has to be the key to the options and the value must be a key-value dictionary of options as usual.

Usage:

Multiselect::make('Country', 'country')
    ->options([
        'IT' => 'Italy',
        'SG' => 'Singapore',
    ]),

Multiselect::make('Language', 'language')
    ->optionsDependOn('country', [
        'IT' => [
            'it' => 'Italian',
        ],
        'SG' => [
            'en' => 'English',
            'ms' => 'Malay',
            'zh' => 'Chinese',
        ]
    ]),

    // Optionally define max number of values
    ->optionsDependOnMax([
        'IT' => 1,
        'SG' => 3,
    ])

Belongs-To-Many

You can use this field for BelongsToMany relationship selection.

// Add your BelongsToMany relationship to your model:
public function categories()
{
    return $this->belongsToMany(\App\Models\Category::class);
}

// Add the field to your Resource for asynchronous option querying:
Multiselect::make('Categories', 'categories')
  ->belongsToMany(\App\Nova\Resources\Category::class),

// Alternatively, you can set the second argument to 'false' to
// query the options on page load and prevent the user from having
// to first type in order to view the available options. Note: Not
// recommended for unbounded relationship row counts.
Multiselect::make('Categories', 'categories')
  ->belongsToMany(\App\Nova\Resources\Category::class, false),

Options

Possible options you can pass to the field using the option name as a function, ie ->placeholder('Choose peanuts').

Option type default description
options Array|callable [] Options in an array as key-value pairs (['id' => 'value']).
api($path, $resource) String, String (Resource) null URL that can be used to fetch options asynchronously. The search string is provided in the search query parameter. The API must return object containing key-value pairs (['id' => 'value']).
asyncResource($resource) String (Resource) null Provide a Resource class to fetch the options asynchronously.
placeholder String Field name The placeholder string for the input.
max Number Infinite The maximum number of options a user can select.
groupSelect Boolean false For use with option groups - allows the user to select whole groups at once
singleSelect Boolean false Makes the field act as a single select which also means the saved value will not be an array.
saveAsJSON Boolean false When you have a SQL JSON column, you can force the field to save the values as JSON. By default, values are saved as a stringified array.
optionsLimit Number 1000 The maximum number of options displayed at once. Other options are still accessible through searching.
nullable Boolean false If the field is nullable an empty select will result in null else an empty array ([]) is stored.
reorderable Boolean false Enables (or disables) the reordering functionality of the multiselect field.
optionsDependOn String, Array null Determines which Multiselect this field depends on.
belongsToMany String (Resource) null Allows the Multiselect to function as a BelongsToMany field.
belongsTo String (Resource) null Allows the Multiselect to function as a BelongsTo field.
taggable Boolean false Makes the Multiselet to support tags (dynamically entered values).
clearOnSelect Boolean false Clears input after an option has been selected.
distinct String Field Attribute Syncs options between multiple multiselects in the same group and disables the options that have already been used.
indexDelimiter String ', ' Sets delimiter used to join values on index view
indexValueDisplayLimit Number 9999 Define how many values can be displayed at once on index view
indexCharDisplayLimit Number 40 Set char limit for index display value

Localization

The translations file can be published by using the following publish command:

php artisan vendor:publish --provider="Outl1ne\MultiselectField\FieldServiceProvider" --tag="translations"

You can then edit the strings to your liking.

Overwriting the detail field

You can overwrite the detail view value component to customize it as you see fit.

Create a new component for NovaMultiselectDetailFieldValue and register it in your app.js. The component receives two props: field and values. The values prop is an array of selected labels.

// in NovaMultiselectDetailFieldValue.vue

<template>
  <div class="relative rounded-lg bg-white shadow border border-60" v-if="values">
    <div class="overflow-hidden rounded-b-lg rounded-t-lg">
      <div class="border-b border-50 cursor-text font-mono text-sm py-2 px-4" v-for="(value, i) of values" :key="i">
        {{ value }}
      </div>
    </div>
  </div>

  <div v-else></div>
</template>

<script>
export default {
  props: ['field', 'values'],
};
</script>
// in app.js

import NovaMultiselectDetailFieldValue from './NovaMultiselectDetailFieldValue';

Nova.booting((Vue, router, store) => {
  Vue.component('nova-multiselect-detail-field-value', NovaMultiselectDetailFieldValue);
});

Overwriting the form tag field

You can overwrite the tag template in the form-field component to customize it as you see fit.

Create a new component for FormFieldTag and register it in your app.js. The component receives two props: option and remove. The option prop is an object with, for example, the label.

// in FormFieldTag.vue

<template>
  <span class="multiselect__tag">
    <span>{{ option.label.trim() }}</span>
    <i class="multiselect__tag-icon" @click="remove(option)"></i>
  </span>
</template>

<script>
export default {
  props: ['option', 'remove'],
};
</script>
// in app.js

import FormFieldTag from './FormFieldTag';

Nova.booting((Vue, router, store) => {
  Vue.component('form-multiselect-field-tag', FormFieldTag);
});

Credits

License

This project is open-sourced software licensed under the MIT license.

nova-multiselect-field's People

Contributors

alancolant avatar alberto-bottarini avatar ali-shaikh avatar alkrauss48 avatar anand-whatdigital avatar craigharley avatar crynobone avatar dependabot[bot] avatar faridaghili avatar gavro avatar gmedeiros avatar jplhomer avatar kaareloun avatar kropcik avatar lorenzoalu avatar marikamustv avatar marttinnotta avatar mrleblanc101 avatar msnoeren avatar mstyles avatar muhammadsaeedparacha avatar nonovd avatar potentweb avatar shawnheide avatar tarpsvo avatar tim-frensch avatar victorlap avatar wamesro avatar wilfredchen 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

nova-multiselect-field's Issues

JSON column support

With Laravel, arrays are often saved in JSON column type. Can you add support for this?

Thanks!

Overwriting the detail field: Expand docs for a noobie?!

Hi I don't understand the docs fully, I've tried make:component but I think its wrong do I need to create a nova-tool to overwrite the styling via the instructions in your docs?

Please can you elaborate on them a little more so they are a little more beginner friendly

Thanks for the package

Stored value not prefilled when updating resource

Hi,

I have the MultiSelect field setup as followed:

$options = $optionRepository->selectAll()
    ->pluck('name', 'id');

MultiSelect::make('Options')
    ->options($options->toArray())
    ->onlyOnForms(),

This works brilliantly for the create form (values are stored in DB), but it's not prefilling my field in the edit form.
When I check the ajax call, to get the Resource's fields for the edit form, I can see that the MultiSelect field has the correct values. It's just not setting the div element with class multiselect__tags.

Nova version: 2.12.0
optimistdigital/nova-multiselect-field: 1.9.0

Thanking you in advance.

saveAsJson()

Hey thanks for the package

I'm using the latest version of nova 3.3.2 and saveAsJson() is not working how I had expected for a json column.

expected to see {"example_1","example_2} but I get ["example_1","example_2]

Is this expected behaviour?

Also can you please expand on the documentation for "Overwriting the detail field" I have no idea if that should be a new nova-component or within base resources or nova/resources

I've tried the latter 2 but couldn't get it to overwrite so I'm guessing it maybe a nova-component but when setting up a nova-component it usually wants vendor/package not just package?

Does not work on update view on latest Nova version (3.8.2)

Hello!

I am using version 1.9.1 of this package in the context of Nova v3.8.2.

All Multiselect fields I have don't render at all on the update screen. They do show up correctly in the details view though, but it makes me unable to create new resources with required Multiselect fields.

Example implementation:

Multiselect::make("Langues", 'languages')
    ->options(Languages::all())
    ->reorderable()
    ->saveAsJSON()
    ->placeholder('Définir les langues pour cet objet.')
    ->help("Définir les langues pour cet objet.")
    ->hideFromIndex()
    ->rules('required')
    ->updateRules('sometimes')
    ->displayUsing(function ($value) {
        if (is_array($value)) return json_encode($value);
        return $value;
    });

I'm not too sure why this is happening. I get errors in the JS console but they are related to another package (flexible content).

CSS issue

When using in your own nova menu builder as a custom field, the dropdown is totally out. See visibility field in below screenshot.

Capture

Eager Loading is not used and each query is duplicated

tags (categories) for each row is displayed with a query, without an eager loading and each query is also duplicated.

I added relationship name into with array in resource, but nothing is changed. It is tested using belongsToMany relationship.

        Multiselect
            ::make(__('Categories'), 'categories')
            ->options([1 => 'bla', 2 => 'bla bla'])
            ->rules('required'),

Please take a look to attached file, for more info.

2 rows, 4 belonged queries:

Screen Shot 2020-07-15 at 00 19 23

Multiselect open without clicking on it

Hi,
Is there a way to stop the multiselect from opening itself without me click on it? Probably have something with the css class but I'm not sure how can i make the changes and some help would be greate

Default values

I'm trying to set some default values but i dont' get it.

Example:

Multiselect::make('tag')->options([
    1 => 'First',
    2 => 'Second',
    3 => 'Third',
])

->default() method see like doesn't exist in Multiselect
->withMeta(['value' => [1, 2]) i work around withMeta without success.

Some help with it? I looking in closed issues like #37 and #22

Enable single-select or multi select based on condition?

Is there a way to implement the function in case where something is "active" ->singleSelect(true) otherwise singleSelect(false)?

EX)
->dependsOn('is_active')
->singleSelect(function () if('is_active' == 1) return true; else return false))

If it is working as dependsOnOptions, it can't be better

Thanks in advance

translation in different language

Translated all strings to Dutch and put tranlated strings in:

\resources\lang\vendor\nova-multiselect\nl.json

This doesn't work, i must place it in en.json, which is not logical.

Or am i missing something?

Tranlation file:

{
  "novaMultiselect.maxElements": "Selectie van :max item(s) toegestaan.",
  "novaMultiselect.noResult": "Geen resultaten gevonden.",
  "novaMultiselect.noOptions": "Lijst is leeg.",
  "novaMultiselect.limitText": "en :count meer",
  "novaMultiselect.selectLabel": "Druk enter om te selecteren",
  "novaMultiselect.selectGroupLabel": "Druk enter om groep te selecteren",
  "novaMultiselect.selectedLabel": "Geselecteerd",
  "novaMultiselect.deselectLabel": "Druk enter om te verwijderen",
  "novaMultiselect.deselectGroupLabel": "Druk enter om groep te verwijderen",
  "novaMultiselect.placeholder": "Selecteer optie(s)",
  "novaMultiselect.nItemsSelected": ":count items geselecteerd"
}

Translate strings

Is it possible to make custom translations for strings like:
'Press enter to select'
'Selected'
'Press enter remove'
etc.

Provide default values

It will be great if we can provide a default. Iike i pass in everything I passed in to options to select all.

Option group support

Would it be possible to have this support <optgroups>, which would bring parity with the native select field?

Thank you!

null as string

An empty value is stored as a "null" string. Regardless of the nullable () method

Dutch translation

If you want to add the Dutch translation, here is my translation:

{
  "novaMultiselect.maxElements": "Maximaal :max toegestaan.",
  "novaMultiselect.noResult": "Geen resultaten gevonden.",
  "novaMultiselect.noOptions": "Lijst is leeg.",
  "novaMultiselect.limitText": "en :count meer",
  "novaMultiselect.selectLabel": "Druk enter om te selecteren",
  "novaMultiselect.selectGroupLabel": "Druk enter om groep te selecteren",
  "novaMultiselect.selectedLabel": "Geselecteerd",
  "novaMultiselect.deselectLabel": "Druk enter om te verwijderen",
  "novaMultiselect.deselectGroupLabel": "Druk enter om groep te verwijderen",
  "novaMultiselect.placeholder": "Selecteer optie(s)",
  "novaMultiselect.nItemsSelected": ":count items geselecteerd",
  "novaMultiselect.reorder": "Ordenen",
  "novaMultiselect.doneReordering": "Ordenen afgerond"
}

For other users: file can be placed in \resources\lang\vendor\nova-multiselect-field

[FEATURE REQUEST ]Order group options

Hi there! Great package!

Would be nice if we could define if the groups could be ordered by asc/desc order.

Do you think this could be a nice improvement to the package?

Doesn't work as pivot field

Thanks for this tool!

When using it as a pivot field in a BelongsToMany relation, the values are stored correctly - but unfortunately not shown in the index or detail view after saving.

Best,
Sebastian

BelongsToMany does not store order

Hi,

First of all, thanks for the work on this package.

I've got belongsToMany working, the only thing missing is the order of the items. Changing them after the initial save does not work. Is there a column or something on the pivot table that I should add? Or is this functionality not available for the belongsToMany?

Regards,
Joost.

Composer require not working

In Nova.php line 706:
                                       
  Method Translations does not exist.

When I try to install the package, I got this error while @php artisan package:discover --ansi
Would you please take care of this and help me what's wrong?

Does not save json in nova menu builder

Here is my code in nova menu builder

public static function getFields(): array
    {
        return [
            Multiselect::make('Visibility', 'role')
                ->options(\App\Role::all()->except(1)->pluck('label', 'name')->toArray())
                ->saveAsJSON(true)
        ];
    } 

When update menu item it saves {"data": null, "role": null} empty json object like this into the data column in nova_menu_menu_items table.

TypeError: Cannot read property 'toUpperCase' of undefined

All of my multiselects have stopped working, displaying an error in the console. Using the example code produces the same error.

nova-multiselect-field version: 1.9.1
Nova version: 3.8.2
PHP version: 7.4.3
Laravel version: 7.10.3

image

image

custom templates

Hello, just wondering if its possible to add custom templates to the package? https://vue-multiselect.js.org/#sub-custom-option-template.

I'm no sure if it helps but as a starting point what if users could do something like this:

// items from db
$items = Items::get()->keyBy('id'); //get all data and use their id's as keys (used when storing selected items in the db).
//field
Multiselect::make('My Multiselect')
    ->options($items)
    ->placeholder('Choose')
    ->template(view('mytemplate')->render()),  //new template option

//mytemplate.blade.php
<template slot="option" slot-scope="props">
  <div>
  	<span class="title">@{{ props.option.title }}</span>
  	<span class="small">@{{ props.option.desc }}</span>
  </div>
</template>   
//FormField.vue
<multiselect
	@input="handleChange"
	@open="() => repositionDropdown(true)"
	...
	...
>
	{{ field.template }}
</multiselect>

Thanks

Issue Mehotd Translations does not exist

i installed 1.1 few weeks ago , and it was working fine.
Yesterday i deed composer update and it updated to version 1.3.2 but nova stoped working with the following error.

BadMethodCallException Method translations does not exist

until i had to remove the pakcage then my laravel nova started to work again.

kind you please look into fixing it... ?

BelongsToMany options not working

I'm on Nova 3.12.1 and I need to overwrite the fillUsing you have on the belongsToMany function cause the callback method you use is returning a callable when it should not.

When I replace fillUsing like this (remove the return function) it all work fine:

->fillUsing(function ($request, $model, $requestAttribute, $attribute) {
//return function () use ($request, $model, $attribute) {     <-- removing this
    // Validate
    if (!is_callable([$model, $attribute])) {
        throw new RuntimeException("{$model}::{$attribute} must be a relation method.");
    }

    $relation = $model->{$attribute}();

    if (!method_exists($relation, 'sync')) {
        throw new RuntimeException("{$model}::{$attribute} does not appear to model a BelongsToMany or MorphsToMany.");
    }

    // Sync
    $relation->sync($request->get($attribute) ?? []);
//};  <-- And removing this (of course)
})

Unexpected token and property errors

Hello, hoping you can help. When creating a field with the following code:

            Multiselect::make('Sectors', 'sectors')
                ->rules('required')
                ->options([
                    'Option1' => 'Option1',
                    'Option2' => 'Option2',
                    'Option3' => 'Option3',
                ])
                ->placeholder('Choose')
                ->max(5),

I receive a console error on page load:

SyntaxError: Unexpected token e in JSON at position 0
    at JSON.parse 

If I proceed to click save and continue anyway I then get a new error:

TypeError: Cannot read property 'status' of undefined

I cleared my browser cache, Laravel cache and also disabled all the other fields but the error remains. As soon as I remove the multiselect the page starts to load and save correctly again.

Any ideas on what could be causing this?

Options Group: Visual

Great package!
Currently the Option Groups are helpful for selecting values, but afterwards it is not possible to distinguish them:
edit?viaResource= viaResourceId= viaRelationship= 2020-03-25 12-22-13

Would be great to have visual support for the selected options.

using dependsOn with belongsToMany hides the field

I am looking to use the dependsOn feature with the belongsToMany feature. Essentially we have groups of categories that we want to show depending on another field. Right now if we try to use these two features together nothing shows up.

Multiselect::make('Topics', 'topics')
  ->dependsOn('teachable_type')
  ->dependsOnOptions([
     'activity' => $activity,
     'skill'    => $skill,
     'tls'      => $tls,
  ])
  ->belongsToMany('App\Nova\Category')
  ->hideFromIndex()
  ->nullable(),

Save Additional Information

Hi,

This tool is great. it works as expected with simple key value options; however is it possible to store additional information with the request?

I have a belongsToManyThrough in which i am using this tool to populate a BelongsToMany field. The issue i am having is that i have a singular table, say attributes, that stores the 2 primary keys and a third enum field. An example is product categories, in the attributes table i store the product_id, category_id and type (enum, value = 'category')

When doing this with this package i find that the type field always defaults to the default type - i need to be able to set the type for each multiselect, of which there are many. Is this possible?

Weird Encoding Issues

For some reason i have to use this workaround with fillUsing:

            Multiselect::make('Robots', 'robots')->options([
                'index' => 'index',
                'follow' => 'follow',
                'noindex' => 'noindex',
                'nofollow' => 'nofollow',
            ])->nullable()->fillUsing(
                fn (NovaRequest $request, \App\Models\Eloquent\Seo $seo) => $seo->robots = $request->input('robots', [])
            )->default(['index', 'follow']),

Model:

    public $casts = [
        'robots' => 'array',
    ];

is this supposed to work out of the box?

Save multi-selected values as native array

Can we save multi-selected values as native array in database? Because most of the time we will use casting in Model with array conversion. Adding quote ("") around the values triggers error while looping.

Thanks

Null values being saved as 'null' string

For nullable fields which do not saveAsJSON, if the value is null it was being encoded and turned into a 'null' string. I am using the latest version (1.9.0). This issue was reported previously but was closed (#30), so I'm recreating it (feel free to close this one if we don't want a duplicate). I've submitted a fix here: #48

Originally posted by @mstyles in #30 (comment)

Feature Request - valueAsHtml() for options value

I would like to have a feature request - valueAsHtml()

I hope there will be a method like valueAsHtml() to let me inject some HTML to the value.
So that I can make some image or link elements to the value, then user can select/view our values with thumbnail.
I hope I can doing this with a simple injecting value method instead of overriding the vue.

Appreciate if there will be a option to let {{ value }} become {!! value !!}

This package is great!

French multiselect

Hi after upgrading to the latest version I noticed that all my select fields turned into French by default...

Is there a way to have default options when your are using dependsOn method

Hi Guys,
Thank you for this wonderful package. It really is a great one.

I would like to know if its possible to have default options when you're using the dependsOn method.

        `Multiselect::make('Report Year', 'report_year')
            ->options(array_combine(range(date("Y"), 2010), range(date("Y"), 2010)))
            ->singleSelect()
            ->rules('required')
            ->onlyOnForms(),`

        `Multiselect::make('Report Month', 'report_month')
            ->dependsOn('report_year')
            ->dependsOnOptions([
                date("Y") => $this->months(),
               ])
            ->default($this->allMonths()). // something like this but its not working
            ->singleSelect()
            ->rules('required'),`

I would like to show only months that have passed in the current Year and then all months for the previous years.
Is this possible?
Thanks.

'e.toUppercase is not a function'

Hello,

Since 1.9.1 I'm getting 'e.toUppercase is not a function', even though there is no MultiSelect field rendered on the current page.

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.