├── version.txt
├── dist
├── css
│ └── field.css
├── mix-manifest.json
└── js
│ └── field.js.LICENSE.txt
├── resources
├── css
│ └── field.css
└── js
│ ├── components
│ ├── IndexField.vue
│ ├── DetailField.vue
│ └── FormField.vue
│ └── field.js
├── .gitignore
├── .eslintrc.js
├── webpack.mix.js
├── .github
└── workflows
│ └── ci.yml
├── src
├── FieldServiceProvider.php
└── AjaxField.php
├── .php-cs-fixer.dist.php
├── package.json
├── composer.json
├── changelog.md
└── readme.md
/version.txt:
--------------------------------------------------------------------------------
1 | 1.3.2
2 |
--------------------------------------------------------------------------------
/dist/css/field.css:
--------------------------------------------------------------------------------
1 | .dark .vs--single .vs__selected{color:#0ca5e9!important}
2 |
--------------------------------------------------------------------------------
/dist/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/js/field.js": "/js/field.js",
3 | "/css/field.css": "/css/field.css"
4 | }
5 |
--------------------------------------------------------------------------------
/resources/css/field.css:
--------------------------------------------------------------------------------
1 | /* Nova Field CSS */
2 | .dark .vs--single .vs__selected {
3 | color: #0ca5e9 !important;;
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /vendor
3 | /node_modules
4 | package-lock.json
5 | composer.phar
6 | composer.lock
7 | phpunit.xml
8 | .phpunit.result.cache
9 | .DS_Store
10 | Thumbs.db
11 |
--------------------------------------------------------------------------------
/resources/js/components/IndexField.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ field.value }}
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/resources/js/components/DetailField.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: [
4 | "eslint:recommended",
5 | "plugin:vue/essential"
6 | ],
7 | "globals": {
8 | "Nova": true,
9 | "store": true,
10 | },
11 | rules: {
12 | // override/add rules settings here...
13 | "indent": ["error", "tab"]
14 | },
15 | env: {
16 | node: true,
17 | },
18 | }
--------------------------------------------------------------------------------
/resources/js/field.js:
--------------------------------------------------------------------------------
1 | import IndexField from './components/IndexField'
2 | import DetailField from './components/DetailField'
3 | import FormField from './components/FormField'
4 |
5 | Nova.booting((app) => {
6 | app.component('IndexAjaxField', IndexField)
7 | app.component('DetailAjaxField', DetailField)
8 | app.component('FormAjaxField', FormField)
9 | })
10 |
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | let mix = require('laravel-mix')
2 |
3 | mix.setPublicPath('dist')
4 | .js('resources/js/field.js', 'js')
5 | .css('resources/css/field.css', 'css')
6 | .vue({version: 3})
7 | .webpackConfig({
8 | externals: {
9 | vue: 'Vue',
10 | },
11 | output: {
12 | uniqueName: 'spatie/nova-ajax-field',
13 | },
14 | resolve: {
15 | symlinks: false
16 | }
17 | })
18 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI Checks
2 |
3 | on: [push]
4 |
5 | jobs:
6 | php-ci:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v1
11 | - name: Install depenedencies
12 | uses: php-actions/composer@v1
13 | - name: Run linter
14 | run: composer run lint
15 |
16 | npm-ci:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v1
20 | - name: Use Node.js
21 | uses: actions/setup-node@v3
22 | - name: Install depenedencies
23 | run: npm install
24 | - name: Run linter
25 | run: npm run lint
26 | - name: Build assets
27 | run: npm run prod
28 |
--------------------------------------------------------------------------------
/src/FieldServiceProvider.php:
--------------------------------------------------------------------------------
1 | setRules([
6 | '@Symfony' => true,
7 | '@PHP71Migration' => true,
8 | 'array_syntax' => ['syntax' => 'short'],
9 | 'method_chaining_indentation' => true,
10 | 'no_useless_else' => true,
11 | 'no_useless_return' => true,
12 | 'ordered_class_elements' => true,
13 | 'ordered_imports' => true,
14 | 'phpdoc_order' => true,
15 | 'strict_comparison' => true,
16 | 'strict_param' => true,
17 | 'yoda_style' => false,
18 | 'full_opening_tag' => true,
19 | 'indentation_type' => true,
20 | ])
21 | ->setIndent("\t")
22 | ->setLineEnding("\n")
23 | ->setRiskyAllowed(true)
24 | ->setFinder(
25 | PhpCsFixer\Finder::create()
26 | ->in([
27 | 'src',
28 | ])
29 | );
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "npm run development",
5 | "development": "mix",
6 | "watch": "mix watch",
7 | "watch-poll": "mix watch -- --watch-options-poll=1000",
8 | "hot": "mix watch --hot",
9 | "prod": "npm run production",
10 | "production": "mix --production",
11 | "fix": "eslint --fix --ext .js,.vue resources",
12 | "lint": "eslint --ext .js,.vue resources"
13 | },
14 | "devDependencies": {
15 | "cross-env": "^5.0.0",
16 | "eslint": "^7.3.1",
17 | "eslint-plugin-vue": "^7.0.0-alpha.8",
18 | "laravel-mix": "^6.0",
19 | "laravel-nova": "^1.0",
20 | "vue-loader": "^16.8.3"
21 | },
22 | "dependencies": {
23 | "lodash": "^4.17.21",
24 | "vue": "^3.0",
25 | "vue-select": "^4.0.0-beta.3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/dist/js/field.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | * Determine if an object is a Buffer
3 | *
4 | * @author Feross Aboukhadijeh
5 | * @license MIT
6 | */
7 |
8 | /**
9 | * @license
10 | * Lodash
11 | * Copyright JS Foundation and other contributors
12 | * Released under MIT license
13 | * Based on Underscore.js 1.8.3
14 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
15 | */
16 |
17 | /**
18 | * @license
19 | * Lodash
20 | * Copyright OpenJS Foundation and other contributors
21 | * Released under MIT license
22 | * Based on Underscore.js 1.8.3
23 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
24 | */
25 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "razorcreations/ajax-field",
3 | "description": "A searchable AJAX powered field for Laravel Nova 3 and 4.",
4 | "version": "1.3.2",
5 | "authors": [
6 | {
7 | "name": "Ross Cooper",
8 | "email": "ross@razorcreations.com",
9 | "homepage": "http://www.razorcreations.com",
10 | "role": "Developer"
11 | },
12 | {
13 | "name": "Tom Stowe",
14 | "email": "tom@razorcreations.com",
15 | "homepage": "http://www.razorcreations.com",
16 | "role": "Developer"
17 | },
18 | {
19 | "name": "Gary Garside",
20 | "email": "gary@razorcreations.com",
21 | "homepage": "http://www.razorcreations.com",
22 | "role": "Developer"
23 | }
24 | ],
25 | "keywords": [
26 | "laravel",
27 | "nova",
28 | "field",
29 | "ajax"
30 | ],
31 | "license": "MIT",
32 | "require": {
33 | "php": "^7.3|^8.0"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Razorcreations\\AjaxField\\": "src/"
38 | }
39 | },
40 | "extra": {
41 | "laravel": {
42 | "providers": [
43 | "Razorcreations\\AjaxField\\FieldServiceProvider"
44 | ]
45 | }
46 | },
47 | "config": {
48 | "sort-packages": true
49 | },
50 | "minimum-stability": "dev",
51 | "prefer-stable": true,
52 | "require-dev": {
53 | "friendsofphp/php-cs-fixer": "^3.8"
54 | },
55 | "scripts": {
56 | "fix": "./vendor/bin/php-cs-fixer fix",
57 | "lint": "./vendor/bin/php-cs-fixer fix --dry-run"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ## 1.3.1 - 2024-11-14
10 |
11 | - Fixed the #0f0 green
12 | - Correctly fire the parent field observer
13 |
14 | ## 1.3.0 - 2024-11-14
15 |
16 | - Fixed selected parent option not being passed correctly to the ajax endpoint
17 | - Added Custom stylesheet to help dark mode conflicts
18 |
19 | ## 1.2.3 - 2024-11-06
20 |
21 | - Updated dist bundle
22 |
23 | ## 1.2.2 - 2024-11-06
24 |
25 | - Fix to vue-select 3.x returning an instance of the event on change, rather than the updated value
26 | - `root` declaration on eslint config to fix deprecation warnings
27 |
28 | ## 1.2.1 - 2024-11-05
29 |
30 | - Remove Nova secrets from being required in the CI runner action during Composer setup
31 |
32 | ## 1.2.0 - 2024-11-05
33 |
34 | - Fixed javascript error when inside a flexible componet
35 | - Any Ajax Request results are now stored in a cached variable so label isn't lost on search
36 | - Use Nova Placeholder before name on search input
37 |
38 | ## 1.0.1 - 2022-05-11
39 |
40 | - Added filterable option flag, contributed by https://github.com/nea
41 |
42 | ## 1.0.0 - 2022-04-07
43 |
44 | - Update to support Nova 4.0
45 | ## 0.3.5 - 2020-08-18
46 |
47 | - Fixed bug where if the inital value was an array the options array would not be populated correctly
48 |
49 | ## 0.3.4 - 2020-08-18
50 |
51 | - Rebuilt assets
52 |
53 | ## 0.3.3 - 2020-08-18
54 |
55 | - Removed some left over code
56 |
57 | ## 0.3.2 - 2020-07-09
58 |
59 | - Fixed an empty string value being selected with multi method
60 |
61 | ## 0.3.1 - 2020-07-09
62 |
63 | - Fixed responsive multiselect fields losing their values on search
64 |
65 | ## 0.3.0 - 2020-07-02
66 |
67 | - Add new responsive() method that will trigger ajax select on text change
68 |
69 | ## 0.2.3 - 2020-07-01
70 |
71 | - Dist files updated
72 |
73 | ## 0.2.2 - 2020-07-01
74 |
75 | - Fixed bug with passing params in get request
76 |
77 | ## 0.2.1 - 2020-07-01
78 |
79 | - Add ability to add a parent field reference to pass through filter to ajax endpoint
80 |
81 | ## 0.2.0 - 2020-06-30
82 |
83 | - Added the ability to enable multiselect.
84 | - Fixed `labelKey` missing in vue component.
85 |
86 | ## 0.1.0 - 2020-06-30
87 |
88 | - Initial release.
89 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # AJAX Field for Laravel Nova
2 |
3 | [](//packagist.org/packages/razorcreations/ajax-field) [](//packagist.org/packages/razorcreations/ajax-field) [](//packagist.org/packages/razorcreations/ajax-field) [](//packagist.org/packages/razorcreations/ajax-field)
4 |
5 | 
6 |
7 | ## Installation
8 |
9 | `composer require razorcreations/ajax-field`
10 |
11 | For Nova 3 support use
12 |
13 | `composer require razorcreations/ajax-field@0.3.2`
14 |
15 | ## Usage
16 |
17 | ```php
18 |
19 | use Razorcreations\AjaxField\AjaxField;
20 |
21 | // Inside your resources fields definition
22 | AjaxField::make('Foo')->setUrl('/api/ajaxselect/foo'),
23 |
24 | // If you are using integers or floats for the values ensure to chain on the type methods...
25 | AjaxField::make('Foo')->setUrl('/api/ajaxselect/foo')->typeInt(),
26 | ```
27 |
28 | The field expects the response from the AJAX call to respond with a JSON array of options in the following format:
29 | ```json
30 | [
31 | {
32 | "label": "Bob",
33 | "value": 1,
34 | },
35 | {
36 | "label": "Jenny",
37 | "value": 2,
38 | }
39 | ]
40 | ```
41 | If you wish you can override the default keys of "value" and "label" using the following methods:
42 | ```php
43 | AjaxField::make('Foo')->setUrl('/api/ajaxselect/foo')->setValueKey('id')->setLabelKey('name'),
44 | ```
45 |
46 | You can pass through another Nova fields value by adding the parent method with the key of the nova field
47 | ```php
48 | // Create a parent field
49 | Text::make('Foo', 'foo');
50 |
51 | // Create ajax field, with parent method
52 | AjaxField::make('Bar')->setUrl('/api/ajaxselect/foo')->parent('foo'),
53 | ```
54 | This will hit the AjaxUrl with the fields key value pair appended as a get param e.g. /api/ajaxselect/foo?foo=value
55 |
56 | Rather than having the options loaded in once on page load, you can use ->responsive() to pass through the field value when the input value changes
57 | ```php
58 | AjaxField::make('Foo')->setUrl('/api/ajaxselect/foo')->responsive(),
59 | ```
60 |
61 |
62 | ## Contributing
63 |
64 | If you would like to contribute please fork the project and submit a PR.
65 |
66 | ### Coding Standards
67 |
68 | - `composer run fix` to fix any PHP linting issues automatically
69 | - `composer run lint` to show any PHP linting issues
70 | - `npm run fix` to fix any JS/Vue linting issue automatically
71 | - `npm run lint` to show any JS/Vue linting issues
--------------------------------------------------------------------------------
/src/AjaxField.php:
--------------------------------------------------------------------------------
1 | withMeta([
31 | 'url' => $url,
32 | ]);
33 | }
34 |
35 | /**
36 | * Sets the key of the selected option that should be saved as the value, default is "value".
37 | */
38 | public function setValueKey(string $key): self
39 | {
40 | return $this->withMeta([
41 | 'valueKey' => $key,
42 | ]);
43 | }
44 |
45 | /**
46 | * Sets the key of the selected option that should be displayed, default is "label".
47 | */
48 | public function setLabelKey(string $key): self
49 | {
50 | return $this->withMeta([
51 | 'labelKey' => $key,
52 | ]);
53 | }
54 |
55 | /**
56 | * Sets the type of the value being save to an integer.
57 | */
58 | public function typeInt(): self
59 | {
60 | return $this->setType('int');
61 | }
62 |
63 | /**
64 | * Sets the type of the value being save to a float.
65 | */
66 | public function typeFloat(): self
67 | {
68 | return $this->setType('float');
69 | }
70 |
71 | /**
72 | * Enable multi-select.
73 | *
74 | * Note: values are saved comma seperated
75 | */
76 | public function multiple(): self
77 | {
78 | return $this->withMeta([
79 | 'multiple' => true,
80 | ]);
81 | }
82 |
83 | /*
84 | * Adds a param to the ajax call so it can pass through a value from another field
85 | */
86 | public function parent(string $attribute): self
87 | {
88 | return $this->withMeta([
89 | 'parent_field' => $attribute,
90 | ]);
91 | }
92 |
93 | /*
94 | * Ajax request to be on text input as opposed to single intitial request
95 | */
96 | public function responsive(): self
97 | {
98 | return $this->withMeta([
99 | 'responsive' => true,
100 | 'filterable' => false, // @see https://vue-select.org/guide/ajax.html#disabling-filtering
101 | ]);
102 | }
103 |
104 | /**
105 | * When loading server side options, it can be useful to disable the client side filtering. Use the filterable prop to disable filtering.
106 | *
107 | * @see https://vue-select.org/guide/ajax.html#disabling-filtering
108 | */
109 | public function filterable(bool $filterable = true): self
110 | {
111 | return $this->withMeta([
112 | 'filterable' => $filterable,
113 | ]);
114 | }
115 |
116 | private function setType(string $type): self
117 | {
118 | return $this->withMeta([
119 | 'type' => $type,
120 | ]);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/resources/js/components/FormField.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
21 |
22 |
23 |
24 |
25 |
300 |
301 |
--------------------------------------------------------------------------------