建立新的欄位類型

建立新的欄位類型之前,請先評估其中是否含有其他欄位 方法 來自訂欄位。如果您的應用程式需要儲存 或者想為現有值類型建立新的 UI 可能需要建立新的欄位類型

如要建立新欄位,請執行下列步驟:

  1. 實作建構函式
  2. 註冊 JSON 金鑰並實作 fromJson
  3. 處理區塊端 UI 和事件的初始化作業 接聽程式
  4. 處理事件監聽器的處理 (系統會針對 您)。
  5. 實作價值處理
  6. 新增欄位值的文字表示法,方便無障礙功能
  7. 新增額外功能,例如:
  8. 設定欄位的其他方面,例如:

本節假設您已閱讀並熟悉 剖析 欄位

如需自訂欄位的範例,請參閱「自訂欄位」 示範 ,直接在 Google Cloud 控制台實際操作。

實作建構函式

欄位的建構函式負責設定欄位的初始值 並視需要將本地設定 驗證工具。自訂範本 欄位的建構函式會在來源區塊初始化期間呼叫,無論如何 顯示來源區塊是以 JSON 或 JavaScript 定義。自訂工作是由 欄位的建立期間無法存取來源區塊。

下列程式碼範例會建立名為 GenericField 的自訂欄位:

class GenericField extends Blockly.Field {
  constructor(value, validator) {
    super(value, validator);

    this.SERIALIZABLE = true;
  }
}

方法簽章

欄位建構函式通常會採用值和本機驗證工具。值為 要是您未傳遞值 (或傳遞類別失敗的值) 驗證),那麼會使用父類別的預設值。對於 預設的 Field 類別,值為 null。如果不想要使用預設選項 值,請務必傳送合適的值。驗證工具參數 會顯示於可編輯欄位,且通常會標示為「選填」。瞭解詳情 驗證工具中的驗證工具 文件

結構

建構函式中的邏輯應遵循以下流程:

  1. 呼叫沿用的超建構函式 (所有自訂欄位均應沿用 Blockly.Field 或其其中一個子類別),以便正確初始化該值 並為該欄位設定本機驗證工具
  2. 如果欄位可以序列化,請在 建構函式中設定。可編輯的欄位必須可序列化,且欄位可以編輯 所以您應該將這個屬性設為 true,除非您知道 則不應可序列化
  3. 選用:套用其他自訂項目 (例如標籤欄位)。 允許傳送 css 類別,並套用至文字)。

JSON 與登錄

在「JSON 區塊」 定義、 欄位是以字串 (例如 field_numberfield_textinput) 表示。 Blockly 會維護來自這些字串到欄位物件的對應,以及 建構期間在適當物件的 fromJson 上。

呼叫 Blockly.fieldRegistry.register 即可將欄位類型加入此地圖。 傳入欄位類別做為第二個引數:

Blockly.fieldRegistry.register('field_generic', GenericField);

您也需要定義 fromJson 函式。實作項目 先解除參照任何 字串 表格 參照的 replaceMessageReferences, 然後將值傳遞至建構函式。

GenericField.fromJson = function(options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
      options['value']);
  return new CustomFields.GenericField(value);
};

正在初始化

建立欄位時,欄位基本上只包含一個值。 初始化是建構 DOM 的位置,會建立模型 (如果欄位有 擁有模型),而事件則會繫結。

螢幕長亮模式

在初始化期間,您必須負責建立所需的任何內容 以便讓欄位在區塊上顯示

預設、背景和文字

預設的 initView 函式會建立淺色的 rect 元素和 text 元素。如果希望欄位同時包含這兩個欄位,加上一些額外值 等人,請先呼叫父類別 initView 函式,然後再新增其餘的 DOM 元素如果您希望欄位只加入一個欄位,但不要同時加入兩者, 可使用 createBorderRect_createTextElement_ 函式。

自訂 DOM 建構

如果您的欄位是一般文字欄位 (例如「Text」 Input) 系統會為您處理 DOM 建構程序。否則,您就必須 initView 函式建立需要用到的 DOM 元素 以便日後轉譯欄位。

例如,下拉式選單欄位可能包含圖片和文字。initView內 可建立單一圖片元素和單一文字元素。之後render_ 會根據

您可使用 Blockly.utils.dom.createSvgElement 方法或使用傳統 DOM 建立功能 方法。

欄位的區塊顯示螢幕適用以下規定:

  • 所有 DOM 元素都必須是該欄位 fieldGroup_ 的子項。欄位 即可自動建立群組。
  • 所有 DOM 元素都必須保持在欄位回報的尺寸之內。

詳情請參閱 轉譯 一節,進一步瞭解如何自訂及更新顯示器畫面。

新增文字符號

如何在欄位文字中加入符號 (例如 角度 ] 欄位的度數符號),即可附加符號元素 (通常包含在 <tspan>) 直接加入欄位的 textElement_

輸入事件

根據預設,欄位會登錄工具提示事件,以及滑鼠指向事件 (用於 顯示 editors)。 監聽其他類型的事件 (例如想處理 拖曳欄位),就應該覆寫該欄位的 bindEvents_ 函式。

bindEvents_() {
  // Call the superclass function to preserve the default behavior as well.
  super.bindEvents_();

  // Then register your own additional event listeners.
  this.mouseDownWrapper_ =
  Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
      function(event) {
        this.originalMouseX_ = event.clientX;
        this.isMouseDown_ = true;
        this.originalValue_ = this.getValue();
        event.stopPropagation();
      }
  );
  this.mouseMoveWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
      function(event) {
        if (!this.isMouseDown_) {
          return;
        }
        var delta = event.clientX - this.originalMouseX_;
        this.setValue(this.originalValue_ + delta);
      }
  );
  this.mouseUpWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
      function(_event) {
        this.isMouseDown_ = false;
      }
  );
}

如要繫結至事件,通常應該使用 Blockly.utils.browserEvents.conditionalBind敬上 函式。這種繫結事件方法會在期間篩除次要輕觸動作 。如果您希望處理常式即使在進行中的拖曳過程中也可執行 您可以使用 Blockly.browserEvents.bind敬上 函式。

丟棄

如果您在欄位的 bindEvents_ 中註冊任何自訂事件監聽器 您需要在 dispose 函式中取消註冊。

如果您正確初始化 檢視畫面 (透過將所有 DOM 元素附加至 fieldGroup_ 後), 欄位的 DOM 會自動處理。

價值處理

→ 如要瞭解欄位值與其文字的相關資訊,請參閱「剖析 欄位

驗證順序

說明驗證工具執行順序的流程圖

實作類別驗證工具

欄位只接受特定值。舉例來說,數字欄位 接受數字,顏色欄位應只接受顏色等。 透過類別和地方 驗證工具。課程 驗證工具遵循與本機驗證相同的規則,差別在於驗證工具也會執行 的 建構函式 因此,不應參照來源區塊。

如要實作欄位的類別驗證工具,請覆寫 doClassValidation_ 函式。

doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

處理有效值

如果傳遞至具有 setValue 欄位的值有效,您會收到 doValueUpdate_ 回呼。根據預設,doValueUpdate_ 函式:

  • value_ 屬性設為 newValue
  • 設定 isDirty_ 設為 true

如果您只需要儲存值,且不想執行任何自訂處理方式 不需要覆寫 doValueUpdate_

或者,如果您想執行下列操作:

  • newValue 的自訂儲存空間。
  • 根據 newValue 變更其他屬性。
  • 儲存目前的值是否有效。

您必須覆寫 doValueUpdate_

doValueUpdate_(newValue) {
  super.doValueUpdate_(newValue);
  this.displayValue_ = newValue;
  this.isValueValid_ = true;
}

處理無效值

如果傳遞至 setValue 欄位的值無效,您會收到 doValueInvalid_ 回呼。根據預設,doValueInvalid_ 函式會執行以下動作: 什麼都不做。也就是說,根據預設,無效值不會顯示。此外, 表示系統不會重新轉譯欄位,因為 isDirty_敬上 資源不會設定。

如要顯示無效的值,請覆寫 doValueInvalid_。 在多數情況下,您應將 displayValue_ 屬性設為 無效的值,請設定 isDirty_ 設為 true,並覆寫 render_ 根據 displayValue_ 而非 value_

doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}

多部分值

如果您的欄位含有多部分的值 (例如清單、向量、物件), 某些部分可能會像個別值一樣處理。

doClassValidation_(newValue) {
  if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    this.cachedValidatedValue_ = newValue;
    return null;
  }
  return newValue;
}

在上述範例中,系統會分別驗證 newValue 的每個屬性。接著 如有任何個別屬性,則位於 doClassValidation_ 函式結尾 無效,值就會快取至 cacheValidatedValue_ 屬性, 傳回 null (無效)。透過個別驗證的方式快取物件 屬性可讓 doValueInvalid_敬上 函式並分別處理 !this.cacheValidatedValue_.property 檢查,而不是重新驗證每個 管理個別資源

這種模式也可用於驗證多部分值,不過可用於 local 驗證工具 但無法強制執行這個模式

isDirty_

isDirty_setValue 函式和欄位其他部分,藉此判斷該欄位是否需要 重新轉譯。如果欄位的顯示值有所變更,通常 isDirty_ 應該 設為 true

文字

→ 瞭解欄位文字的用途以及不同之處 傳回該欄位值,請參閱 Anatomy of a 欄位

如果欄位文字與欄位的值不同, 覆寫 getText 函式 以便提供正確的文字

getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}

建立編輯者

如果您定義了 showEditor_ 函式,Blockly 會自動監聽 點擊並呼叫 showEditor_。您可以將任何 HTML ,方法是使用 DropDownDiv 這個特殊 div 功能 和 WidgetDiv,疊加在 Blockly 的使用者介面上方。

DropDownDiv 的用途是提供位於連接方塊中的編輯器 附加至欄位它會自動放置於靠近球場的位置,同時還自動 在可見範圍內角度挑選器和顏色挑選器是絕佳的 DropDownDiv

角度挑選器的圖片

WidgetDiv 的用途是 讓編輯人員以外的編輯作業數字欄位會使用 WidgetDiv 納入內含 HTML 文字輸入框的欄位。DropdownDiv 時 會為您處理定位,WidgetDiv 不會。元素必須 座標系統採用相對於 視窗左上角。文字輸入編輯器是 WidgetDiv

文字輸入編輯器的圖片

showEditor_() {
  // Create the widget HTML
  this.editor_ = this.dropdownCreate_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // Set the dropdown's background colour.
  // This can be used to make it match the colour of the field.
  Blockly.DropDownDiv.setColour('white', 'silver');

  // Show it next to the field. Always pass a dispose function.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.disposeWidget_.bind(this));
}

WidgetDiv 程式碼範例

showEditor_() {
  // Show the div. This automatically closes the dropdown if it is open.
  // Always pass a dispose function.
  Blockly.WidgetDiv.show(
    this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));

  // Create the widget HTML.
  var widget = this.createWidget_();
  Blockly.WidgetDiv.getDiv().appendChild(widget);
}

清除所用資源

DropDownDiv 和 WidgetDiv 會刪除小工具 HTML 元素,但您必須手動處理 已套用至這些元素

widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}

系統會在 DropDownDivnull 結構定義中呼叫 dispose 函式。啟用 在 WidgetDiv 結構定義中呼叫的 WidgetDiv。無論是哪種情況 建議您使用 bind 函式傳遞時的函式,如上述 DropDownDiv 所示 和 WidgetDiv 範例

→ 如要瞭解非特定丟棄編輯者的資訊,請參閱 處理

更新遮蔽式顯示器

render_ 函式可用來更新欄位的螢幕上顯示設定,使其相符 設定本身的內部值

常見的例子包括:

  • 更改文字 (下拉式選單)
  • 變更顏色 (顏色)
,瞭解如何調查及移除這項存取權。

預設值

預設的 render_ 函式會將顯示文字設為 getDisplayText_ 函式。getDisplayText_ 函式會傳回欄位的 value_ 屬性 轉換為字串,但截斷以符合文字數量上限 長度。

如果你使用預設的封鎖式顯示畫面和預設文字行為 適用於您的欄位,不需要覆寫 render_

如果預設文字行為適用於您的欄位,但欄位的封鎖設定 螢幕含有其他靜態元素,您可以呼叫預設的 render_ 函式,但您仍需覆寫這個欄位,才能更新欄位的 大小)。

如果預設文字行為不適用於您的欄位或欄位的 封鎖顯示螢幕具有其他動態元素,您必須自訂 render_ 函式

說明如何決定是否覆寫轉譯的流程圖

自訂顯示

如果預設的顯示行為不適用於您的欄位,您需要 定義自訂顯示行為這可能涉及任何設定 例如顯示文字、變更圖片元素,以及更新背景色彩

所有 DOM 屬性變更都是合法的,只有以下兩點需要注意:

  1. DOM 建立作業應於 初始化、 因為它會更有效率
  2. 您應一律更新 size_ 屬性來配合區塊螢幕的大小。
render_() {
  switch(this.value_.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      break;
    case 'Crown':
      this.crown_.style.display = '';
      break;
    case 'Mask':
      this.mask_.style.display = '';
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      break;
  }

  switch(this.value_.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  this.textContent_.nodeValue = this.value_.turtleName;

  this.updateSize_();
}

正在更新大小

更新欄位的 size_ 屬性非常重要,因為這樣能通知 區塊轉譯程式碼 說明如何放置欄位相信 確切來說,size_ 應該透過實驗來達到目的。

updateSize_() {
  const bbox = this.movableGroup_.getBBox();
  let width = bbox.width;
  let height = bbox.height;
  if (this.borderRect_) {
    width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
}

比對區塊顏色

如果您希望欄位元素與區塊的色彩相符 您應該覆寫 applyColour 方法。建議你 透過區塊的樣式屬性存取色彩。

applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}

正在更新編輯權限

updateEditable 函式可用於變更欄位的顯示方式 看看是否可以編輯根據預設,這個函式會讓 如果背景無法/無法編輯,則背景不會提供懸停回應 (邊框)。 網上螢幕的可編輯性不應隨大小而改變, 但允許其他變更

updateEditable() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  super.updateEditable();

  const group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
}

序列化

序列化的作用是儲存 欄位狀態,方便之後重新載入至工作區。

工作區狀態一律會包含欄位值,但也可能 包含其他狀態,例如欄位的 UI 狀態。舉例來說 欄位是可縮放的地圖,可供使用者選取國家/地區 也可以將縮放等級序列化

如果欄位可序列化,您必須將 SERIALIZABLE 屬性設為 true

Blockly 為欄位提供兩組序列化掛鉤。一對勾 新的 JSON 序列化系統可以使用,另一對使用 還是傳統的 XML 序列化系統

saveStateloadState

saveStateloadState 是能與新 JSON 搭配使用的序列化掛鉤 序列化系統。

在某些情況下,您不需要提供這些資訊, 實作程序如果 (1) 您的欄位是基礎子類別的直接子類別 Blockly.Field 類別,(2) 您的值是 JSON 可序列化 type 以及 (3) 您只需要 序列化這個值,那麼預設的實作方式就沒有問題!

否則,saveState 函式應傳回 JSON 可序列化 代表欄位狀態的物件/值。你的loadState 函式應接受相同的 JSON 可序列化物件/值,並套用至 這個欄位。

saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}

完整序列化與備份資料

saveState 也會收到選用參數 doFullSerialization。這是 通常參照狀態由不同的 serializer (例如備份資料模型)。參數會指出 將區塊還原序列化時,系統就無法使用參照狀態,因此 欄位應執行所有序列化作業。舉例來說 個別區塊序列化,或複製貼上區塊。

以下是兩個常見用途:

  • 將個別區塊載入備份資料的工作區時 則該欄位在自身狀態下擁有足夠的資訊, 建立新的資料模型
  • 當您複製區塊時,該欄位一律會建立新的備份 而非參照現有資料模型

使用這個欄位的欄位是內建變數欄位。一般情況下 參照的變數 ID;如果 doFullSerialization 為 true 容器會將所有狀態序列化

saveState(doFullSerialization) {
  const state = {'id': this.variable_.getId()};
  if (doFullSerialization) {
    state['name'] = this.variable_.name;
    state['type'] = this.variable_.type;
  }
  return state;
}

loadState(state) {
  const variable = Blockly.Variables.getOrCreateVariablePackage(
      this.getSourceBlock().workspace,
      state['id'],
      state['name'],   // May not exist.
      state['type']);  // May not exist.
  this.setValue(variable.getId());
}

這麼做是為了確保變數欄位能載入工作區 只要變數不存在,就能建立一個參照的新變數。

toXmlfromXml

toXmlfromXml 是能與舊版 XML 搭配使用的序列化掛鉤 序列化系統。請只在必要情況下使用這些掛鉤 (例如,在專案 位於尚未遷移的舊程式碼集上),否則請使用 saveStateloadState

toXml 函式應傳回一個 XML 節點,代表 這個欄位。且 fromXml 函式應接受相同的 XML 節點,並套用 加到欄位中

toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}

可編輯與可序列化屬性

EDITABLE 屬性會決定欄位是否應有 UI 來指出 可與之互動預設值為 true

SERIALIZABLE 屬性會決定是否應序列化欄位。這項服務 預設值為 false。如果這個屬性是 true,您可能需要提供 序列化與去序列化函式 (請參閱 序列化)。

自訂遊標

CURSOR 屬性決定使用者將遊標懸停在上方時看到的遊標。 這個欄位。必須是有效的 CSS 遊標字串。這會預設為遊標 由 .blocklyDraggable 定義,也就是擷取遊標。