Skip to main content

Build a custom mutator

5. Update the block shape​

The resizable_list needs to add or remove inputs depending on the number of items in the list. This means that the shape of the block should be updated based on the itemCount.

A block with itemCount==1 should have just one input:

A list block with one input

A block with itemCount==2 should have two inputs:

A list block with two inputs

And so on. The block implemented in this codelab will always have a minimum of one input.

Note that blocks with mutators don't always need to change their shape based on the extra state. In this case, updating the shape is necessary.

Adding an input​

The same updateShape() function will be responsible for updating the shape regardless of whether an input has been added or removed. First, focus on adding a new input. Add the following function and code to the LIST_MUTATOR:

export const LIST_MUTATOR = {
...

updateShape: function() {
// Add new inputs.
for (let i = 1; i < this.itemCount; i++) {
if (!this.getInput('ADD' + i)) {
this.appendValueInput('ADD' + i).setAriaLabelProvider(
() => 'value ' + (i + 1)
);
}
}
},
};

The updateShape() function adds itemCount - 1 inputs, since the block starts with 1 input. This function also sets the aria label for each input manually, so that screenreaders distinguish clearly between each input.

Finally, note that the inputs are named numerically. The input that you defined in the block definition is called ADD0, so subsequent blocks are named ADD1, ADD2, ADD3, etc.

Removing an input​

To remove an input, remove the appropriate number of inputs from the end of the list. Finish the updateShape() function with the code below:

export const LIST_MUTATOR = {
...

updateShape: function() {
// Add new inputs.
for (let i = 1; i < this.itemCount; i++) {
if (!this.getInput('ADD' + i)) {
this.appendValueInput('ADD' + i).setAriaLabelProvider(
() => 'value ' + (i + 1)
);
}
}
// Remove deleted inputs.
for (let i = this.itemCount; this.getInput('ADD' + i); i++) {
this.removeInput('ADD' + i);
}
},
};

Updating after load​

Since this list block will add or remove inputs based on the value of itemCount, it is important to update the physical shape after deserialization. Add a call to updateShape() in loadExtraState():

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

Next steps​

Although updateShape() is complete, only the deserializer calls it. The next step of creating a mutator is considering how the user will interact with the mutator in order to modify the extraState.