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.

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

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

We can change or delete an attribute in the component and it will change the corresponding value in the element.

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 use the 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 attribute scope

If you need to have the current component scope inside 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 handling

There are two ways for attributes 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 is the 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 the 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 in 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 }

This function creates the link with the attribute.

Component.prototype.unattr(name, handler)

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

This function removes the link with the attrtibute.

Boolean attributes

Some attributes in 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 such 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 example for false:

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>

The default boolean attributes are in Akili.htmlBooleanAttributes. In addition, some system components use their own.