Просмотр исходного кода

#7123: Handle empty_option on API Select

thatmattlove 4 лет назад
Родитель
Сommit
ddff193786

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js.map


+ 21 - 15
netbox/project-static/src/select/api/apiSelect.ts

@@ -22,13 +22,6 @@ import type { Stringifiable } from 'query-string';
 import type { Option } from 'slim-select/dist/data';
 import type { Trigger, PathFilter, ApplyMethod, QueryFilter } from './types';
 
-// Empty placeholder option.
-const PLACEHOLDER = {
-  value: '',
-  text: '',
-  placeholder: true,
-} as Option;
-
 // Attributes which if truthy should render the option disabled.
 const DISABLED_ATTRIBUTES = ['occupied'] as string[];
 
@@ -52,6 +45,8 @@ export class APISelect {
    */
   public readonly placeholder: string;
 
+  public readonly emptyOption: Nullable<Option> = null;
+
   /**
    * Event that will initiate the API call to NetBox to load option data. By default, the trigger
    * is `'load'`, so data will be fetched when the element renders on the page.
@@ -147,7 +142,7 @@ export class APISelect {
   /**
    * This instance's available options.
    */
-  private _options: Option[] = [PLACEHOLDER];
+  private _options: Option[] = [];
 
   /**
    * Array of options values which should be considered disabled or static.
@@ -168,6 +163,14 @@ export class APISelect {
       this.preSorted = true;
     }
 
+    const emptyOption = base.getAttribute('data-empty-option');
+    if (isTruthy(emptyOption)) {
+      this.emptyOption = {
+        text: emptyOption,
+        value: '',
+      };
+    }
+
     if (hasUrl(base)) {
       const url = base.getAttribute('data-url') as string;
       this.url = url;
@@ -285,17 +288,16 @@ export class APISelect {
     // Get the placeholder index (note: if there is no placeholder, the index will be `-1`).
     const placeholderIdx = deduplicated.findIndex(o => o.value === '');
 
-    if (hasPlaceholder && placeholderIdx < 0) {
+    if (hasPlaceholder && placeholderIdx < 0 && this.emptyOption !== null) {
       // If there is a placeholder but it is not the first element (due to sorting or other merge
       // issues), remove it from the options array and place it in front.
       deduplicated.splice(placeholderIdx);
-      deduplicated = [PLACEHOLDER, ...deduplicated];
+      deduplicated = [this.emptyOption, ...deduplicated];
     }
-    if (!hasPlaceholder) {
+    if (!hasPlaceholder && this.emptyOption !== null) {
       // If there is no placeholder, add one to the front of the array.
-      deduplicated = [PLACEHOLDER, ...deduplicated];
+      deduplicated = [this.emptyOption, ...deduplicated];
     }
-
     this._options = deduplicated;
     this.slim.setData(deduplicated);
   }
@@ -304,7 +306,11 @@ export class APISelect {
    * Remove all options and reset back to the generic placeholder.
    */
   private resetOptions(): void {
-    this.options = [PLACEHOLDER];
+    if (this.emptyOption !== null) {
+      this.options = [this.emptyOption];
+    } else {
+      this.options = [];
+    }
   }
 
   /**
@@ -534,7 +540,7 @@ export class APISelect {
    */
   private async getOptions(action: ApplyMethod = 'merge'): Promise<void> {
     if (this.queryUrl.includes(`{{`)) {
-      this.options = [PLACEHOLDER];
+      this.resetOptions();
       return;
     }
     await this.fetchOptions(this.queryUrl, action);

+ 5 - 2
netbox/utilities/forms/fields.py

@@ -376,7 +376,7 @@ class DynamicModelChoiceMixin:
     widget = widgets.APISelect
 
     def __init__(self, query_params=None, initial_params=None, null_option=None, disabled_indicator=None, fetch_trigger=None,
-                 *args, **kwargs):
+                 empty_label=None, *args, **kwargs):
         self.query_params = query_params or {}
         self.initial_params = initial_params or {}
         self.null_option = null_option
@@ -386,11 +386,14 @@ class DynamicModelChoiceMixin:
         # to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference
         # by widget_attrs()
         self.to_field_name = kwargs.get('to_field_name')
+        self.empty_option = empty_label or ""
 
         super().__init__(*args, **kwargs)
 
     def widget_attrs(self, widget):
-        attrs = {}
+        attrs = {
+            'data-empty-option': self.empty_option
+        }
 
         # Set value-field attribute if the field specifies to_field_name
         if self.to_field_name:

Некоторые файлы не были показаны из-за большого количества измененных файлов