Documentation

Report Edit

Controllers

import {Controller} from 'cx/ui';

Controllers are used to concentrate business logic required for views. This includes preparing data for rendering, calculating values, reacting on changes, defining callbacks, etc. Controllers are assigned to widgets using the controller attribute. Once assigned, controllers are passed down the widget tree to all descendants.

onInit, onDestroy

onInit method is invoked once the controller is created. Similarly, onDestroy is called just before the controller is destroyed.

onInit method is a good place to initialize or fetch data and define triggers and computable values.

ControllerIndex
class TabController extends Controller {
    onInit() {
        this.store.set('$page.tabOpenTime', 0);
        this.timer = setInterval(() => {
            this.store.update('$page.tabOpenTime', t => t + 1);
        }, 1000);
    }
    onDestroy() {
        clearInterval(this.timer);
    }
}
Copied!Cx Fiddle

Triggers

Triggers execute a given callback when specified data changes.


Triggers are defined using the addTrigger method which takes four arguments. The first argument is the trigger's name which can later be used to remove the trigger using the removeTrigger method. The second argument is a list of bindings to be monitored. The third argument is a function which will be executed when any of the bindings change. The fourth argument should be a boolean value which controls whether or not the trigger should be immediately executed.

Triggers are used to:

  • load data from the server when selection changes
  • implement complex data behavior
ControllerIndex
class CbController extends Controller {
   onInit() {
      this.addTrigger('t1', ['$page.cb1'], cb1 => {
         this.store.set('$page.cb2', !cb1);
      });
      this.addTrigger('t2', ['$page.cb2'], cb2 => {
         this.store.set('$page.cb1', !cb2);
      });
   }
}
Copied!Cx Fiddle

Computed Values

Computed values are automatically calculated based on other data, similar to how spreadsheet formulas work. The following example shows how the population is calculated by selecting individual cities from the drop-down.

Amsterdam has 1.6M people.

Computed values are defined using the addComputable method which takes three arguments. The first argument is a binding path where computed data will be stored. The second argument is a list of bindings. Values pointed by these bindings will be used as input parameters for calculation. The third argument is a function which computes the data.

Computed values are commonly used to:

  • filter data
  • provide additional data based on selection
  • aggregate data
ControllerIndex
class InfoController extends Controller {
    onInit() {
        this.store.set('$page.cities', [{id: 'ams', text: 'Amsterdam', population: '1.6M'}, {id: 'bg',text: 'Belgrade',population: '3M'}]);

        this.addComputable('$page.city', ['$page.cities', '$page.cityId'], (options, id) => {
            return options.find(o=>o.id == id);
        });
    }
}
Copied!Cx Fiddle

Any method defined in a controller can be invoked from the view code. This is commonly used for event callbacks.

ControllerIndex
class MethodController extends Controller {
    sayHello() {
        MsgBox.alert('Hello!');
    }
}
Copied!Cx Fiddle

For simple controller invocations, use short syntax by assigning just the name of the controller method to the handler. In this case, arguments passed over to the method will be event and instance.

Index
<div controller={MethodController}>
    <Button onClick="sayHello">Say Hello</Button>
</div>
Copied!Cx Fiddle

When using names of callbacks, it's possible to invoke methods defined higher in the ancestor controller tree.

invokeParentMethod

In some cases we might need to call the parent Controller's method from within the child Controller that contains part of the business logic.

In such cases, we can use invokeParentMethod as shown in the example below.

Todo List



invokeParentMethod accepts the name of the parent method, followed by any number of arguments the method expects.

ControllerIndex
// Open fiddle to see the entire code example
// https://fiddle.cxjs.io/?f=4m9CnnAH
class NewTaskController extends Controller {
    addTask() {
        let task = this.store.get("$page.task");
        this.store.delete("$page.task");
        // addNewTask method is defined in TodoListController
        this.invokeParentMethod("addNewTask", task);
    }
}
Copied!Cx Fiddle

invokeMethod

invokeMethod behaves similarly to invokeParentMethod with the difference that the current Controller instance is checked first if it contains the specified method. This is useful if we are invoking the Controller's method from an inline event handler:

invokeMethod
<div
    controller={{
        onSubscribe(email) {
            alert("Your email is: " + email);
        }
    }}
    layout={LabelsTopLayout}
>
  <TextField label="Email" value-bind="$page.email" />
  <Button
    text="Subscribe"
    onClick={(e, {controller, store}) => {
        let email = store.get('$page.email')
        // do some additional logic...
        controller.invokeMethod('onSubscribe', email);
    }}
  />
</div>
Copied!Cx Fiddle

invokeParent accepts the name of the method, followed by any number of arguments the method expects.