Skip to main content

Build a custom mutator

4. Create the mutator​

Since mutators contain extra state, this extra state needs to be serialized and deserialized with the rest of the state of the block. Although mutators can vary greatly, any mutator requires serialization hooks.

Create the mutator mixin object​

Create a new folder called mutators, then create a file in that folder called list_mutator.js. In list_mutator.js, import Blockly, then create an empty object:

import * as Blockly from 'blockly/core';

// The mutator mixin
export const LIST_MUTATOR = {
// Add mutator functions here
};

The LIST_MUTATOR will be the mixin object for the custom mutator.

Serialize​

The mutator must serialize the extra state of the block. This is accomplished by implementing the function saveExtraState(). Since this codelab implements a list mutator, you can use the same saveExtraState() code as the default lists_create_with block. This function should return a JSON serializable value.

Add the following code to the LIST_MUTATOR object:

export const LIST_MUTATOR = {
saveExtraState: function() {
return {
'itemCount': this.itemCount,
};
},
};

Deserialize​

The mutator mixin must also load, or deserialize, the extra state of the block. To deserialize, loadExtraState() reads in the itemCount value from the state of the block.

Add the following code to the LIST_MUTATOR, just after saveExtraState():

loadExtraState: function(state) {
this.itemCount = state['itemCount'];
},

Register the mutator​

Now, register the mutator with Blockly.Extensions.registerMutator. This function requires a string name and a mixin object as parameters. It also takes two optional parameters:

  • A function which runs after the mixin functions are added to the block.
  • A list of blocks to include in the mutator flyout, if you are using the default mutator UI. This parameter is not applicable in this codelab, since you will be creating a custom UI.

In this case, the optional function is a great way to initialize the number of list items in the mutator. Since the block starts with one input (see the block definition), you'll set a variable on the block called itemCount to 1.

Add the following import and registration to index.js:

import {LIST_MUTATOR} from './mutators/list_mutator';

...

// Register the blocks and generator with Blockly
Blockly.common.defineBlocks(blocks);
Blockly.common.defineBlocks(mutatorBlocks);
Object.assign(javascriptGenerator.forBlock, forBlock);
Blockly.Extensions.registerMutator(
'list_mutator',
LIST_MUTATOR,
function() { this.itemCount = 1; }
);

Add the mutator to the block​

Now that the mutator is registered, add it to the block definition in list.js:

const resizableListBlock = {
type: 'resizable_list',
message0: 'resizable list with %1',
args0: [
{
type: 'input_value',
name: 'ADD0',
}
],
output: null,
style: "list_blocks",
tooltip: '',
helpUrl: '',
mutator: 'list_mutator',
};