Свойства и опции

Работа с опциями контрола

Опции — это объект, в котором хранятся параметры, переданные при инициализации контрола. В шаблоне опции доступны в переменной _options, а в модуле — в переменной this._options.

Опции доступны только на чтение, и менять их можно только через родительский контрол. Такое ограничение введено для того, чтобы избежать излишнего усложнения кода. Изменение объекта опций подобно изменению аргументов, переданных в функцию. Опции должны меняться только в том случае, если родитель изменил параметры контрола.

В следующем примере мы инициализируем контрол MyControl и передаем в него опцию color.

<!-- Parent/Template.wml -->
<div>
   <Demo.Module color="{{ someColor }}" />
</div>
<!-- Demo/Template.wml -->
<div style="color: {{ _options.color }};">
   Hello Wasaby!
</div>
// Module.js
define("Demo/Module", ["UI/Base", "wml!Demo/Template"], function(Base, template) {
   var ModuleClass = Base.Control.extend({
      _template: template
   });
   return ModuleClass;
});

См. демо-пример на CodeSandbox

С опциями можно работать в хуках жизненного цикла.

Начальные значения опций

Для указания начального значения опций определите в контроле статический метод getDefaultOptions. Данный метод устанавливает значение опции по-умолчанию, если эта опция не задана.

// Module.js
define("Demo/Module", ["UI/Base", "wml!Demo/Template"], function(Base, template) {
   var ModuleClass = Base.Control.extend({ ... });
   ModuleClass.getDefaultOptions = function getDefaultOptions() {
      return {
         number: 10, 
         text: 'Default text'
      };
   };
   return ModuleClass;
});
<!-- Parent.wml -->
<div>
   <Demo.Module/>
</div>
<!-- Module.wml -->
<div>
   {{_options.number}}
</div>

Результат

<div>
   10
</div>

Контроль типа данных опций

С помощью статического метода getOptionTypes можно указать типы опций контрола. Когда контрол получает опцию другого типа, Wasaby не построит контрол и выдаст ошибку.

// Module.js
define('Demo/Module', ['UI/Base', 'Types/entity' ], function(Base,entity) {
   var ModuleClass = Base.Control.extend({ ... });
   ModuleClass.getOptionTypes =  function getOptionTypes() {
      return {
         number: entity.descriptor(Number), 
         text: entity.descriptor(String)
      }
   };
   return ModuleClass;
});

К типам опций можно применить модификаторы. С помощью required() обозначают опции, обязательные для построения контрола.

// Module.js
define('Demo/Module', ['UI/Base', 'Types/entity' ], function(Base, entity) {
   var ModuleClass = Base.Control.extend({ ... });
   ModuleClass.getOptionTypes =  function getOptionTypes() {
      return {
         text: entity.descriptor(String).required()
      }
   };
   return ModuleClass;
});

С помощью oneOf() обозначают допустимый набор значений.

// Module.js
define('Demo/Module', ['UI/Base', 'Types/entity' ], function(Base, entity) {
   var ModuleClass = Base.Control.extend({ ... });
   ModuleClass.getOptionTypes =  function getOptionTypes() {
      return {
         alignment: entity.descriptor(String).oneOf([
            'center',
            'left',
            'right'
         ])
      }
   };
   return ModuleClass;
});

Синхронизация опций

Wasaby по умолчанию синхронизирует изменения опций в одном направлении — из родительского контрола к дочерними. Для создания двусторонней синхронизации используют директиву bind.

<!-- Module.wml -->
<div>
  <Demo.TextBox bind:text="myText" />
</div>

При этом из контрола, для которого применяется директива bind, должно быть опубликовано событие на изменение значения свойства. Имя публикуемого события должно соответствовать формату <свойство дочернего контрола>Changed.

// Module.js
define("Demo/Module", ["UI/Base", "wml!Demo/Module"], function(Base, template) {
   var ModuleClass = Base.Control.extend({
      _template: template,
      myText: 'Hello Wasaby'
   });
   return ModuleClass;
});
// TextBox.js
define("Demo/TextBox", ["UI/Base", "wml!Demo/TextBox"], function(Base, template) {
   var ModuleClass = Base.Control.extend({
      _template: template,
      changeText: function(text) {
         this._notify('textChanged', [text]);
      }
   });
   return ModuleClass;
});

Полная форма записи директивы такова:

bind:<свойство дочернего контрола>="свойство родительского контрола"

Событие "<свойство дочернего контрола>Changed" может быть предназначено для bind, который объявлен в контроле, который располагается выше родительского контрола. В этом случае нужно выполнить одно из следующих условий:

  1. Отправлять событие с параметром bubbling:true (см. Всплываемость).
  2. Проксировать событие у родительского контрола. Примечание: для проксирования не нужно объявлять bind у родительского контрола. Событие достаточно поймать, а затем — опубликовать такое же.
    Ниже показан пример как это реализуется для трёх контрол:
    • Control-1
      <!-- Control-1.wml -->
      <div>
         <Demo.Control-2 bind:title="myText" />
      </div>
      // Control-1.js
      define("Demo/Control-1", ["UI/Base", "wml!Demo/Control-1"], function(Base, template) {
         var ModuleClass = Base.Control.extend({
            _template: template,
            myText: 'Hello Wasaby'
         });
         return ModuleClass;
      });
    • Control-2
      <!-- Control-2.wml -->
      <div>
         <Demo.Control-3 title="{{ _options.title }}" on:titleChanged="changeTitle()" />
      </div>
      // Control-2.js
      define("Demo/Control-2", ["UI/Base", "wml!Demo/Control-2"], function(Base, template) {
         var ModuleClass = Base.Control.extend({
            _template: template,
            changeTitle: function(title) {
               this._notify('titleChanged', [title]);
            }
         });
         return ModuleClass;
      });
    • Control-3
      // Control-3.js
      define("Demo/Control-3", ["UI/Base", "wml!Demo/Control-3"], function(Base, template) {
         var ModuleClass = Base.Control.extend({
            _template: template,
            changeTitle: function(title) {
               this._notify('titleChanged', [title]);
            }
         });
         return ModuleClass;
      });

При обратном биндинге можно ссылаться на:

  • Cвойства контрола
    bind:prop="myProp"
  • Подсвойства любого уровня вложенности
    bind:prop="myProp.innerProp"
  • Подсвойства опции
    bind:prop="_options.myProp.innerProp"

Свойства контрола

Каждый контрол хранит некоторое внутреннее состояние, представленное набором свойств. В каждый момент времени контрол может отличаться своим состоянием. Состояние контрола используется для работы контрола и организуется разработчиком.

Свойства, которые принимают значения типов Number, Boolean и String, объявляют на прототипе. В экземпляр класса контрола они копируются "по значению".

define("Demo/Module", ["UI/Base"], function(Base) {
   var ModuleClass = Base.Control.extend({
      simpleVariable: 1
   });
   return ModuleClass;
});

​​Если свойства принимают значения типов Array или Object, то ссылки на них должны быть явно добавлены на инстанс контрола в хуке _beforeMount.

define("Demo/Module", ["UI/Base"], function(Base) {
   var ModuleClass = Base.Control.extend({
      filter: null,      
      _beforeMount: function(options) {
         this.filter = {
            // Значения объекта.
         }
      },
   });
   return ModuleClass;
});

В модуле доступ к свойствам осуществляется через указатель this.

Примечание. При использовании нативных возможностей TypeScript, объявлять свойства типов Object и Array можно так:

Class MyClass extends Control {
   protected myProp: string[] = ['a', 'b', 'c']
}

Доступ к свойствам в шаблоне

Свойства контрола доступны в области видимости его шаблона в качестве переменных. Подробнее о работе с переменными в шаблоне написано здесь.

define("Demo/Module", ["UI/Base", "wml!Template"], function(Base) {
   var ModuleClass = Base.Control.extend({
      simpleVariable: 1
   });
   return ModuleClass;
});
<!-- Template.wml -->
<div>
{{ simpleVariable }}   <!-- 1 -->
</div>