Posts Tagged ‘aurelia’

Dynamic Aurelia

From someone used to developing in a “traditional” way, switching to a javascript framework is like crossing over to a different universe where the laws of physics do not really apply. They might seem similar but they’re not. Take inheritance for instance, the foundation stone of OOP and throw it down the drain. No more inheritance, everything is composition right now.

Yet in time you will learn the new laws and adapt. Making the decision on which framework to choose has been one of the toughest things I had to do, but finally I am content with choosing Aurelia. It’s structured and comprehensible.

One of the things less documented, however, is the way to do dynamic composition programmatically (in code), as opposed to declarative (in html). This means adding a new element in the DOM and loading a component into it or loading directly some html string with Aurelia markup.

I currently found 3 ways of doing this, and even if I don’t know which is the recommended, one I will mention all of them below. I want to thank all the articles and gitter help I got in making the code below work.

Using the compositionEngine.compose()

The compose element is the way Aurelia allows you to insert a dynamic component into the page. What if there is no compose markup and you want to add a variable number of components?
This corresponds to the following use case scenario: I have a menu navigation which opens tabs for each different application module. Each module is implemented as a component (they do not inherit a base class!). When the user opens a menu entry, a new tab is created and the corresponding component loaded. A working example can be found in this gist.

To add a new tab, a new DOM node is created using basic javascript, then the CompositionEngine.compose is called provided with a viewModel, the component name, and a slot to attach to, the newly created element.

this.instruction = Object.assign(this.instruction, {viewModel: menuId, host: el, viewSlot: vSlot});
this.compositionEngine.compose(this.instruction).then(controller => {
       vSlot.bind();
       vSlot.attached();
});

Please note that the limitation I currently found to this method is that if 2 instances of the same component are created they will share the same instance of the viewModel. Test here by opening “second module” twice and changing the value. This is due to the fact that the viewModel is obtained using container.get() which by default returns a singleton. One solution is the mark the viewModel with transient(), another would be to create the viewModel instance and add it to the child container (I guess this is Aurelia does internally for the components). Also don’t forget to call unbind and detach on the component if you also plan to allow for the components to be removed.

Using enhance

A simpler method is to insert the markup for the compose element directly and call templatingEngine.enhance on it. This also does not suffer from previous viewModel reusage limitation.

...
              content: '<compose view-model="' + menuId + '" id="' + moduleDivId + '"></compose>',
              encoded: false
          });
 
      let el = document.getElementById(moduleDivId);
      let view = this.templatingEngine.enhance({ element: el, bindingContext: {}, overrideContext: {}});

See more info here.

Please note that I think the api has changed recently since I am experiencing some trouble with this method on latest aurelia-templating.

Using the viewCompiler

Both previous methods add a component dynamically in html in a place where no previous markup exists. At some point however one might wish to load a chunk of html containing aurelia markup in the current DOM. Take the usecase where parts of a form are user generated and are loaded from the server at runtime. Check the gist here. As you see, when the html is loaded, binding also takes place.

The dynamic part happens here:

        let viewFactory = this.viewCompiler.compile(html, this.resources);
        let view = viewFactory.create(this.container);
        view.bind(viewModel, createOverrideContext(viewModel));
        let viewSlot = new ViewSlot(containerElement, true);
        viewSlot.add(view);
        viewSlot.attached();
        return () => {
            viewSlot.remove(view);
            view.unbind();
        };

 

Update NumericTextBox precision on the fly

As currency selection changed you naturally want to change the precision and format of a NumericTextBox which contains an amount in the given currency (damn JPY for not using the same precision as everyone else).

At a first glance one might think this can be accomplished using computed properties and something like:

    @bindable precision:number = 2;
 
    @computedFrom('precision')
    get step():number {
        return Math.pow(10, - this.precision);
    }
 
    @computedFrom('precision')
    get format():string {
        return "n" + this.precision;
    }
<input type="number" ak-numerictextbox="k-value.two-way:value; k-spinners.one-way:spinners; k-format.one-way:format; k-decimals.one-way:precision; k-step.one-way:step"/>

However this is not possible and in this specific case there are no kendo functions to achieve this.

The only option, short of recreating the control each time the currency changes is to use the setOptions method as described here:

    precisionChanged(newValue, oldValue){
        let step = Math.pow(10, - this.precision);
        this.numericTextBox.setOptions({format: 'n' + newValue, decimals: newValue, step: step});
        this.numericTextBox.value(this.value);
    }

Please note the value(value()) is a very ugly hack. See running gist.

 

Dispatch change events in custom components

This is the first post about Aurelia and the Kendo-UI-Bridge. It’s a brave new world for me.

The goal was to create a custom component (which wraps a ak-combobox) and dispatch a change event in a similar way to the standard components (i.e ak-combobox, ak-datepicker, etc.).

<input ak-datepicker="k-value.two-way: model.date" k-on-change.delegate="onChange($event)"/>

For these components, the change event is dispatched after the model changes.

In my component code I had initially wrapped the k-on-change from the base component and continued dispatching it.
This however did not had the desired effect. The wrapping component emitted the event before the model was updated (binding was updated).

<dictionary-combo value.two-way="model.currency" k-on-change.delegate="onChange($event)"></dictionary-combo>
    onChange(event){
       event.stopPropagation();
       this.dispatch()
    }
 
    dispatch(){
      this.dispatchCustomEvent(this.element, "k-on-change", {value: this.value});
    }
 
    dispatchCustomEvent(element:Element, type:string, detail:Object, bubbles:boolean){
        let newEvent;
        if (window.CustomEvent) {
            newEvent = new CustomEvent(type, {
                detail: detail,
                bubbles: true
            });
        } else {
            newEvent = document.createEvent('CustomEvent');
            newEvent.initCustomEvent(type, bubbles, true, {
                detail: detail
            });
        }
        element.dispatchEvent(newEvent);
    }

The solution is to delay the event. Stop propagating the initial event and add the action of sending the new event to the aurelia taskQueue. This would ensure the event occurs after the binding and can be done:

    onChange(event){
       event.stopPropagation();
       console.log("dictionaryCombo.onChange: " + this.value);
       //wrong way: this.dispatch()
       this.taskQueue.queueMicroTask(() => this.dispatch());
    }

Refer to this gist for complete example.

Please note that I found this, thanks to the wonderful guys on the aurelia-kendo-ui gitter to which I remain grateful