(my) Best Practices in Aurelia - Sharing and Passing State

We get a lot of questions in the official Aurelia gitter regarding some basic best practices so I wanted to share a few quick thoughts and examples that are my own.

Here is an example on github

Shared state

Most applications have a need for sharing state. Whether the state is session-type information about the user or some configuration options. The recommendation here is to use a shared class that is injected into your other view-models / modules. Ex-

export class Configuration {  
  constructor(){
      this.baseUri = 'https://api.github.com/';
  }
}

Then in our module that is in charge of data access (and anywhere else we need this property) we can access this property -

Remember:
  1. Import the class from the configuration.js file, which in this case is in the same directory (denoted by the ./ prefix)
  2. Inject the Configuration class into the module
  3. Grab an instance of the config object from the constructor and set it to a local object (this.config = config)

Passing State

Sometimes we don't want to use a singleton to share state. Sometimes we just want our parent view-model to pass the state down to our child. In these cases we can pass the state down from the parent to the child. There are two ways I encourage to do this -

Using Dependency-Injection

Using dependency injection we can inject the parent into the child. This will give the proper instance of our parent into the child as well so this works well inside of a repeater. This works because it is provided by the HtmlElement. Example -

parent-element view-model

export class ParentElement{  
  randomValue = Math.rand();
}

parent-element view

<template>  
  <compose view-model="child-element"></compose>
</template>  

child-element view-model

import {inject} from 'aurelia-framework';  
import {ParentElement} from './parent-element';  
inject(ParentElement)  
export class ChildElement{  
  constructor(parentelement){
    this.parentelement = parentelement;
  }
}

child-element view

<template>  
  ${parentelement.randomValue}
</template>  

Pros

  1. We don't have to specify that it is the parent specifically.
  2. We inherit all of the properties of the parent by default.

Cons

  1. We are directly tying the child to the parent. It now becomes less re-usable.
  2. When working on the application with other developers it can feel a bit like magic.

Binding the parent to the child

We can also bind the model of the child to the parent. Example -

parent-element view-model

export class ParentElement{  
  randomValue = Math.rand();
  this.thisVM = this;
}

parent-element view

<template>  
  <compose view-model="./child-element" model.bind="thisVM"></compose>
</template>  

child-element view-model

export class ChildElement{  
  activate(parentelement){
    this.parentelement = parentelement;
  }
}

child-element view

<template>  
  ${parentelement.randomValue}
</template>  

Pros

  1. This makes our child more re-usable since any parent could bind to it.
  2. We can use this method to pass specific properties down to our child instead of the whole parent.

Cons

  1. This requires that whatever object or parent view-model we pass in to have the property parameters. If it doesn't it won't be as apparent.