Trường thả xuống lưu trữ một chuỗi làm giá trị và một chuỗi làm văn bản. Giá trị này là một khoá không phụ thuộc vào ngôn ngữ, sẽ được dùng để truy cập vào văn bản và sẽ không được dịch khi Blockly chuyển đổi giữa các ngôn ngữ. Văn bản này là một chuỗi ký tự mà con người đọc được và sẽ hiển thị cho người dùng.
Trường thả xuống
Trường thả xuống có trình chỉnh sửa đang mở
Trường thả xuống trên khối bị thu gọn
dựa trên xu hướng
Hàm khởi tạo thả xuống nhận một trình tạo trình đơn và một trình xác thực không bắt buộc. Trình tạo trình đơn là một mảng các lựa chọn (trong đó mỗi lựa chọn chứa một phần dễ đọc và một chuỗi không phụ thuộc vào ngôn ngữ) hoặc một hàm tạo ra một mảng các lựa chọn. Phần mà con người có thể đọc được của mỗi lựa chọn có thể là một chuỗi, hình ảnh hoặc phần tử HTML và mảng có thể chứa nhiều lựa chọn thuộc các loại khác nhau.
Trình đơn thả xuống văn bản đơn giản
JSON
{
"type": "example_dropdown",
"message0": "drop down: %1",
"args0": [
{
"type": "field_dropdown",
"name": "FIELDNAME",
"options": [
[ "first item", "ITEM1" ],
[ "second item", "ITEM2" ]
]
}
]
}
JavaScript
Blockly.Blocks['example_dropdown'] = {
init: function() {
this.appendDummyInput()
.appendField('drop down:')
.appendField(new Blockly.FieldDropdown([
['first item', 'ITEM1'],
['second item', 'ITEM2']
]), 'FIELDNAME');
}
};
Việc tách thông tin mà con người có thể đọc được khỏi khoá không phụ thuộc vào ngôn ngữ cho phép chế độ cài đặt của trình đơn thả xuống được giữ nguyên giữa các ngôn ngữ. Ví dụ: phiên bản tiếng Anh của một khối có thể xác định [['left', 'LEFT'], ['right',
'RIGHT]]
trong khi phiên bản tiếng Đức của cùng khối đó sẽ xác định [['links',
'LEFT'], ['rechts', 'RIGHT]]
.
Trình đơn thả xuống về hình ảnh
Các lựa chọn trong trình đơn thả xuống có thể là hình ảnh, được biểu thị dưới dạng các đối tượng có thuộc tính src
, width
, height
và alt
.
JSON
{
"type": "image_dropdown",
"message0": "flag %1",
"args0": [
{
"type": "field_dropdown",
"name": "FLAG",
"options": [
["none", "NONE"],
[{"src": "canada.png", "width": 50, "height": 25, "alt": "Canada"}, "CANADA"],
[{"src": "usa.png", "width": 50, "height": 25, "alt": "USA"}, "USA"],
[{"src": "mexico.png", "width": 50, "height": 25, "alt": "Mexico"}, "MEXICO"]
]
}
]
}
JavaScript
Blockly.Blocks['image_dropdown'] = {
init: function() {
var input = this.appendDummyInput()
.appendField('flag');
var options = [
['none', 'NONE'],
[{'src': 'canada.png', 'width': 50, 'height': 25, 'alt': 'Canada'}, 'CANADA'],
[{'src': 'usa.png', 'width': 50, 'height': 25, 'alt': 'USA'}, 'USA'],
[{'src': 'mexico.png', 'width': 50, 'height': 25, 'alt': 'Mexico'}, 'MEXICO']
];
input.appendField(new Blockly.FieldDropdown(options), 'FLAG');
}
};
Trình đơn thả xuống HTML
Lựa chọn có thể là bất kỳ phần tử HTML nào, miễn là phần tử đó không quá lớn và không cố gắng xử lý các sự kiện chuột hoặc bàn phím. (Bạn có trách nhiệm tuân thủ các quy tắc này – Blockly không thực thi các quy tắc đó.)
Khi trình đơn thả xuống mở ra, danh sách sẽ hiển thị phần tử HTML. Khi đóng và phần tử là lựa chọn đã chọn, danh sách sẽ hiển thị (theo thứ tự ưu tiên giảm dần) thuộc tính title
, thuộc tính aria-label
hoặc thuộc tính innerText
của phần tử.
JSON
{
"type": "flags_with_text_dropdown",
"message0": "flag with text %1",
"args0": [
{
"type": "field_dropdown",
"name": "FLAG_WITH_TEXT",
"options": [
["x", "X"], // Placeholder. An empty array throws an exception.
]
}
],
// Use an extension to add the HTML element options.
"extensions": ["flag_with_text_extension"]
}
Blockly.Extensions.register('flag_with_text_extension',
function() {
function createFlagWithTextDiv(text, src) {
const div = document.createElement('div');
div.setAttribute('style', 'width: 75px;');
div.setAttribute('title', text);
const img = document.createElement('img');
img.setAttribute('src', src);
img.setAttribute('style', 'height: 25px; display: block; margin: auto;');
div.appendChild(img);
const para = document.createElement('p');
para.innerText = text;
para.setAttribute('style', 'text-align: center; margin: 5px;');
div.appendChild(para);
return div;
}
const canadaDiv = createFlagWithTextDiv('Canada', 'canada.png');
const usaDiv = createFlagWithTextDiv('USA', 'usa.png');
const mexicoDiv = createFlagWithTextDiv('Mexico', 'mexico.png');
const options = [
['none', 'NONE'],
[canadaDiv, 'CANADA'],
[usaDiv, 'USA'],
[mexicoDiv, 'MEXICO']
];
this.getField('FLAG_WITH_TEXT').setOptions(options);
});
Bạn có thể thực hiện việc này bằng cách sử dụng tiện ích JSON.
JavaScript
function createFlagWithTextDiv(text, src) {
const div = document.createElement('div');
div.setAttribute('style', 'width: 75px;');
div.setAttribute('title', text);
const img = document.createElement('img');
img.setAttribute('src', src);
img.setAttribute('style', 'height: 25px; display: block; margin: auto;');
div.appendChild(img);
const para = document.createElement('p');
para.innerText = text;
para.setAttribute('style', 'text-align: center; margin: 5px;');
div.appendChild(para);
return div;
}
const canadaDiv = createFlagWithTextDiv('Canada', 'canada.png');
const usaDiv = createFlagWithTextDiv('USA', 'usa.png');
const mexicoDiv = createFlagWithTextDiv('Mexico', 'mexico.png');
Blockly.Blocks['flags_with_text_dropdown'] = {
init: function() {
const input = this.appendDummyInput()
.appendField('flag with text');
const options = [
['none', 'NONE'],
[canadaDiv, 'CANADA'],
[usaDiv, 'USA'],
[mexicoDiv, 'MEXICO']
];
input.appendField(new Blockly.FieldDropdown(options), 'FLAG_WITH_TEXT');
}
};
Trình đơn thả xuống linh hoạt
JSON
{
"type": "dynamic_dropdown",
"message0": "day %1",
"args0": [
{
"type": "field_dropdown",
"name": "DAY",
"options": [
["x", "X"], // Placeholder. An empty array throws an exception.
]
}
],
// Use an extension to set the menu function.
"extensions": ["dynamic_menu_extension"]
}
Blockly.Extensions.register('dynamic_menu_extension',
function() {
this.getField('DAY').setOptions(
function() {
var options = [];
var now = Date.now();
for(var i = 0; i < 7; i++) {
var dateString = String(new Date(now)).substring(0, 3);
options.push([dateString, dateString.toUpperCase()]);
now += 24 * 60 * 60 * 1000;
}
return options;
});
});
Bạn có thể thực hiện việc này bằng cách sử dụng tiện ích JSON.
JavaScript
Blockly.Blocks['dynamic_dropdown'] = {
init: function() {
var input = this.appendDummyInput()
.appendField('day')
.appendField(new Blockly.FieldDropdown(
this.generateOptions), 'DAY');
},
generateOptions: function() {
var options = [];
var now = Date.now();
for(var i = 0; i < 7; i++) {
var dateString = String(new Date(now)).substring(0, 3);
options.push([dateString, dateString.toUpperCase()]);
now += 24 * 60 * 60 * 1000;
}
return options;
}
};
Bạn cũng có thể cung cấp một trình đơn thả xuống bằng một hàm thay vì danh sách các lựa chọn tĩnh, cho phép các lựa chọn này thay đổi linh hoạt. Hàm này sẽ trả về một mảng các lựa chọn có cùng định dạng [human-readable-value, language-neutral-key]
như các lựa chọn tĩnh. Mỗi khi người dùng nhấp vào trình đơn thả xuống, hàm sẽ chạy và các lựa chọn sẽ được tính toán lại.
Dòng phân cách
Sử dụng chuỗi 'separator'
để thêm một dòng giữa các lựa chọn trong trình đơn thả xuống.
JSON
{
"type": "separator_dropdown",
"message0": "food %1",
"args0": [
{
"type": "field_dropdown",
"name": "FOOD",
"options": [
["water", "WATER"],
["juice", "JUICE"],
"separator",
["salad", "SALAD"],
["soup", "SOUP"],
]
}
]
}
JavaScript
Blockly.Blocks["separator_dropdown"] = {
init: function() {
var input = this.appendDummyInput()
.appendField("food1");
var options = [
["water", "WATER"],
["juice", "JUICE"],
"separator",
["salad", "SALAD"],
["soup", "SOUP"],
];
input.appendField(new Blockly.FieldDropdown(options), "FOOD");
}
};
Chuyển đổi tuần tự
JSON
JSON cho một trường thả xuống có dạng như sau:
{
"fields": {
"FIELDNAME": "LANGUAGE-NEUTRAL-KEY"
}
}
Trong đó, FIELDNAME
là một chuỗi tham chiếu đến một trường thả xuống và giá trị là giá trị cần áp dụng cho trường. Giá trị này phải là khoá tuỳ chọn không phụ thuộc vào ngôn ngữ.
XML
XML cho một trường thả xuống sẽ có dạng như sau:
<field name="FIELDNAME">LANGUAGE-NEUTRAL-KEY</field>
Trong đó, thuộc tính name
của trường chứa một chuỗi tham chiếu đến trường thả xuống và văn bản bên trong là giá trị cần áp dụng cho trường. Văn bản bên trong phải là một khoá tuỳ chọn hợp lệ, không phụ thuộc vào ngôn ngữ.
Tuỳ chỉnh
Mũi tên thả xuống
Bạn có thể dùng thuộc tính Blockly.FieldDropdown.ARROW_CHAR
để thay đổi ký tự unicode biểu thị mũi tên thả xuống.
Thuộc tính ARROW_CHAR
mặc định là \u25BC
(▼) trên Android và \u25BE
(▾) trong trường hợp khác.
Đây là một thuộc tính chung, vì vậy, thuộc tính này sẽ sửa đổi tất cả các trường thả xuống khi được đặt.
Chiều cao của trình đơn
Bạn có thể dùng thuộc tính Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH
để thay đổi chiều cao tối đa của trình đơn. Được xác định là tỷ lệ phần trăm chiều cao khung nhìn, khung nhìn là cửa sổ.
Thuộc tính MAX_MENU_HEIGHT_VH
mặc định là 0,45.
Đây là một thuộc tính chung, vì vậy, thuộc tính này sẽ sửa đổi tất cả các trường thả xuống khi được đặt.
Khớp tiền tố/hậu tố
Nếu tất cả các lựa chọn trong trình đơn thả xuống đều có chung các từ tiền tố và/hoặc hậu tố, thì những từ này sẽ tự động được tách ra và chèn dưới dạng văn bản tĩnh. Ví dụ: dưới đây là 2 cách để tạo cùng một khối (cách đầu tiên không có tính năng so khớp hậu tố và cách thứ hai có):
Không có tính năng khớp hậu tố:
JSON
{
"type": "dropdown_no_matching",
"message0": "hello %1",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["world", "WORLD"],
["computer", "CPU"]
]
}
]
}
JavaScript
Blockly.Blocks['dropdown_no_matching'] = {
init: function() {
var options = [
['world', 'WORLD'],
['computer', 'CPU']
];
this.appendDummyInput()
.appendField('hello')
.appendField(new Blockly.FieldDropdown(options), 'MODE');
}
};
Với tính năng khớp hậu tố:
JSON
{
"type": "dropdown_with_matching",
"message0": "%1",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["hello world", "WORLD"],
["hello computer", "CPU"]
]
}
]
}
JavaScript
Blockly.Blocks['dropdown_with_matching'] = {
init: function() {
var options = [
['hello world', 'WORLD'],
['hello computer', 'CPU']
];
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown(options), 'MODE');
}
};
Một ưu điểm của phương pháp này là khối dễ dịch sang các ngôn ngữ khác. Mã trước đó có các chuỗi 'hello'
, 'world'
và 'computer'
, trong khi mã đã sửa đổi có các chuỗi 'hello world'
và 'hello computer'
. Người dịch sẽ dễ dàng dịch các cụm từ hơn là dịch từng từ riêng lẻ.
Một ưu điểm khác của phương pháp này là thứ tự từ thường thay đổi giữa các ngôn ngữ. Hãy tưởng tượng một ngôn ngữ sử dụng 'world hello'
và 'computer hello'
.
Thuật toán so khớp hậu tố sẽ phát hiện 'hello'
phổ biến và hiển thị hậu tố đó sau danh sách thả xuống.
Tuy nhiên, đôi khi việc so khớp tiền tố/hậu tố không thành công. Có một số trường hợp mà hai từ luôn đi cùng nhau và không nên tách tiền tố.
Ví dụ: 'drive red car'
và 'drive red truck'
chỉ nên có 'drive'
được tách ra, chứ không phải 'drive red'
. Bạn có thể dùng khoảng trắng không ngắt dòng Unicode '\u00A0'
thay cho khoảng trắng thông thường để ngăn chặn trình so khớp tiền tố/hậu tố. Do đó, bạn có thể khắc phục ví dụ trên bằng 'drive red\u00A0car'
và 'drive red\u00A0truck'
.
Một trường hợp khác mà tính năng so khớp tiền tố/hậu tố không hoạt động là trong các ngôn ngữ không phân tách các từ riêng lẻ bằng khoảng trắng. Tiếng Trung là một ví dụ điển hình. Chuỗi '訪問中國'
có nghĩa là 'visit China'
, lưu ý rằng không có dấu cách giữa các từ.
Hai ký tự cuối cùng ('中國'
) là từ 'China'
, nhưng nếu tách ra thì chúng sẽ có nghĩa là 'centre'
và 'country'
. Để tính năng so khớp tiền tố/hậu tố hoạt động trong các ngôn ngữ như tiếng Trung, bạn chỉ cần chèn một khoảng trắng vào vị trí cần ngắt. Ví dụ: '訪問 中國'
và '訪問 美國'
sẽ tạo ra "visit [China/USA]"
, trong khi '訪問 中 國'
và '訪問 美 國'
sẽ tạo ra "visit [centre/beautiful] country"
.
Tạo trình xác thực thả xuống
Giá trị của trường thả xuống là một chuỗi trung lập về ngôn ngữ, vì vậy, mọi trình xác thực phải chấp nhận một chuỗi và trả về một chuỗi là một lựa chọn có sẵn, null
hoặc undefined
.
Nếu trình xác thực của bạn trả về bất kỳ giá trị nào khác, thì hành vi của Blockly sẽ không xác định và chương trình của bạn có thể gặp sự cố.
Ví dụ: bạn có thể xác định một trường thả xuống có 3 lựa chọn và một trình xác thực như sau:
validate: function(newValue) {
this.getSourceBlock().updateConnections(newValue);
return newValue;
},
init: function() {
var options = [
['has neither', 'NEITHER'],
['has statement', 'STATEMENT'],
['has value', 'VALUE'],
];
this.appendDummyInput()
// Pass the field constructor the options list, the validator, and the name.
.appendField(new Blockly.FieldDropdown(options, this.validate), 'MODE');
}
validate
luôn trả về giá trị mà nó được truyền, nhưng nó gọi hàm trợ giúp updateConnection
để thêm hoặc xoá các đầu vào dựa trên giá trị của trình đơn thả xuống:
updateConnections: function(newValue) {
this.removeInput('STATEMENT', /* no error */ true);
this.removeInput('VALUE', /* no error */ true);
if (newValue == 'STATEMENT') {
this.appendStatementInput('STATEMENT');
} else if (newValue == 'VALUE') {
this.appendValueInput('VALUE');
}
}