Handsontable中的cell editor(2023-11-16)

本文主要是對handsontable官網(wǎng)中cell editor的簡單翻譯,僅供參考。
原文地址:https://handsontable.com/docs/javascript-data-grid/cell-editor/

editor簡述

cell editor指的是單元格編輯器函數(shù)。handsontable將展示單元格的值的過程與改變單元格的值的過程分開,Renderers(渲染器)主要責任是呈現(xiàn)數(shù)據(jù),Editor(編輯器)主要是修改數(shù)據(jù)。因為Renderers只有一個簡單的任務:獲取單元格的實際值并以HTML代碼的形式返回它的表示,所以它們可以是一個函數(shù)。然而,Editor需要做這些工作:處理用戶的輸入(鼠標、鍵盤事件),驗證數(shù)據(jù),并且根據(jù)驗證結(jié)果進行不同的展示,如果將這些功能都放到同一個函數(shù)中顯然不是很合理,所以在handsontable中編輯器是通過編輯器類(editor classes)來描述。

EditorManager

EditorManager是一個負責處理Handsontable中可用的所有編輯器的類。Handsontable通過EditorManager對象與編輯器交互。在第一次調(diào)用Handsontable()構(gòu)造函數(shù)之后會運行init()方法,并在這個方法中實例化EditorManager對象。EditorManager對象的引用在Handsontable實例中是私有的,不能直接訪問它。但是,有一些方法可以更改EditorManager的默認行為,稍后會詳細介紹。

EditorManager的任務

EditorManager主要有以下四個任務:

  • 為活動單元格選擇合適的編輯器
  • 準備編輯器
  • 展示編輯器(基于用戶行為)
  • 關閉編輯器(基于用戶行為)

以下,將詳細解釋每個任務。

為活動單元格選擇合適的編輯器

首先解釋活動單元格,當用戶選擇一個單元格時,當前單元格即為活動單元格(此時暫不討論多選的情況,因為我暫時也不清楚,以免誤導讀者),EditorManager查找分配給該單元格的編輯器類,檢查編輯器配置選項的值??梢匀?為表中的所有單元格)、或者每列(為列中的所有單元格),或單獨為每個單元格定義編輯器配置選項。具體配置詳情參考(https://handsontable.com/docs/javascript-data-grid/configuration-options/#cascading-configuration)。編輯器配置選項的值可以是指定字符串(代表某個特定編輯器的字符串,例如:'text', 'checkbox', 'autocomplete'……),也可以是一個編輯器類。通過這個值,EditorManager將獲得一個被指定的編輯器類的實例,注意,每個編輯器類對象都是單個表中的一個單例,這意味著每個表只調(diào)用一次它的構(gòu)造函數(shù)。如果一個頁面上有3個表,每個表都有自己的編輯器類實例。

準備編輯器

當EditorManager獲取到編輯器類實例(編輯器對象)后,會調(diào)用編輯器類實例的prepare()方法。prepare()方法設置與所選單元格相關的編輯器對象屬性,但不顯示編輯器。每次用戶選擇一個單元格時都會調(diào)用prepare()。在某些情況下,可以對同一個單元格多次調(diào)用它,而不更改選擇。

展示編輯器

prepare()執(zhí)行完之后,EditorManager等待觸發(fā)單元格編輯的用戶事件。相關的用戶事件如下:

  • 按下enter
  • 按下shift + enter
  • 雙擊單元格
  • 按下 f2

以上任意事件被觸發(fā),EditorManager就會調(diào)用編輯器類實例的beginEditing()方法去展示編輯器。

關閉編輯器

打開編輯器時,EditorManager等待應該結(jié)束單元格編輯的用戶事件被觸發(fā)。相關的用戶事件如下:

  • 點擊另一個單元格(保存更改)
  • 按下enter(保存更改,并移動至下一個單元格)
  • 按下enter + shift (保存更改,并移動至上一個單元格)
  • 按下ctrl + enter或者alt + enter(在單元格內(nèi)新增一行)
  • 按下esc(放棄更改)
  • 按下tab(保存更改,并向右或者向左移動(取決于表格布局))
  • 按下shift + tab(保存更改,并向左或者向右移動(取決于表格布局))
  • 按下Page Up或Page Down(保存更改,并向上或下移動一屏)

如果觸發(fā)了這些事件中的任何一個,EditorManager將會調(diào)用編輯器的finishiting()方法,該方法應該嘗試保存更改(除非按下了ESC鍵)并關閉編輯器。

覆蓋EditorManager的默認行為

有時候我們可能希望更改導致編輯器打開或關閉的默認事件。例如,默認情況下,編輯器可能使用向上和向下箭頭事件來執(zhí)行一些操作(例如增加或減少單元格值),并且我們不希望在用戶按下這些鍵時EditorManager關閉編輯器。這時候就可以通過beforeKeyDown鉤子來處理這種情況,因為EditorManager在處理用戶事件之前會運行beforeKeyDown鉤子。如果為beforeKeyDown注冊了一個監(jiān)聽器,那么對事件對象EditorManager的stopImmediatePropagation()調(diào)用將執(zhí)行其默認操作(這句翻譯感覺不是很準確,暫時參考有道詞典)。更多信息參考下文。

BaseEditor

Handsontable.editors.BaseEditor是一個抽象類,所有編輯器類都應該繼承于它。它實現(xiàn)了一些基本的編輯器方法,并聲明了一些應該由每個編輯器類實現(xiàn)的方法。

公共方法

公共方法是由BaseEditor類實現(xiàn)的方法。它們包含了每個編輯器都應該具備的一些核心邏輯。大多數(shù)時候,我們應該盡量避免使用這些方法。但有的時候我們可能會希望重寫部分常用方法,來創(chuàng)建一些比較復雜的編輯器,在這種情況下,您應該始終調(diào)用原始方法,然后再執(zhí)行特定于您的編輯器的其他操作。
實例:

// CustomEditor 是一個類, 繼承自 BaseEditor
class CustomEditor extends BaseEditor {
  prepare(row, col, prop, td, originalValue, cellProperties) {
    // 執(zhí)行原始方法...
    super.prepare(row, col, prop, td, originalValue, cellProperties);
    // ...然后做一些特定于你的CustomEditor的東西
    this.customEditorSpecificProperty = 'foo';
  }
}

有下方這七個公共方法:

  • prepare(row: Number, col: Number, prop: Number|String, td: HTMLTableCellElement, originalValue: Mixed, cellProperties: Object):undefined
    為給定單元格準備要顯示的編輯器。設置大多數(shù)實例屬性。返回undefined
  • beginEditing(newInitialValue: Mixed, event: Mixed):undefined
    設置編輯器值為newInitialValue。如果newInitialValue未定義,則編輯器值將設置為原始單元格值。內(nèi)部調(diào)用open()方法。返回undefined
  • finishEditing(restoreOriginalValue: 'Boolean' [optional], ctrlDown: Boolean [optional], callback: Function)
    嘗試結(jié)束單元格編輯。內(nèi)部調(diào)用saveValue()和discardEditor()。如果restoreOriginalValue設置為true,則將單元格值設置為其原始值(編輯之前的值)。ctrlDown值作為第二個參數(shù)傳遞給saveValue()。
    callback包含一個布爾參數(shù),這個布爾參數(shù)的值取決于新的值是否有效,或者allowInvalid配置選項被設置為true,否則參數(shù)為false
  • discardEditor(result: Boolean):undefined
    單元格驗證結(jié)束時調(diào)用。如果新值保存成功(result為true或allowInvalid屬性為true),則調(diào)用close()方法,否則調(diào)用focus()方法并保持編輯器打開。
  • saveValue(value: Mixed, ctrlDown: Boolean):undefined
    嘗試將value保存為單元格的新值。在內(nèi)部執(zhí)行驗證。如果ctrlDown設置為true,則新值將設置為所有選定的單元格。
  • isOpened():Boolean
    如果編輯器打開則返回true,如果編輯器關閉則返回false。在調(diào)用open()之后,編輯器被認為是打開的。在調(diào)用close()方法后,編輯器被認為是關閉的。
  • extend():Function
    返回Function—從當前類繼承的類函數(shù)。返回類的原型方法可以被安全地覆蓋,而不會有改變父類原型的危險。(這是一個與單元格編輯過程無關的實用方法。)

實例1:從BaseEditor繼承并重寫它的方法

const CustomEditor = Handsontable.editors.BaseEditor.prototype.extend();

// 這不會更改 BaseEditor.prototype.beginEditing()方法
CustomEditor.prototype.beginEditing = function() {};

實例2:從其他editor類繼承

const CustomTextEditor = Handsontable.editors.TextEditor.prototype.extend();

// CustomTextEditor可以使用所有TextEditor中實現(xiàn)的方法。
// 你可以安全地覆蓋任何方法而不影響原始的TextEditor。
Editor特定的方法

特定于編輯器的方法是在BaseEditor中沒有實現(xiàn)的方法。每個編輯器類都必須實現(xiàn)這些方法。

  • init()
    init()方法在創(chuàng)建編輯器類的新實例時調(diào)用。每個表實例只會調(diào)用一次,因為所有編輯器都在表實例中作為單例使用。init()方法主要用來創(chuàng)建展示界面(翻譯可能有誤)。其結(jié)果可以在編輯器的生命周期中重用。最常見的操作是創(chuàng)建編輯器的HTML結(jié)構(gòu)。init()沒有返回值。
  • getValue()
    getValue()方法應返回當前編輯器值,即應保存為單元格新的值。
  • setValue(newValue: Mixed)
    setValue()方法應將編輯器值設置為newValue。

示例:實現(xiàn)一個DateEditor,它通過顯示日歷來幫助選擇日期。getValue()和setValue()方法可以這樣工作:

class CalendarEditor extends TextEditor {
  constructor(hotInstance) {
    super(hotInstance);
  }

  getValue() {
    // returns currently selected date, for example "2023/09/15"
    return calendar.getDate();
  }

  setValue(newValue) {
    // highlights given date on calendar
    calendar.highlightDate(newValue);
  }
}
  • open()
    顯示編輯器,不需要返回任何值。示例:
class CustomEditor extends TextEditor {
  open() {
    this.editorDiv.style.display = '';
  }
}
  • close()
    在更改單元格值后隱藏編輯器,不需要返回任何值。示例:
class CustomEditor extends TextEditor {
  close() {
    this.editorDiv.style.display = 'none';
  }
}
  • focus()
    聚焦編輯器,不需要返回任何值。當用戶想要通過選擇另一個單元格來關閉編輯器,并且編輯器中的值不生效(當allowInvalid為false)時,調(diào)用此方法。示例:
class CustomEditor extends TextEditor {
  focus() {
    this.editorInput.focus();
  }
}
編輯器共有屬性

下面提到的所有屬性都可以通過this在編輯器實例中使用,例:this.instance。并且每次調(diào)用prepare()方法時都會更新。

屬性 類型 描述
instance Handsontable.Core 此編輯器對象所屬的Handsontable實例。在類構(gòu)造函數(shù)中設置,在編輯器的整個生命周期中不可變。
row Number 活動單元格行索引。
col Number 活動單元格的列索引。
prop String 與活動單元格關聯(lián)的屬性名(僅當數(shù)據(jù)源是對象數(shù)組時相關)。
TD HTMLTableCellNode 活動單元的節(jié)點對象。
cellProperties Object 表示活動單元格屬性的對象。

如何創(chuàng)建自定義編輯器

  1. 如果只是想增強現(xiàn)有的編輯器,可以擴展它的類并只覆蓋它的幾個方法。
    示例:擴展TextEditor,創(chuàng)建PasswordEditor,顯示密碼

  2. 也可以通過創(chuàng)建一個繼承自BaseEditor的新編輯器類來從頭構(gòu)建一個新的編輯器。
    示例:創(chuàng)建一個全新的SelectEditor,它使用<select> list來改變cell的值。

創(chuàng)建PasswordEditor

TextEditor是Handsontable中最復雜的默認編輯器。它顯示一個<textarea>,它會自動改變其大小以適應其內(nèi)容。我們想創(chuàng)建一個PasswordEditor,它保留了所有這些功能,但顯示<input type="password">字段而不是<textarea>。
我們需要創(chuàng)建一個繼承自TextEditor新的編輯器類,然后覆蓋它的一些方法,用input:password替換<textarea>。文本區(qū)域和密碼輸入具有相同的API,因此我們所要做的就是替換負責創(chuàng)建HTML元素的代碼。TextEditor中的init()內(nèi)部調(diào)用了createElements()方法,該方法創(chuàng)建<textarea>節(jié)點,并在編輯器初始化期間將其附加到DOM中。
具體代碼如下:

import Handsontable from 'handsontable';

class PasswordEditor extends Handsontable.editors.TextEditor {
  createElements() {
    super.createElements();

    this.TEXTAREA = this.hot.rootDocument.createElement('input');
    this.TEXTAREA.setAttribute('type', 'password');
    this.TEXTAREA.setAttribute('data-hot-input', true); // Makes the element recognizable by HOT as its own component's element.
    this.textareaStyle = this.TEXTAREA.style;
    this.textareaStyle.width = 0;
    this.textareaStyle.height = 0;

    this.TEXTAREA_PARENT.innerText = '';
    this.TEXTAREA_PARENT.appendChild(this.TEXTAREA);
  }
}

使用:

const container = document.querySelector('#container')
const hot = new Handsontable(container, {
  columns: [
    {
      type: 'text'
    },
    {
      editor: PasswordEditor
      // If you want to use string 'password' instead of passing
      // the actual editor class check out section "Registering editor"
    }
  ]
});
創(chuàng)建SelectEditor

SelectEditor允許用戶從定義好的<select>列表中選擇值作為單元格的新值。另外,用戶還可以使用ARROW_UP和ARROW_DOWN鍵更改當前選擇的選項。具體步驟如下:

  1. 創(chuàng)建一個繼承自Handsontable.editors.BaseEditor的新類。
const SelectEditor = Handsontable.editors.BaseEditor.prototype.extend();
  1. 添加創(chuàng)建<select>標簽并將其添加到DOM上的函數(shù)。
    有三個方法可以完成這一步,init(),prepare(),open()。
    init()方法在創(chuàng)建編輯器類對象期間被調(diào)用。每個表實例最多只發(fā)生一次,因為一旦創(chuàng)建了對象,每次EditorManager請求這個編輯器類實例時都會重用它。
    prepare()方法在用戶選擇指定單元格時會調(diào)用,指定單元格指的是editor設置為特定編輯器類的的單元格。因此,如果我們將SelectEditor設置為整個列的編輯器,那么選擇該列中的任何單元格將調(diào)用SelectEditor的prepare()方法。換句話說,這個方法在表生命周期中可以被調(diào)用數(shù)百次,特別是在處理大數(shù)據(jù)時。另外,prepare()不應該顯示編輯器(這是open()的工作)。顯示編輯器是由用戶事件(按ENTER、F2或雙擊單元格等)觸發(fā)的,因此在調(diào)用prepare()和實際顯示編輯器之間存在一段時間。盡管如此,應該盡可能快地完成prepare()執(zhí)行的操作,以提供最佳的用戶體驗。
    當需要顯示編輯器時調(diào)用open()方法。在大多數(shù)情況下,該方法應該將CSS里display屬性更改為block或執(zhí)行類似的操作。用戶希望在事件(按下適當?shù)逆I或雙擊單元格)觸發(fā)后立即顯示編輯器,因此open()方法應該盡可能快地工作。
    了解了所有這些,最合理的地方放置負責創(chuàng)建<select>輸入的代碼是在init()方法中的某個地方。DOM操作被認為是相當昂貴的(就資源消耗而言)操作,因此最好只執(zhí)行一次,并在編輯器的整個生命周期中重用生成的HTML節(jié)點。
import Handsontable from 'handsontable';

class SelectEditor extends Handsontable.editors.BaseEditor {
  /**
   * Initializes editor instance, DOM Element and mount hooks.
   */
  init() {
    // Create detached node, add CSS class and make sure its not visible
    this.select = this.hot.rootDocument.createElement('SELECT');
    this.select.classList.add('htSelectEditor');
    this.select.style.display = 'none';

    // Attach node to DOM, by appending it to the container holding the table
    this.hot.rootElement.appendChild(this.select);
  }
}
.htSelectEditor {
  /*
   * This hack enables to change <select> dimensions in WebKit browsers
   */
  -webkit-appearance: menulist-button !important;
  position: absolute;
  width: auto;
  z-index: 300;
}
  1. 添加一個函數(shù),用于填充<select>下拉選項,對應的下拉選項通過cell properties傳入。
    通過配置項傳入下拉選項列表:
const container = document.querySelector('#container')
const hot = new Handsontable(container, {
  columns: [
    {
      editor: SelectEditor,
      selectOptions: ['option1', 'option2', 'option3']
    }
  ]
});

在init()方法中填充列表不是很適合,因為init()方法只會執(zhí)行一次,當多個列都使用SelectEditor編輯器,并且每個列的下拉選項都不同,甚至同一列中的兩個單元格的下拉選項都不一致的時候,init()方法就不能實現(xiàn)我們的需求了。
我們只剩下兩個地方prepare()和open()。后一種方法更容易實現(xiàn),但正如我們前面所說,setValue()應該盡可能快地工作,如果selectOptions包含一長串選項,那么創(chuàng)建<option>節(jié)點并將它們附加到DOM可能會很耗時。因此,prepare()似乎是做這類工作更安全的地方。唯一要記住的是,當重寫prepare()時,我們應該始終調(diào)用BaseEditor的原始方法。prepare()設置一些重要的屬性,這些屬性被其他編輯器方法使用。

// Create options in prepare() method
prepare(row, col, prop, td, originalValue, cellProperties) {
  // Remember to invoke parent's method
  super.prepare(row, col, prop, td, originalValue, cellProperties);

  const selectOptions = this.cellProperties.selectOptions;
  let options;

  if (typeof selectOptions === 'function') {
    options = this.prepareOptions(selectOptions(this.row, this.col, this.prop));
  } else {
    options = this.prepareOptions(selectOptions);
  }

  this.select.innerText = '';

  Object.keys(options).forEach((key) => {
    const optionElement = this.hot.rootDocument.createElement('OPTION');
    optionElement.value = key;
    optionElement.innerText = options[key];
    this.select.appendChild(optionElement);
  });
}

prepareOptions(optionsToPrepare) {
  let preparedOptions = {};

  if (Array.isArray(optionsToPrepare)) {
    for (let i = 0, len = optionsToPrepare.length; i < len; i++) {
      preparedOptions[optionsToPrepare[i]] = optionsToPrepare[i];
    }

  } else if (typeof optionsToPrepare === 'object') {
    preparedOptions = optionsToPrepare;
  }

  return preparedOptions;
}
  1. 實現(xiàn)這些函數(shù):getValue(),setValue(),open(),close(),focus()。
getValue() {
  return this.select.value;
}

setValue(value) {
  this.select.value = value;
}

open() {
  const {
    top,
    start,
    width,
    height,
  } = this.getEditedCellRect();
  const selectStyle = this.select.style;

  this._opened = true;

  selectStyle.height = `${height}px`;
  selectStyle.minWidth = `${width}px`;
  selectStyle.top = `${top}px`;
  selectStyle[this.hot.isRtl() ? 'right' : 'left'] = `${start}px`;
  selectStyle.margin = '0px';
  selectStyle.display = '';
}

focus() {
  this.select.focus();
}

close() {
  this._opened = false;
  this.select.style.display = 'none';
}

getvalue()、setvalue()和close()的實現(xiàn)很簡單,但是open()需要一些注釋。首先,假設實現(xiàn)填充下拉列表的代碼放在prepare()中。其次,在顯示列表之前,我們設置其height和minWidth,使其與相應單元格的大小匹配。這是一個可選步驟,但如果沒有它,編輯器將根據(jù)瀏覽器的不同而具有不同的大小。限制<select>的最大高度可能是一個好主意。最后,由于<select>被附加到表格容器的末尾,我們必須更改它的位置,以便它可以顯示在正在編輯的單元格上方。同樣,這是一個可選的步驟,但是將編輯器放在適當?shù)膯卧衽赃吽坪跏欠浅:侠淼摹?/p>

  1. 覆蓋默認的EditorManager行為,這樣按向上(Arrow Up)和向下(Arrow Down)箭頭鍵不會關閉編輯器,而是改變當前選擇的值。
    雖然我們不能直接訪問EditorManager實例,但是仍然可以覆蓋它的行為。在EditorManager開始處理鍵盤事件之前,它會觸發(fā)beforeKeyDown鉤子。如果任何偵聽函數(shù)調(diào)用事件對象上的stopImmediatePropagation()方法,EditorManager將不再處理此事件。因此,我們所要做的就是注冊一個beforeKeyDown監(jiān)聽器函數(shù),該函數(shù)檢查箭頭向上或箭頭向下是否被按下,如果是,停止事件傳播并相應地改變<select>列表中當前選擇的值。
    我們需要注意的是,偵聽器應該只在打開編輯器時工作。我們希望保留其他編輯器的默認行為,以及沒有打開編輯器時的默認行為。這就是為什么注冊偵聽器的最合理的位置是open()方法,close()方法應該包含將刪除偵聽器的代碼。
onBeforeKeyDown() {
  const previousOptionIndex = this.select.selectedIndex - 1;
  const nextOptionIndex = this.select.selectedIndex + 1;

  switch (event.keyCode) {
    case 38: // Arrow Up
      if (previousOptionIndex >= 0) {
        this.select[previousOptionIndex].selected = true;
      }

      event.stopImmediatePropagation();
      event.preventDefault();
      break;

    case 40: // Arrow Down
      if (nextOptionIndex <= this.select.length - 1){
        this.select[nextOptionIndex].selected=true;
      }

      event.stopImmediatePropagation();
      event.preventDefault();
      break;

    default:
      break;
  }
}

open() {
  this.addHook('beforeKeyDown', () => this.onBeforeKeyDown());
}

close() {
  this.clearHooks();
}

活動編輯器是最近調(diào)用prepare()方法的編輯器。例如,如果選擇一個單元格,其編輯器是Handsontable.TextEditor,那么getActiveEditor()將返回這個編輯器類的一個對象。如果選擇編輯器為Handsontable.DateEditor的單元格,活動編輯器將會改變,現(xiàn)在getActiveEditor()將返回一個DateEditor類的對象。

  1. 注冊編輯器。
    創(chuàng)建編輯器時,最好是為其分配一個別名,該別名將引用這個特定的編輯器類。Handsontable默認定義了11個別名:
別名 類型
autocomplete Handsontable.editors.AutocompleteEditor
base Handsontable.editors.BaseEditor
checkbox Handsontable.editors.CheckboxEditor
date Handsontable.editors.DateEditor
dropdown Handsontable.editors.DropdownEditor
handsontable Handsontable.editors.HandsontableEditor
numeric Handsontable.editors.NumericEditor
password Handsontable.editors.PasswordEditor
select Handsontable.editors.SelectEditor
text Handsontable.editors.TextEditor
time Handsontable.editors.TimeEditor

它為用戶提供了一種方便的方式來定義在更改某些單元格的值時應該使用哪個編輯器。用戶不需要知道哪個類負責顯示編輯器,他甚至根本不需要知道有任何類。此外,您可以更改與別名關聯(lián)的類,而無需更改定義表的代碼。
要注冊自己的別名,請使用Handsontable.editors.registerEditor()函數(shù)。它接受兩個參數(shù):

  • editorName - 字符串:代表編輯器的別名
  • editorClass - 類:編輯器類
Handsontable.editors.registerEditor('select', SelectEditor);

盡量不要選擇Handsontable已經(jīng)定義了的別名。如果您以已注冊的名稱注冊編輯器,則目標類將被覆蓋:

// 現(xiàn)在‘text’別名指向MyNewTextEditor
Handsontable.editors.registerEditor('text', MyNewTextEditor);

因此,除非您有意要覆蓋現(xiàn)有別名,否則請嘗試選擇唯一的名稱。一個好的做法是用一些自定義名稱(例如您的GitHub用戶名)前綴您的別名,以盡量減少名稱沖突的可能性。如果要發(fā)布編輯器,這一點尤其重要,因為您永遠不知道使用編輯器的用戶是否注冊了別名。

// 這樣定義比較好
Handsontable.editors.registerEditor('my.select', SelectEditor);

優(yōu)化編輯器代碼

如果你打算發(fā)布你的編輯器,或者只是想保持你的代碼整潔,有3個簡單的步驟可以幫助你組織你的代碼。

  1. 使用IIFE包裹代碼
    將代碼放在模塊中,以避免污染全局命名空間。你可以使用AMD、CommonJS或任何其他模塊模式,但隔離代碼的最簡單方法是使用普通的立即調(diào)用函數(shù)表達式(IIFE)。
(Handsontable => {
  const CustomEditor = Handsontable.editors.BaseEditor.prototype.extend();

  // ...rest of the editor code

})(Handsontable);

將Handsontable作為參數(shù)傳遞是可選的(因為它是全局定義的),但使用盡可能少的全局對象是一個很好的實踐,以使模塊化和依賴管理更容易。

  1. 向?qū)S妹臻g添加編輯器
    包含在IIFE中的代碼不能從外部訪問,除非它是故意暴露的。使用Handsontable.editors.registerEditor方法將編輯器注冊到編輯器集合中,以使事情井井有條。通過這種方式,您可以在表定義期間使用編輯器,并且其他用戶如果想擴展編輯器,可以輕松訪問編輯器。
(Handsontable => {
  const CustomEditor = Handsontable.editors.BaseEditor.prototype.extend();

  // ...rest of the editor code

  // And at the end
  Handsontable.editors.registerEditor('custom', CustomEditor);

})(Handsontable);

可以這樣使用:

const container = document.querySelector('#container');
const hot = new Handsontable(container, {
  columns: [{
    editor: Handsontable.editors.CustomEditor
  }]
});

// 擴展
const AnotherEditor = Handsontable.editors.getEditor('custom').prototype.extend();
  1. 注冊別名
    最后一步是注冊編輯器別名,這樣用戶就可以輕松地引用它,而不需要知道實際的類名。
(Handsontable => {
  const CustomEditor = Handsontable.editors.BaseEditor.prototype.extend();

  // ...rest of the editor code

  // Put editor in dedicated namespace
  Handsontable.editors.CustomEditor = CustomEditor;

  // Register alias
  Handsontable.editors.registerEditor('theBestEditor', CustomEditor);

})(Handsontable);

// 使用
const container = document.querySelector('#container')
const hot = new Handsontable(container, {
  columns: [{
    editor: 'theBestEditor'
  }]
});
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容