Attributes

Attributes are the important part of the component. They allow it to be isolated from the outside world, but at the same time have a connection with it.

You can get any attribute value in the component property attrs after compilation.

class MyComponent extends Akili.Component {
  compiled() {
    console.log(this.attrs.title === 'Hello'); // true
  }
}
<my-component title="Hello"></my-component>

You can change or delete attribute in the component any time and it will change the value in the element too.

class MyComponent extends Akili.Component {
  compiled() {
    console.log(this.el.getAttribute('title')); // Hello
    console.log(this.el.hasAttribute('x')); // true

    this.attrs.title === 'Goodbye';
    delete this.attrs.x;

    console.log(this.el.getAttribute('title')); // Goodbye
    console.log(this.el.hasAttribute('x')); // false
  }
}
<my-component title="Hello" x="${ this.someParentScopeValue }"></my-component>

By default the attribute expressions of the most components include a parent scope.

class ParentComponent extends Akili.Component {
  created() {
    this.scope.test = 'parent';
  }
}

class ChildComponent extends Akili.Component {
  created() {
    this.scope.test = 'child';
  }

  compiled() {
    console.log(this.attrs.test === 'child'); // false
    console.log(this.attrs.test === 'parent'); // true
  }
}
<parent-component>
  <child-component test="${ this.test }"></child-component>
</parent-component>

The scope of an attribute

If you want to have the scope of the current component in the attribute expression, you have to use controlAttributes option.

class ParentComponent extends Akili.Component {
  created() {
    this.scope.test = 'parent';
  }
}

class ChildComponent extends Akili.Component {
  static controlAttributes = true;

  created() {
    this.scope.test = 'child';
  }

  compiled() {
    console.log(this.attrs.test === 'child'); // true
    console.log(this.attrs.test === 'parent'); // false
  }
}
<parent-component>
  <child-component test="${ this.test }"></child-component>
</parent-component>

Attribute's handling

There are two ways for attribute's handling. The first is a callback using.

class MyComponent extends Akili.Component {
  compiled() {
    this.attr('my-attribute', val => console.log(val === 'test')) 
  }
}

You can also pass the camel case name of the attribute myAttribute.

class MyComponent extends Akili.Component {
  compiled() {
    const fn = () => {};

    // callback is called on start (if this.attrs.hasOwnProperty('myAttribute')) and on change
    this.attr('myAttribute', fn);

    // callback is called only on change
    this.attr('myAttribute', fn, { callOnStart: false });

    // callback is called on start and on change
    this.attr('myAttribute', fn, { callOnStart: true });

    // callback is called on any attribute change
    this.attr((value, attrKey) => {});
  }
}

The second way to handle attribute's change is scope property binding.

class MyComponent extends Akili.Component {
  compiled() {
    // double binding
    this.attr('my-title', 'title');
    console.log(this.scope.title); // 'Hello'

    // don't get attribute change, but trigger the scope property event
    this.attr('my-title', 'title', { get: false });

    // don't trigger the event, but get the attribute change
    this.attr('my-title', 'title', { set: false });

    setTimeout(() => {
      this.scope.title = 'Hi';
    }, 1000);
  }
}
<my-component my-title="Hello" on-my-title="${ console.log(event.detail) // 'Hi' }"></my-component>

So you can get the attribute change to the scope property. And vice versa, automatically trigger an event with the specified name when you change the scope property.

Component.prototype.attr(name, handler, options)

  • name string - attribute name
  • handler string | string[] | function - scope property or a function to link
  • [options] Object - options, for example { callOnStart: false }

Component.prototype.unattr(name, handler)

  • name string - attribute name
  • handler string | string[] | function - scope property or a function to unlink

Boolean attributes

Some attributes in the html are boolean. For example the checkbox attribute checked. If it exists, it is already true.

<input type="checkbox" checked /> === <input type="checkbox" checked="checked"/>

To manage these attributes, there is a special variable booleanAttributes.

class MyComponent extends Akili.Component {
  static booleanAttributes = ['my-attribute'];

  compiled() {
    this.attr('myAttribute', val => console.log(val === true)) 
  }
}
<my-component my-attribute="Hello"></my-component>
<my-component my-attribute></my-component>
<my-component my-attribute="${ 1 === 1 }"></my-component>

And false example:

class MyComponent extends Akili.Component {
  static booleanAttributes = ['my-attribute'];

  compiled() {
    this.attr('myAttribute', val => console.log(val === false)) 
  }
}
<my-component></my-component>
<my-component my-attribute="${ 1 !== 1 }"></my-component>

You can find all default boolean attributes in Akili.htmlBooleanAttributes. In addition, some system components use their own.