Scope

Any variable recorded in scope is available in a template.

class MyComponent extends Akili.Component {
  constructor(el, scope) {
    super(el, scope);
    scope.example = 'Example!';
    console.log(this.scope === scope); // true
  }
}
<my-component>${ this.example }</my-component>

Inside the element above, we'll get Example! instead of the expression, after the compilation. As you can see scope in the component is the same object as this in the template.

The scope content

In the scope you can store any type of javascript variables, except objects with circular references. You can also save functions and call them at any time.

class MyComponent extends Akili.Component {
  constructor(...args) {
    super(...args);

    this.scope.test = (str) => {
      return str + ' example';
    }
  }
}
<my-component>${ this.test('my') }</my-component>

Scopes hierarchy

The componen structure has a hierarchical structure. Each scope is inherited from the parent scope through the prototype mechanism.

class ParentComponent extends Akili.Component {
  constructor(...args) {
    super(...args);
    this.scope.example = 'Example';
    this.scope.unique = 'unique!';
  }
}

class ChildComponent extends Akili.Component {
  constructor(...args) {
    super(...args);
    this.scope.example = 'ExampleTwo';
  }
}
<parent-component>
  <child-component>
    ${ this.example } is ${ this.unique }
  </child-component>
</parent-component>

Inside the element above, we'll see ExampleTwo is unique! But you also can get access to the parent component scope.

<parent-component>
  <child-component>
    ${ this.__parent.example } is ${ this.unique }
  </child-component>
</parent-component>

There will be Example is unique!

Proxy

Scope of the component is a javascript Proxy object.

You can't use the same object in different scope variables. Every proxy object will create a copy of the target object and all nested objects.

class MyComponent extends Akili.Component {
  created() {
    let obj = {};
    this.scope.x = obj;
    this.scope.y = this.scope.x;
    console.log(this.scope.x === obj); // false;
    console.log(this.scope.y === obj); // false;
    console.log(this.scope.x === this.scope.y); // false;
  }
}

All plain objects inside the scope are proxy too.

class MyComponent extends Akili.Component {
  created() {
    this.scope.example = ['ex', 'am', 'pl', 'e'];
  }

  compiled() {
    this.scope.example[0] = 's';
  }
}
<my-component>${ this.example.join('') }</my-component>

The expression result will be sample;

Parsing

By default, we use javascript new Function() for parsing. You can upgrade the parser rewriting Component.parse method. To parse some piece of code in html, you need to write an expression in the following form:

${ your javascript expression }

Such expressions might be anywhere in your templates including element attributes.

Globals

If you want to get a global variable inside the expression it is better to use Akili.globals.

import globals from 'akili/src/globals';
console.log(globals === Akili.globals); // true
globals.my = "global variable";
<div data="${ my }"></div>

Custom scope

If you need your own scope in the component you take scope property.

import Scope from 'akili/src/scope';

class MyScope extends Scope {
  init() {
    this.example = 'Hello';
  }
}

class MyComponent extends Akili.Component {
  static scope = MyScope;

  created() {
    this.scope.init();
  }
}
<my-component>${ this.example }</my-component>

To use some auxiliary variable in your scope functions you need to start it with two characters _ or property might be just an underscore. Otherwise, this variable will be monitored like any other in the scope.