변형자

뮤테이터는 블록에 추가 직렬화 (저장되고 로드되는 추가 상태)를 추가하는 믹스인입니다. 예를 들어 내장 controls_iflist_create_with 블록은 입력 수를 저장할 수 있도록 추가 직렬화가 필요합니다. 사용자가 블록의 모양을 변경할 수 있는 UI를 추가할 수도 있습니다.

목록 만들기 블록의 세 가지 변형: 입력 없음, 입력 3개, 입력 5개

if/do 블록의 두 가지 변형: if-do 및 if-do-else-if-do-else

블록의 도형을 변경한다고 해서 추가 직렬화가 필요한 것은 아닌 것에 유의하세요. 예를 들어 math_number_property 블록은 모양을 변경하지만 값이 이미 직렬화된 드롭다운 필드를 기반으로 합니다. 따라서 필드 검사기만 사용할 수 있으며 뮤테이터는 필요하지 않습니다.

드롭다운이 'even'으로 설정된 `math_number_property` 블록입니다. 단일 값 입력이 있습니다. 드롭다운이 'divisible by'로 설정된 `math_number_property` 블록. 두 개의 값 입력이 있습니다.

mutator가 필요한 경우와 그렇지 않은 경우에 관한 자세한 내용은 직렬화 페이지를 참고하세요.

또한 뮤테이터는 사용자가 몇 가지 선택적 메서드를 제공하는 경우 블록의 도형을 변경할 수 있는 내장 UI를 제공합니다.

직렬화 후크

뮤테이터에는 작동하는 두 쌍의 직렬화 후크가 있습니다. 한 쌍의 후크는 새 JSON 직렬화 시스템에서 작동하고 다른 쌍은 기존 XML 직렬화 시스템에서 작동합니다. 이러한 쌍 중 하나 이상을 제공해야 합니다.

saveExtraState 및 loadExtraState

saveExtraStateloadExtraState는 새 JSON 직렬화 시스템에서 작동하는 직렬화 후크입니다. saveExtraState는 블록의 추가 상태를 나타내는 직렬화 가능한 JSON 값을 반환하고 loadExtraState는 동일한 직렬화 가능한 JSON 값을 수락하여 블록에 적용합니다.

// These are the serialization hooks for the lists_create_with block.
saveExtraState: function() {
  return {
    'itemCount': this.itemCount_,
  };
},

loadExtraState: function(state) {
  this.itemCount_ = state['itemCount'];
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

결과 JSON은 다음과 같습니다.

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}

상태 없음

블록이 직렬화될 때 기본 상태에 있으면 saveExtraState 메서드가 이를 나타내기 위해 null를 반환할 수 있습니다. saveExtraState 메서드가 null을 반환하면 JSON에 extraState 속성이 추가되지 않습니다. 이렇게 하면 저장 파일 크기가 작게 유지됩니다.

전체 직렬화 및 백업 데이터

saveExtraState는 선택적 doFullSerialization 매개변수도 수신합니다. 이는 다른 serializer (예: 백업 데이터 모델)에 의해 직렬화된 상태를 참조하는 블록에서 사용됩니다. 이 매개변수는 블록이 역직렬화될 때 참조된 상태를 사용할 수 없음을 나타내므로 블록은 모든 백킹 상태를 자체적으로 직렬화해야 합니다. 예를 들어 개별 블록이 직렬화되거나 블록이 복사-붙여넣기될 때는 이 속성이 true입니다.

이에 대한 일반적인 사용 사례는 다음과 같습니다.

  • 개별 블록이 백엔드 데이터 모델이 없는 워크스페이스에 로드되면 자체 상태에 새 데이터 모델을 만들기에 충분한 정보가 있습니다.
  • 블록을 복사하여 붙여넣으면 항상 기존 백그라운드 데이터 모델을 참조하는 대신 새 백그라운드 데이터 모델이 생성됩니다.

이를 사용하는 일부 블록은 @blockly/block-shareable-procedures 블록입니다. 일반적으로 상태를 저장하는 백엔드 데이터 모델에 대한 참조를 직렬화합니다. 하지만 doFullSerialization 매개변수가 true이면 모든 상태를 직렬화합니다. 공유 가능한 프로시저 블록은 이를 사용하여 복사하여 붙여넣을 때 기존 모델을 참조하는 대신 새 백업 데이터 모델을 만들도록 합니다.

mutationToDom 및 domToMutation

mutationToDomdomToMutation는 이전 XML 직렬화 시스템에서 작동하는 직렬화 후크입니다. 이러한 후크는 필요한 경우에만 사용하세요 (예: 아직 이전하지 않은 이전 코드베이스에서 작업하는 경우). 그 외의 경우에는 saveExtraStateloadExtraState를 사용하세요.

mutationToDom는 블록의 추가 상태를 나타내는 XML 노드를 반환하고 domToMutation는 동일한 XML 노드를 수락하여 블록에 상태를 적용합니다.

// These are the old XML serialization hooks for the lists_create_with block.
mutationToDom: function() {
  // You *must* create a <mutation></mutation> element.
  // This element can have children.
  var container = Blockly.utils.xml.createElement('mutation');
  container.setAttribute('items', this.itemCount_);
  return container;
},

domToMutation: function(xmlElement) {
  this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

결과 XML은 다음과 같습니다.

<block type="lists_create_with">
  <mutation items="3"></mutation>
</block>

mutationToDom 함수가 null을 반환하면 XML에 추가 요소가 추가되지 않습니다.

UI 후크

특정 함수를 변형자의 일부로 제공하면 Blockly에서 블록에 기본 '변형자' UI를 추가합니다.

변환자 버블이 열려 있는 if-do 블록 이를 통해 사용자는 if-do 블록에 else-if 및 else 절을 추가할 수 있습니다.

추가 직렬화를 추가하려는 경우 이 UI를 사용할 필요가 없습니다. blocks-plus-minus 플러그인에서 제공하는 것과 같은 맞춤 UI를 사용하거나 UI를 전혀 사용하지 않을 수도 있습니다.

구성 및 분해

기본 UI는 composedecompose 함수를 사용합니다.

decompose: 블록을 더 작고 이동, 추가, 삭제할 수 있는 하위 블록으로 '확장'합니다. 이 함수는 하위 블록이 연결되는 뮤테이터 워크스페이스의 기본 블록인 '최상위 블록'을 반환해야 합니다.

그런 다음 compose는 하위 블록의 구성을 해석하고 이를 사용하여 기본 블록을 수정합니다. 이 함수는 decompose에서 반환된 '상단 블록'을 매개변수로 허용해야 합니다.

이러한 함수는 '변경된' 블록에 '혼합'되므로 this를 사용하여 해당 블록을 참조할 수 있습니다.

// These are the decompose and compose functions for the lists_create_with block.
decompose: function(workspace) {
  // This is a special sub-block that only gets created in the mutator UI.
  // It acts as our "top block"
  var topBlock = workspace.newBlock('lists_create_with_container');
  topBlock.initSvg();

  // Then we add one sub-block for each item in the list.
  var connection = topBlock.getInput('STACK').connection;
  for (var i = 0; i < this.itemCount_; i++) {
    var itemBlock = workspace.newBlock('lists_create_with_item');
    itemBlock.initSvg();
    connection.connect(itemBlock.previousConnection);
    connection = itemBlock.nextConnection;
  }

  // And finally we have to return the top-block.
  return topBlock;
},

// The container block is the top-block returned by decompose.
compose: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we collect up all of the connections of on our main block that are
  // referenced by our sub-blocks.
  // This relates to the saveConnections hook (explained below).
  var connections = [];
  while (itemBlock && !itemBlock.isInsertionMarker()) {  // Ignore insertion markers!
    connections.push(itemBlock.valueConnection_);
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }

  // Then we disconnect any children where the sub-block associated with that
  // child has been deleted/removed from the stack.
  for (var i = 0; i < this.itemCount_; i++) {
    var connection = this.getInput('ADD' + i).connection.targetConnection;
    if (connection && connections.indexOf(connection) == -1) {
      connection.disconnect();
    }
  }

  // Then we update the shape of our block (removing or adding iputs as necessary).
  // `this` refers to the main block.
  this.itemCount_ = connections.length;
  this.updateShape_();

  // And finally we reconnect any child blocks.
  for (var i = 0; i < this.itemCount_; i++) {
    connections[i].reconnect(this, 'ADD' + i);
  }
},

saveConnections

원하는 경우 기본 UI와 함께 작동하는 saveConnections 함수를 정의할 수도 있습니다. 이 함수를 사용하면 기본 워크스페이스에 있는 기본 블록의 하위 요소를 뮤테이터 워크스페이스에 있는 하위 블록과 연결할 수 있습니다. 그런 다음 이 데이터를 사용하여 하위 블록이 재구성될 때 compose 함수가 기본 블록의 하위 요소를 올바르게 다시 연결하는지 확인할 수 있습니다.

saveConnectionsdecompose 함수에서 반환된 '상단 블록'을 매개변수로 수락해야 합니다. saveConnections 함수가 정의된 경우 Blockly는 compose를 호출하기 전에 이를 호출합니다.

saveConnections: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we go through and assign references to connections on our main block
  // (input.connection.targetConnection) to properties on our sub blocks
  // (itemBlock.valueConnection_).
  var i = 0;
  while (itemBlock) {
    // `this` refers to the main block (which is being "mutated").
    var input = this.getInput('ADD' + i);
    // This is the important line of this function!
    itemBlock.valueConnection_ = input && input.connection.targetConnection;
    i++;
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }
},

등록 중

변형자는 특수한 종류의 믹스인에 불과하므로 블록 유형의 JSON 정의에서 사용할 수 있기 전에 등록해야 합니다.

// Function signature.
Blockly.Extensions.registerMutator(name, mixinObj, opt_helperFn, opt_blockList);

// Example call.
Blockly.Extensions.registerMutator(
    'controls_if_mutator',
    { /* mutator methods */ },
    undefined,
    ['controls_if_elseif', 'controls_if_else']);
  • name: JSON에서 사용할 수 있도록 뮤테이터와 연결할 문자열입니다.
  • mixinObj: 다양한 변형 메서드가 포함된 객체입니다. 예를 들면 saveExtraStateloadExtraState입니다.
  • opt_helperFn: 믹신이 혼합된 후 블록에서 실행되는 선택적 도우미 함수입니다.
  • opt_blockList: UI 메서드도 정의된 경우 기본 수정자 UI의 플라이아웃에 추가되는 블록 유형 배열(문자열로 지정)입니다(선택사항).

확장 프로그램과 달리 각 블록 유형에는 하나의 변형자만 있을 수 있습니다.

{
  //...
  "mutator": "controls_if_mutator"
}

도우미 함수

뮤테이터는 믹스인과 함께 도우미 함수를 등록할 수 있습니다. 이 함수는 지정된 유형의 각 블록이 생성되고 mixinObj가 추가된 후에 실행됩니다. 변형에 트리거나 효과를 추가하는 데 사용할 수 있습니다.

예를 들어 목록과 같은 블록에 초기 항목 수를 설정하는 도우미를 추가할 수 있습니다.

var helper = function() {
  this.itemCount_ = 5;
  this.updateShape();
}