Skip to content

Commit

Permalink
Fixed a JS Error, and added option caching
Browse files Browse the repository at this point in the history
  • Loading branch information
Stowez committed Nov 5, 2024
1 parent de8e78d commit fc3d4ff
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 33 deletions.
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## 1.1.0 - 2024-11-05

- Fixed javascript error when inside a flexible componet
- Any Ajax Request results are now stored in a cached variable so label isn't lost on search
- Use Nova Placeholder before name on search input

## 1.0.1 - 2022-05-11

- Added filterable option flag, contributed by https://github.com/nea
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "razorcreations/ajax-field",
"description": "A searchable AJAX powered field for Laravel Nova.",
"version": "1.1.0",
"authors": [
{
"name": "Ross Cooper",
Expand Down
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ For Nova 3 support use
## Usage

```php

use Razorcreations\AjaxField\AjaxField;

// Inside your resources fields definition
AjaxField::make('Foo')->setUrl('/api/ajaxselect/foo'),

Expand Down
88 changes: 55 additions & 33 deletions resources/js/components/FormField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
v-model="value"
class="w-full form-control form-input form-input-bordered"
:class="errorClasses"
:placeholder="field.name"
:placeholder="field.placeholder || field.name"
:options="availableOptions"
:label="labelKey"
:multiple="field.multiple"
Expand All @@ -30,16 +30,16 @@ import _ from 'lodash';
import { isArray } from 'util';
export default {
components: {
VueSelect,
},
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data () {
data() {
return {
optionsCache: [],
options: [],
loading: false,
labelKey: 'label',
Expand All @@ -52,16 +52,20 @@ export default {
computed: {
parentComponent() {
if(!this.field.parent_field) {
if (!this.field.parent_field) {
return false;
}
let targetField = this.field.parent_field;
let currentField = this.field.attribute;
let currentField = this.field.attribute;
// If component is inside a flexible, key is prefixed with an id
if( currentField.indexOf('__') ) {
targetField = currentField.substr(0, currentField.indexOf('__')) + '__' + targetField;
if (currentField.indexOf('__')) {
targetField = currentField.substr(0, currentField.indexOf('__')) + '__' + targetField;
}
if (typeof this.$parent.$children === 'undefined') {
return;
}
// Find the component the parent value references
Expand All @@ -71,14 +75,14 @@ export default {
})
},
availableOptions () {
return _.uniq(this.options.concat(this.selectedOptions), 'value');
availableOptions() {
return _.uniqBy(this.options.concat(this.selectedOptions), 'value');
},
},
mounted () {
mounted() {
this.parseInitialValue();
if(this.parentComponent) {
if (this.parentComponent) {
this.parentComponent.$watch('value', (value) => {
this.parentVal = value;
this.prepareField();
Expand All @@ -91,17 +95,17 @@ export default {
methods: {
prepareField() {
//Check whether any filterable data was set, otherwise stay true
if(this.field.filterable !== 'undefined') {
if (typeof this.field.filterable !== 'undefined') {
this.filterable = this.field.filterable;
}
// If field is not responsive, load initial options
if(!this.field.responsive) {
if (!this.field.responsive) {
return this.loadInitialOptions();
}
// If field is responsive, do we have any initial values
if(this.field.value ) {
if (this.field.value) {
return this.loadInitialOptions(this.field.value);
}
},
Expand All @@ -122,17 +126,18 @@ export default {
/*
* Load initial Options
*/
loadInitialOptions (value) {
*/
loadInitialOptions(value) {
let url = this.buildParamString(null, value);
if(Array.isArray(value) && value.length === 1 && !value[0]) {
if (Array.isArray(value) && value.length === 1 && !value[0]) {
this.value = [];
return;
}
window.Nova.request().get( url ).then(({data}) => {
window.Nova.request().get(url).then(({ data }) => {
this.options = data;
this.cacheOptions(data);
this.options.forEach(option => {
if (isArray(this.value)) {
Expand All @@ -147,26 +152,39 @@ export default {
this.selectedOptions.push(option);
}
})
});
}).catch(error => {
console.error('Error loading initial options:', error);

Check failure on line 156 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs

Check failure on line 156 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs

Check failure on line 156 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs

Check failure on line 156 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs
});

Check failure on line 157 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs

Check failure on line 157 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs

Check failure on line 157 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs

Check failure on line 157 in resources/js/components/FormField.vue

View workflow job for this annotation

GitHub Actions / npm-ci

Mixed spaces and tabs
},
/*
* Dynamic search with the input value
*/
*/
search: window._.debounce((loading, searchVal, vm) => {
let url = vm.buildParamString(searchVal)
window.Nova.request().get( url ).then(({data}) => {
window.Nova.request().get(url).then(({ data }) => {
vm.options = data;
vm.cacheOptions(data);
loading(false);
});
}, 350),
/*
* Cache options to optionsCache
*/
cacheOptions(options) {
options.forEach(option => {
if (!this.optionsCache.find(o => o.value === option.value)) {
this.optionsCache.push(option);
}
});
},
/*
* When multiselect input changes, determine if ready to query
*/
inputChange (input, loading) {
if( input.length < 3 && !/^\d+$/.test(input)) {
inputChange(input, loading) {
if (input.length < 3 && !/^\d+$/.test(input)) {
return;
}
loading(true);
Expand All @@ -183,15 +201,15 @@ export default {
let params = {}
let url = this.field.url;
if(this.parentVal) {
if (this.parentVal) {
params[this.field.parent_field] = this.parentVal;
}
if(this.field.responsive && searchVal) {
if (this.field.responsive && searchVal) {
params.search = searchVal
}
if(fieldVal) {
if (fieldVal) {
params.value = fieldVal
}
Expand All @@ -200,7 +218,7 @@ export default {
return url = url + '?' + paramString;
},
parseInitialValue () {
parseInitialValue() {
let value = this.field.value ? this.field.value : null;
if (!value) {
this.value = value;
Expand Down Expand Up @@ -237,12 +255,16 @@ export default {
if (!v) {
return;
}
const selectedOption = this.options.find(option => option.value === v);
this.selectedOptions.push(selectedOption);
const selectedOption = this.optionsCache.find(option => option.value === v);
if (selectedOption) {
this.selectedOptions.push(selectedOption);
}
});
} else {
const selectedOption = this.options.find(option => option.value === value);
this.selectedOptions.push(selectedOption);
const selectedOption = this.optionsCache.find(option => option.value === value);
if (selectedOption) {
this.selectedOptions.push(selectedOption);
}
}
}
},
Expand All @@ -257,4 +279,4 @@ export default {
.vs__dropdown-toggle {
border-color: transparent !important;
}
</style>
</style>

0 comments on commit fc3d4ff

Please sign in to comment.