Behaviors

Behaviors are special methods that usually manipulate the associated dom elements of components. For example, we might want to hide or disable a button - those are behaviors. To invoke a behavior you call a behave() method on a component and pass it the intended behavior name:

button1.behave("disable"); 

It is quite common to invoke behaviors from within components in response to a certain event that was triggered. For example we could disable a button if someone clicks it:

export class MyButtonComponent extends extend_as("MyButtonComponent").mix(Component).with() {

  this.native_events = ["click"];

  consrtuctor() {
    event_handlers.add({
      event: 'click',
      role:  #self,
      handler: (self,event) => self.behave('disable')
    });
  }
}

There are some behaviors which Webface.js defines for all components, others are only available for some (see behavior classess in the repository). All components have the following behaviors available:

  • hide()
  • show()
  • disable()
  • enable()
  • lock()
  • unlock()

Note: the show() behavior implementation simply sets the display property of the dom_element to block. If block is not what you wish, you can either override the method in your own behavior class or add the data-component-display-value attribute to your dom_element and set its value to whatever your prefer - for example inline-block. Then, the show() behavior will use that instead of block.

Creating custom behaviors

When designing your own component, you most certainly will want to create your own custom behaviors. To do that, you first need to define a class that extends ComponentBehaviors:

export class MyButtonComponentBehaviors extends extend_as("MyButtonComponentBehaviors").mix(ComponentBehaviors).with() {
  enlarge()       => this.dom_element.style.fontSize = "120%";
  normalizeSize() => this.dom_element.style.fontSize = "100%";
}

Here we defined two behaviors that change our button font size (for whatever reason). Now we need add these behaviors to the list of behaviors in your component:

export class MyButtonComponent extends extend_as("MyButtonComponent").mix(Component).with() {
  static get behaviors() { return [MyButtonComponentBehaviors]; }
  ...
}

The syntax for adding behavior to components may seem a bit odd - why use static getter? But it's due to limitations of Javascript, which doesn't currently have static class variables. And it makes sense to make the list of behaviors the same for all component instances.

As you have probably guessed, you can define and add as many behavior classes as you want, combining different behaviors for various components, effectively making behavior classes modules:

  static get behaviors() { return [MyButtonComponentBehaviors, SomeOtherComponentBehavior]; }

Now we can invoke our newly created behavior with:

button1.behave("rockAndRoll");

If you happen to have the same method in some of the behavior classes you've added to the behaviors list, then the most recent class in which this method is found will be used. In that sense, it works almost like inheritance. For example, suppose you have added the following behavior classes:

static get behaviors() { return [BehaviorsOne, BehaviorsTwo, BehaviorsThree, BehaviorsFour]; }

Let's say only BehaviorsOne and BehaviorsThree have a sayHello() method you're about to call with behave("sayHello"). In that case, BehaviorsThree's sayHello() method will be invoked.

Objects available to behavior instances

When writing your own behavior class, you get access to the following objects:

  • .component - a reference to the component object this behavior is used for.
  • .dom_element - shorthand for .component.dom_element
  • .pos - a refernce to the PositionManager object that helps you easily manipulate the position of DOM elements.
    See Utils / Position Manager page to learn more.
  • .animator - a reference to the Animator object that helps you apply basic animations to DOM elements.
    See Utils / Animator page to learn more.

Do not modify components!

To reduce coupling, behaviors should only read component attributes and use its own methods, but they should not be modifying component by changing its state (through attributes). If you find yourself writing a behavior class that does that, perhaps it's time to reassess whether what you're doing is a good idea.

In the future, Webface.js will make component class only accessible through a read-only proxy, when accessed from by a behavior object, effectively preventing any changes to the component and throwing errors when changes are attempted.