Prima di creare un nuovo tipo di campo, valuta se uno degli altri per personalizzare i campi in base alle tue esigenze. Se la tua applicazione deve archiviare un nuovo tipo di valore o vuoi creare una nuova UI per un tipo di valore esistente, creare un nuovo tipo di campo.
Per creare un nuovo campo:
- Implementa un costruttore.
- Registra una chiave JSON e implementa
fromJson
. - Gestisci l'inizializzazione della UI e dell'evento sul blocco ascoltatori.
- Gestisci l'eliminazione dei listener di eventi (lo smaltimento dell'UI viene gestito per te).
- Implementa la gestione del valore.
- Aggiungi una rappresentazione testuale del valore del campo per l'accessibilità.
- Aggiungi ulteriori funzionalità, ad esempio:
- Configura aspetti aggiuntivi del campo, ad esempio:
In questa sezione si presuppone che tu abbia letto e abbia familiarità con i contenuti in Anatomia di un Campo.
Per un esempio di campo personalizzato, consulta la sezione Campi personalizzati demo di Google.
Implementazione di un costruttore
Il costruttore del campo è responsabile della configurazione del valore iniziale del campo e, facoltativamente, di configurare un strumento di convalida. L'opzione viene richiamato durante l'inizializzazione del blocco di origine, o meno che il blocco di origine sia definito in JSON o JavaScript. Quindi, il modello non ha accesso al blocco di origine durante la creazione.
Il seguente esempio di codice crea un campo personalizzato denominato GenericField
:
class GenericField extends Blockly.Field {
constructor(value, validator) {
super(value, validator);
this.SERIALIZABLE = true;
}
}
Firma del metodo
I costruttori dei campi di solito includono un valore e uno strumento di convalida locale. Il valore è
facoltativo e se non passi un valore (o passi un valore che non supera la classe
, verrà utilizzato il valore predefinito della superclasse. Per
predefinita Field
, il valore è null
. Se non vuoi quella predefinita
un valore appropriato, quindi assicurati di trasmettere un valore adatto. Il parametro di convalida è solo
presenti per i campi modificabili ed è in genere contrassegnata come facoltativa. Scopri di più
sugli strumenti di convalida nella sezione Validators
documenti.
Struttura
La logica all'interno del costruttore deve seguire questo flusso:
- Chiama il super costruttore ereditato (tutti i campi personalizzati devono ereditare
Blockly.Field
o una delle relative sottoclassi) per inizializzare correttamente il valore e impostare lo strumento di convalida locale per il campo. - Se il campo è serializzabile, imposta la proprietà corrispondente nel campo come costruttore. I campi modificabili devono essere serializzabili e devono essere modificabili per impostazione predefinita, quindi probabilmente dovresti impostare questa proprietà su true se non sai non deve essere serializzabile.
- (Facoltativo) Applica una personalizzazione aggiuntiva (ad esempio Campi etichetta) consente di passare una classe CSS, che viene poi applicata al testo).
JSON e registrazione
Nel blocco JSON
definizioni,
sono descritti da una stringa (ad es. field_number
, field_textinput
).
Blockly mantiene una mappa da queste stringhe agli oggetti dei campi e richiama
fromJson
sull'oggetto appropriato durante la costruzione.
Chiama Blockly.fieldRegistry.register
per aggiungere il tipo di campo a questa mappa
passando nella classe campo come secondo argomento:
Blockly.fieldRegistry.register('field_generic', GenericField);
Devi anche definire la funzione fromJson
. L'implementazione dovrebbe
prima dereferenzia qualsiasi stringa
tavola
i riferimenti utilizzando
replaceMessageReferences,
e poi passiamo i valori al costruttore.
GenericField.fromJson = function(options) {
const value = Blockly.utils.parsing.replaceMessageReferences(
options['value']);
return new CustomFields.GenericField(value);
};
Inizializzazione in corso
Quando viene creato, il campo contiene fondamentalmente solo un valore. L'inizializzazione è il luogo in cui viene creato il DOM, viene creato il modello (se il campo possiede un modello) e gli eventi sono legati.
Display on-block
Durante l'inizializzazione sei responsabile di creare tutto ciò di cui hai bisogno per la visualizzazione nel blocco del campo.
Valori predefiniti, sfondo e testo
La funzione initView
predefinita crea un elemento rect
di colore chiaro e un
Elemento text
. Se il tuo campo deve avere entrambi questi elementi, più alcune
componenti aggiuntivi, chiama la funzione initView
della superclasse prima di aggiungere il resto
Elementi DOM. Se vuoi che nel campo ne sia uno, ma non entrambi,
elementi puoi utilizzare le funzioni createBorderRect_
o createTextElement_
.
Personalizzazione creazione DOM
Se il campo è un campo di testo generico (ad es. Testo
di input),
La creazione del DOM verrà gestita per te. In caso contrario dovrai eseguire l'override
la funzione initView
per creare gli elementi DOM di cui avrai bisogno durante
il rendering futuro del campo.
Ad esempio, un campo a discesa può contenere sia immagini sia testo. Tra initView
crea un singolo elemento immagine e un singolo elemento di testo. Poi nel corso di render_
mostra l'elemento attivo e nasconde l'altro, in base al tipo di
l'opzione selezionata.
La creazione di elementi DOM può essere eseguita utilizzando
Blockly.utils.dom.createSvgElement
o utilizzando la creazione DOM tradizionale
di machine learning.
I requisiti per la visualizzazione nel blocco di un campo sono:
- Tutti gli elementi DOM devono essere secondari dell'elemento
fieldGroup_
del campo. Il campo viene creato automaticamente. - Tutti gli elementi DOM devono rimanere all'interno delle dimensioni riportate del campo.
Consulta le Rendering per maggiori dettagli su come personalizzare e aggiornare la visualizzazione a blocchi.
Aggiunta di simboli di testo
Se vuoi aggiungere simboli al testo di un campo (ad esempio
Angolo
simbolo dei gradi di un campo) puoi aggiungere l'elemento simbolo (di solito contenuto in un
<tspan>
) direttamente nel campo textElement_
del campo.
Eventi di input
Per impostazione predefinita, i campi registrano eventi di descrizione comando ed eventi mousedown (da utilizzare per
visualizzazione
editor).
Se vuoi rimanere in ascolto di altri tipi di eventi (ad esempio, se vuoi gestire
trascinato su un campo) devi sostituire la funzione bindEvents_
del campo.
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;
}
);
}
Per eseguire l'associazione a un evento in genere devi usare la classe
Blockly.utils.browserEvents.conditionalBind
personalizzata. Questo metodo di associazione degli eventi filtra i tocchi secondari durante
trascinamenti. Se desideri che il gestore venga eseguito anche nel mezzo di un trascinamento in corso
puoi utilizzare
Blockly.browserEvents.bind
personalizzata.
Smaltimento
Se hai registrato un listener di eventi personalizzato all'interno dell'elemento bindEvents_
del campo
Occorre annullare la registrazione all'interno della funzione dispose
.
Se hai inizializzato correttamente
visualizza
del campo (aggiungendo tutti gli elementi DOM a fieldGroup_
), quindi
il DOM del campo verrà eliminato automaticamente.
Gestione dei valori
→ Per informazioni sul valore di un campo rispetto al suo testo, vedi Struttura di un campo.
Ordine di convalida
Implementazione di uno strumento di convalida delle classi
I campi devono accettare solo determinati valori. Ad esempio, i campi numerici devono accettare numeri, campi colore devono accettare solo colori ecc. Ciò è garantito tramite classi e strumenti di convalida. Il corso segue le stesse regole degli strumenti di convalida locali, ad eccezione del fatto che viene eseguito nel di costruttore e, di conseguenza, non deve fare riferimento al blocco source.
Per implementare lo strumento di convalida della classe del campo, sostituisci doClassValidation_
personalizzata.
doClassValidation_(newValue) {
if (typeof newValue != 'string') {
return null;
}
return newValue;
};
Gestione di valori validi
Se il valore trasmesso a un campo con setValue
è valido, riceverai un
Chiamata doValueUpdate_
. Per impostazione predefinita, la funzione doValueUpdate_
:
- Imposta la proprietà
value_
sunewValue
. - Imposta la
isDirty_
atrue
.
Se devi semplicemente archiviare il valore e non vuoi eseguire alcuna gestione personalizzata,
non è necessario eseguire l'override di doValueUpdate_
.
Altrimenti, se vuoi eseguire operazioni quali:
- Spazio di archiviazione personalizzato di
newValue
. - Modifica le altre proprietà in base a
newValue
. - Salva se il valore corrente è valido o meno.
Dovrai eseguire l'override di doValueUpdate_
:
doValueUpdate_(newValue) {
super.doValueUpdate_(newValue);
this.displayValue_ = newValue;
this.isValueValid_ = true;
}
Gestione di valori non validi
Se il valore trasmesso al campo con setValue
non è valido, riceverai un
Chiamata doValueInvalid_
. Per impostazione predefinita, la funzione doValueInvalid_
niente. Ciò significa che per impostazione predefinita i valori non validi non vengono mostrati. Inoltre,
significa che il campo non verrà sottoposto nuovamente a rendering, perché
isDirty_
non verrà impostata.
Se vuoi visualizzare valori non validi, devi sostituire doValueInvalid_
.
Nella maggior parte dei casi, devi impostare una proprietà displayValue_
su
valore non valido, impostato
isDirty_
a true
e sostituisci
render_
per l'aggiornamento del display a blocchi in base a displayValue_
anziché
value_
.
doValueInvalid_(newValue) {
this.displayValue_ = newValue;
this.isDirty_ = true;
this.isValueValid_ = false;
}
Valori a più parti
Se il campo contiene un valore multiparte (ad es. elenchi, vettori, oggetti), potrebbe volere che le parti vengano gestite come singoli valori.
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;
}
Nell'esempio precedente, ciascuna proprietà di newValue
viene convalidata singolarmente. Poi
alla fine della funzione doClassValidation_
, se una singola proprietà è
non valido, il valore viene memorizzato nella cache per la proprietà cacheValidatedValue_
prima
restituisce null
(non valido). Memorizzazione nella cache dell'oggetto mediante
proprietà consente
doValueInvalid_
per gestirli separatamente, semplicemente eseguendo una
!this.cacheValidatedValue_.property
, invece di riconvalidare ogni
singolarmente.
Questo pattern per la convalida dei valori multiparte può essere utilizzato anche in di convalida ma al momento non c'è modo di applicare questo pattern.
isDirty_
isDirty_
è un flag utilizzato nel
setValue
e altre parti del campo, per capire se il campo deve essere
di nuovo sottoposti a rendering. Se il valore visualizzato del campo è cambiato, solitamente isDirty_
dovrebbe
essere impostato su true
.
Testo
→ Per informazioni su dove viene utilizzato il testo di un campo e sulle sue differenze dal valore del campo, vedi Struttura di un campo.
Se il testo del campo è diverso dal valore del campo stesso, devi
sostituisci
Funzione getText
per fornire il testo corretto.
getText() {
let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
text += ' hat';
}
return text;
}
Creazione di un editor
Se definisci la funzione showEditor_
, Blockly ascolterà automaticamente
fa clic e chiama showEditor_
al momento opportuno. Puoi visualizzare qualsiasi codice HTML
nell'editor racchiudendolo in uno dei due div speciali, denominati DropdownDiv
e WidgetDiv, che fluttuano sopra il resto dell'interfaccia utente di Blockly.
DropdownDiv e WidgetDiv
DropDownDiv
viene utilizzato per fornire editor che risiedono all'interno di un box collegato
in un campo. Si posiziona automaticamente vicino al campo e rimane
entro i limiti visibili. Il selettore angolare e il selettore colori sono ottimi esempi di
DropDownDiv
.
WidgetDiv
è utilizzato per
forniscono editor che non si trovano all'interno di una casella. I campi numerici utilizzano
WidgetDiv per coprire il campo con una casella di immissione di testo HTML. Mentre il comando DropdownDiv
WidgetDiv gestisce il posizionamento al posto tuo. Gli elementi devono essere
posizionato manualmente. Il sistema di coordinate è espresso in coordinate pixel rispetto a
in alto a sinistra della finestra. L'editor di input di testo è un buon esempio
WidgetDiv
.
Codice di esempio DropdownDiv
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));
}
Codice di esempio 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);
}
esegui la pulizia
Sia DropdownDiv che WidgetDiv gestiscono l'eliminazione dell'HTML del widget ma devi eliminare manualmente tutti i listener di eventi applicati a questi elementi.
widgetDispose_() {
for (let i = this.editorListeners_.length, listener;
listener = this.editorListeners_[i]; i--) {
Blockly.browserEvents.unbind(listener);
this.editorListeners_.pop();
}
}
La funzione dispose
viene chiamata in un contesto null
su DropDownDiv
. Attivato
WidgetDiv
viene chiamato nel contesto di WidgetDiv
. In entrambi i casi
è meglio utilizzare
associare
quando passi una funzione di eliminazione, come mostrato in DropDownDiv
sopra
e WidgetDiv
esempi.
→ Per informazioni non specifiche sullo smaltimento degli editor, vedi Smaltimento.
Aggiornamento del display su blocco
La funzione render_
viene utilizzata per aggiornare la visualizzazione sul blocco del campo in modo che corrisponda
il suo valore interno.
Ecco alcuni esempi comuni:
- Modificare il testo (menu a discesa)
- Cambiare il colore (colore)
Predefiniti
La funzione predefinita render_
imposta il testo visualizzato in base al risultato
getDisplayText_
personalizzata. La funzione getDisplayText_
restituisce la proprietà value_
del campo
viene trasmesso a una stringa, dopo che è stato troncato per rispettare i limiti di
lunghezza.
Se utilizzi la visualizzazione predefinita sul blocco e il comportamento predefinito del testo
funziona per il tuo campo, non devi eseguire l'override di render_
.
Se il comportamento predefinito del testo funziona per il campo, ma quest'ultimo è bloccato
La pubblicità display presenta altri elementi statici, puoi chiamare il valore predefinito render_
ma dovrai comunque eseguirne l'override per aggiornare la funzione
dimensioni.
Se il comportamento predefinito del testo non funziona per il tuo campo o il testo
la visualizzazione a blocchi presenta altri elementi dinamici, dovrai personalizzare
render_
personalizzata.
Personalizzazione del rendering
Se il comportamento di rendering predefinito non funziona per il tuo campo, devi per definire un comportamento di rendering personalizzato. Ciò può includere qualsiasi cosa, ad esempio l'impostazione testo visualizzato, modifica degli elementi dell'immagine o aggiornamento dei colori di sfondo.
Tutte le modifiche agli attributi DOM sono legali; le uniche due cose da ricordare sono:
- La creazione del DOM deve essere gestita durante inizializzazione, in quanto è più efficiente.
- Devi sempre aggiornare
size_
in modo che corrisponda alle dimensioni della visualizzazione nel blocco.
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_();
}
Aggiornamento delle dimensioni in corso...
L'aggiornamento della proprietà size_
di un campo è molto importante, perché informa la
bloccare il codice di rendering come posizionare il campo. Il modo migliore per capire
esattamente quello che dovrebbe essere size_
mediante esperimenti.
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;
}
Colori dei blocchi corrispondenti
Se desideri che gli elementi del campo corrispondano ai colori del blocco,
devi sostituire il metodo applyColour
. Ti consigliamo di
accedere al colore attraverso la proprietà di stile del blocco.
applyColour() {
const sourceBlock = this.sourceBlock_;
if (sourceBlock.isShadow()) {
this.arrow_.style.fill = sourceBlock.style.colourSecondary;
} else {
this.arrow_.style.fill = sourceBlock.style.colourPrimary;
}
}
Aggiornamento della modificabilità in corso...
È possibile utilizzare la funzione updateEditable
per modificare l'aspetto del campo
a seconda che siano modificabili o meno. La funzione predefinita fa sì che
lo sfondo ha o non ha una risposta al passaggio del mouse (bordo) se è o non è modificabile.
La visualizzazione sul blocco non deve cambiare di dimensione a seconda della possibilità di modificare, ma
tutte le altre modifiche sono consentite.
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;
}
}
Serializzazione
La serie riguarda il salvataggio lo stato desiderato del campo in modo che possa essere ricaricato nell'area di lavoro in un secondo momento.
Lo stato dell'area di lavoro include sempre il valore del campo, ma potrebbe anche includere un altro stato, ad esempio lo stato dell'interfaccia utente del campo. Ad esempio, se era una mappa zoomabile che consentiva all'utente di selezionare i paesi, potevi serializzare anche il livello di zoom.
Se il campo è serializzabile, devi impostare la proprietà SERIALIZABLE
su
true
.
Blockly fornisce due set di hook di serializzazione per i campi. Un paio di ganci funziona con il nuovo sistema di serializzazione JSON, mentre l'altra coppia funziona con un vecchio sistema di serializzazione XML.
saveState
e loadState
saveState
e loadState
sono hook di serializzazione che funzionano con il nuovo JSON
di serializzazione.
In alcuni casi non è necessario fornirli perché il valore predefinito
le implementazioni funzionano. Se (1) il campo è una sottoclasse diretta della classe di base
Blockly.Field
classe, (2) il valore è serie serializzabile in JSON
type e (3) devi solo
serializzare il valore, l'implementazione predefinita andrà bene.
In caso contrario, la funzione saveState
dovrebbe restituire un codice JSON serializzabile
che rappresenta lo stato del campo. e loadState
deve accettare lo stesso oggetto/valore serializzabile JSON e applicarlo
campo.
saveState() {
return {
'country': this.getValue(), // Value state
'zoom': this.getZoomLevel(), // UI state
};
}
loadState(state) {
this.setValue(state['country']);
this.setZoomLevel(state['zoom']);
}
Serializzazione e backup completi dei dati
saveState
riceve anche un parametro facoltativo doFullSerialization
. Questo è
usata dai campi che normalmente fanno riferimento allo stato serializzato da un
serializzatore (come i modelli dei dati di backup). Il parametro segnala che
lo stato di riferimento non sarà disponibile quando il blocco viene deserializzato, quindi
dovrebbe eseguire tutte le operazioni di serializzazione. Ad esempio, è vero quando
un singolo blocco viene serializzato o quando un blocco viene copiato e incollato.
Due casi d'uso comuni sono:
- Quando un singolo blocco viene caricato in un'area di lavoro in cui i dati di supporto modello inesistente, il campo ha informazioni sufficienti nel proprio stato per per creare un nuovo modello dei dati.
- Quando un blocco viene copiato e incollato, il campo crea sempre un nuovo supporto del modello dei dati, invece di farvi riferimento a uno esistente.
Un campo che lo utilizza è il campo della variabile integrata. Normalmente serializza
l'ID della variabile a cui fa riferimento, ma se doFullSerialization
è true
serializza tutto il suo stato.
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());
}
Il campo della variabile fa in modo che, se è stato caricato in un'area di lavoro, se la sua variabile non esiste, può crearne una nuova a cui fare riferimento.
toXml
e fromXml
toXml
e fromXml
sono hook di serializzazione che funzionano con il vecchio XML
di serializzazione. Usa questi ganci solo se necessario (ad es. se stai lavorando
su un vecchio codebase di cui non è stata ancora eseguita la migrazione), altrimenti usa saveState
e
loadState
.
La funzione toXml
deve restituire un nodo XML che rappresenta lo stato di
campo. La funzione fromXml
deve accettare lo stesso nodo XML e essere applicata
al campo.
toXml(fieldElement) {
fieldElement.textContent = this.getValue();
fieldElement.setAttribute('zoom', this.getZoomLevel());
return fieldElement;
}
fromXml(fieldElement) {
this.setValue(fieldElement.textContent);
this.setZoomLevel(fieldElement.getAttribute('zoom'));
}
Proprietà modificabili e serializzabili
La proprietà EDITABLE
determina se il campo deve avere un'interfaccia utente per indicare che
con cui è possibile interagire. Il valore predefinito è true
.
La proprietà SERIALIZABLE
determina se il campo deve essere serializzato. it
il valore predefinito è false
. Se questa proprietà è true
, potresti dover fornire
le funzioni di serializzazione e deserializzazione (vedi
Serializzazione).
Personalizzazione del cursore
La proprietà CURSOR
determina il cursore visualizzato dagli utenti quando passano il mouse sopra
il tuo campo. Deve essere una stringa del cursore CSS valida. Per impostazione predefinita, il cursore
definito da .blocklyDraggable
, ovvero il cursore di trascinamento.