Trường thả xuống

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 của chuỗi đó. Giá trị này là một khoá trung lập về ngôn ngữ sẽ được dùng để truy cập vào văn bản và sẽ không được dịch khi bạn chuyển đổi tuỳ chọn Chặn giữa các ngôn ngữ. Văn bản là một chuỗi mà con người đọc được và sẽ hiển thị cho người dùng.

dựa trên xu hướng

Hàm khởi tạo trình đơn thả xuống lấy một trình tạo trình đơn và một validator không bắt buộc. Trình tạo trình đơn có rất nhiều tính linh hoạt, nhưng về cơ bản, đây là một mảng tuỳ chọn, mỗi tuỳ chọn chứa một phần có thể đọc được và một chuỗi trung lập về ngôn ngữ.

Trình đơn thả xuống văn bản đơn giản

Mở trình đơn thả xuống có 2 lựa chọn văn bả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 biệt thông tin mà con người có thể đọc được với khoá trung tính về ngôn ngữ cho phép giữ nguyên chế độ cài đặt của trình đơn thả xuống giữa các ngôn ngữ. Ví dụ: phiên bản tiếng Anh của một khối có thể định nghĩa [['left', 'LEFT'], ['right', 'RIGHT]] trong khi phiên bản tiếng Đức của cùng một khối sẽ định nghĩa [['links', 'LEFT'], ['rechts', 'RIGHT]].

Trình đơn thả xuống hình ảnh

Các lựa chọn trong trình đơn thả xuống cũng có thể là hình ảnh thay vì văn bản. Các đối tượng hình ảnh được chỉ định bằng các thuộc tính src, width, heightalt.

Xin lưu ý rằng mặc dù trình đơn thả xuống có thể kết hợp các lựa chọn về văn bản và hình ảnh, nhưng một tuỳ chọn riêng lẻ hiện không thể chứa cả hình ảnh và văn bản.

Trường thả xuống chứa hình ảnh và văn bản

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 linh động

Trường thả xuống có ngày trong tuần

JSON

{
  "type": "dynamic_dropdown",
  "message0": "day %1",
  "args0": [
    {
      "type": "input_dummy",
      "name": "INPUT"
    }
  ],
  "extensions": ["dynamic_menu_extension"]
}
Blockly.Extensions.register('dynamic_menu_extension',
  function() {
    this.getInput('INPUT')
      .appendField(new Blockly.FieldDropdown(
        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;
        }), 'DAY');
  });

Bạn có thể thực hiện việc này bằng cách sử dụng một 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;
  }
};

Trình đơn thả xuống cũng có thể được cung cấp một hàm thay vì danh sách tuỳ chọn tĩnh, cho phép các tuỳ chọn đó là động. Hàm này sẽ trả về một mảng các tuỳ chọn có cùng định dạng [human-readable-value, language-neutral-key] như các tuỳ 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 tuỳ chọn được tính toán lại.

Chuyển đổi tuần tự

JSON

JSON cho trường trình đơn 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ị áp dụng cho trường đó. Giá trị này phải là một khoá tuỳ chọn trung lập về ngôn ngữ.

XML

Mã XML cho 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ị áp dụng cho trường này. Văn bản bên trong phải là một khoá tuỳ chọn trung lập về ngôn ngữ hợp lệ.

Tuỳ chỉnh

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.

Trường thả xuống có mũi tên tuỳ chỉnh

Nếu không, thuộc tính ARROW_CHAR sẽ mặc định là \u25BC (▼) trên Android và \u25BE (▾).

Đây là một thuộc tính chung nên sẽ sửa đổi tất cả các trường thả xuống khi đặt.

Bạn có thể sử 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. Chỉ số này được xác định bằng tỷ lệ phần trăm của 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 nên sẽ sửa đổi tất cả các trường thả xuống khi đặt.

Khớp tiền tố/hậu tố

Nếu tất cả các tuỳ chọn trên trình đơn thả xuống đều có chung từ tiền tố và/hoặc hậu tố, thì các từ này sẽ tự động được loại bỏ 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 này đầu tiên không cần so khớp hậu tố và cách thứ hai có):

Không có so 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');
  }
};

Có 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');
  }
};

Trường thả xuống có

Một lợi thế của phương pháp này là khối dịch vụ sẽ dễ dịch sang các ngôn ngữ khác hơn. Mã trước đó có các chuỗi 'hello', 'world''computer', trong khi mã đã sửa đổi có các chuỗi 'hello world''hello computer'. Thời gian dịch các cụm từ trở nên dễ dàng hơn nhiều so với việc dịch riêng từ.

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''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ị các 'hello' đó sau khi trình đơn 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 trong đó 2 từ phải luôn đi cùng với nhau và không nên loại trừ tiền tố. Ví dụ: 'drive red car''drive red truck' có thể chỉ nên loại bỏ 'drive', chứ không phải 'drive red'. Bạn có thể dùng không gian không ngắt của Unicode '\u00A0' thay cho khoảng trắng thông thường để loại bỏ 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''drive red\u00A0truck'.

Một vị trí khác mà không so khớp được tiền tố/hậu tố là trong các ngôn ngữ không phân tách từng từ bằng dấu cách. Tiếng Trung là một ví dụ điển hình. Chuỗi '訪問中國' có nghĩa là 'visit China', hãy lưu ý rằng việc thiếu dấu cách giữa các từ. Nhìn chung, 2 ký tự cuối cùng ('中國') là từ của 'China', nhưng nếu được phân tách, chúng có nghĩa lần lượt là 'centre''country'. Để tính năng so khớp tiền tố/hậu tố hoạt động được trong các ngôn ngữ như tiếng Trung, bạn chỉ cần chèn một dấu cách vào vị trí ngắt. Ví dụ: '訪問 中國''訪問 美國' sẽ dẫn đến "visit [China/USA]", trong khi '訪問 中 國''訪問 美 國' sẽ dẫn đến "visit [centre/beautiful] country".

Tạo trình xác thực trình đơn thả xuống

Giá trị của trường thả xuống là một chuỗi trung lập về ngôn ngữ. Do đó, mọi trình xác thực đều phải chấp nhận một chuỗi và trả về một chuỗi đây là một tuỳ 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 chưa được 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 tuỳ 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ị đã được truyền, nhưng sẽ gọi hàm trợ giúp updateConnection để thêm hoặc xoá dữ liệu đầu vào dựa trên giá trị 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');
  }
}