Scope

In akili, properties of a component class and properties displayed in templates are separated. The scope of the component is responsible for displaying information in the 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>

So inside the element above, we'll see Example! instead of the expression, after 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. So you can also store functions in the scope 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 component structure of the framework has a hierarchical structure like DOM. So each scope is inherited from the parent scope. This happens 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 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 {
  constructor(...args) {
    super(...args);
    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, for parsing in the framework we use javascript new Function(). You can upgrade the parsing logic by 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 can be written anywhere in your templates, including element attributes.

Globals

If you want to get custom variables inside the expression, use Akili.globals.

import globals from 'akili/src/globals';
console.log(globals === Akili.globals); // true

globals.my = global;
<div data="${ my }"></div>

Custom scope

If you need to set your own scope to the component you can use 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>

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