3.4.22 • Published 7 months ago

reacton-js v3.4.22

Weekly downloads
-
License
MIT
Repository
-
Last release
7 months ago

EN / RU

reacton

GitHub | GitFlic | NpmJS | Loader🔄 | Reacton⤵️

Reacton is a JavaScript plugin for quickly building reactive Web Components. The plugin supports all technologies, methods and properties such as slots and Shadow DOM that are provided by standard Web Components.

- Adding the child components section.

- Added example Webpack build for routes.

- Added example Webpack build for events.

- Updating the build for Webpack.

- Updating the reactive attributes section.

- In version 3.4.0, a bug in accessing special properties in static methods of closed components was fixed and reference attributes were added for quick access to elements.

- Added a loader of single-file components for Webpack.

Below is an example of a simple component:

<!-- mount the MyComponent component -->
<my-component color="red"></my-component>

<!-- create component template MyComponent -->
<template class="MyComponent">
  <h1>Hello, {{ message }}!</h1>
        
  <style>
    h1 {
      color: {{ color }};
    }
  </style>

  <script>
    exports = class {
      // initializing the state object in the constructor
      constructor(props) {
        this.message = 'Reacton'
        this.color = props.color
      }

      static mode = 'open' // add Shadow DOM
    }
  </script>
</template>
  1. Quick start
  2. Component class
  3. Special properties
  4. General methods
  5. Reactive attributes
  6. Reference attributes
  7. Child components
  8. Cycles
  9. Styles
  10. Slots
  11. Events
  12. Routes
  13. SSR

Classes are used to create components. Classes can be either built into the main script or imported from an external module. Create a new working directory, for example named app, and download the reacton.min.js file into this directory.

Add an index.html file to the directory with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create component class MyComponent
    class MyComponent {
      message = 'Reacton'
      color = 'red'

      static template = `
        <h1>Hello, {{ message }}!</h1>
        
        <style>
          h1 {
            color: {{ color }};
          }
        </style>
      `
    }

    // pass component class MyComponent to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

To ensure there are no naming conflicts between standard and custom HTML elements, the component name must contain a dash «-», for example, my-element and super-button are valid names, but myelement is not.

When you open the index.html file in the browser, the screen will display the message created in the MyComponent component:

In this example, a simple component has been created that is embedded in a common script. Let's now move this component into a separate module.

Create a MyComponent.js file in the app directory with the following content:

// export the MyComponent component class
export default class MyComponent {
  message = 'Reacton'
  color = 'red'

  static template = `
    <h1>Hello, {{ message }}!</h1>
    
    <style>
      h1 {
        color: {{ color }};
      }
    </style>
  `
}

Make changes to the index.html file, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script type="module">
    // import the MyComponent component class
    import MyComponent from './MyComponent.js'

    // pass component class MyComponent to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

To work with external components, we need any development server, such as, for example, lite-server.

Install this server using the command in the terminal:

npm install --global lite-server

Now navigate to the app directory using a terminal or open a terminal in that directory and in the terminal enter the command:

lite-server

This will open a default browser window displaying the welcome message shown above.

To quickly access a component in the browser console, add the identifier "mycomp" to its mount element, as shown below:

<!-- mount the MyComponent component -->
<my-component id="mycomp"></my-component>

Now open the browser console and enter two commands:

mycomp.$state.message = 'Web Components'
mycomp.$state.color = 'green'

The title color and message will change immediately:

Reacton provides a more convenient way to create components in <template> tags.

Make changes to the index.html file, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h1>Hello, {{ message }}!</h1>
          
    <style>
      h1 {
        color: {{ color }};
      }
    </style>

    <script>
      exports = class {
        message = 'Reacton'
        color = 'red'
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

In this example, the name of the component class is determined from the name of the first template class.

Components can be created in external files, which is especially useful when using build systems. You can customize your own or download a ready-made build system based on Webpack.

Create a MyComponent.htm file in the app directory with the following content:

<my-component>
  <h1>Hello, {{ message }}!</h1>
          
  <style>
    h1 {
      color: {{ color }};
    }
  </style>

  <script>
    exports = class {
      message = 'Reacton'
      color = 'red'
    }
  </script>
</my-component>

Make changes to the index.html file, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script type="module">
    // request MyComponent.htm file
    const response = await fetch('MyComponent.htm')
    // get the text content of a file
    const MyComponent = await response.text()

    // pass contents of MyComponent component file to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

To work with external components, we need any development server, such as, for example, lite-server.

Install this server using the command in the terminal:

npm install --global lite-server

Now navigate to the app directory using a terminal or open a terminal in that directory and in the terminal enter the command:

lite-server

This will open a default browser window displaying the welcome message shown above.

The component class name determines the name of the component element in the DOM. For example, the class MyComponent or myComponent will match the name my-component in the DOM. Each component class may contain an optional static property name that defines the name of this class.

This property must be specified, for example, when passing an anonymous class directly to a plugin:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass anonymous class to Reacton plugin
    Reacton(class {
      message = 'Reacton'
      color = 'red'

      static name = 'MyComponent' // component name

      static template = `
        <h1>Hello, {{ message }}!</h1>
        
        <style>
          h1 {
            color: {{ color }};
          }
        </style>
      `
    })
  </script>
</body>
</html>

The class name can be specified in camel case, as in the example above, or kebab notation:

static name = 'my-component'

The state of a component is defined as properties of an instance of the component's class. In the example above, there are two states:

message = 'Reacton'
color = 'red'

This is a new way of defining properties for objects. You can also use the old way, by specifying a constructor:

constructor() {
  this.message = 'Reacton'
  this.color = 'red'
}

In addition to state, class objects can also have methods, for example:

class MyComponent {
  message = 'Reacton'
  color = 'red'

  // class object method
  printHello() {
    return `Hello, ${ this.message }!`
  }

  static template =  `
    <h1>{{ printHello() }}</h1>
    
    <style>
      h1 {
        color: {{ color }};
      }
    </style>
  `
}

In this example, the printHello() method of the MyComponent class object has been defined, which simply prints out a hello message.

To render the component's HTML content, the class must have a template static property that defines a string. From this line, the HTML markup of the future component will be created:

static template = `
  <h1>Hello, {{ message }}!</h1>
  
  <style>
    h1 {
      color: {{ color }};
    }
  </style>
`

To display the properties of the state object, or any other expression, double curly braces are used.

The template static property can be a method that executes in the context of the component's state object, which allows you to refer to the properties of that object using the this keyword and using template literals, for example:

static template() {
  const message = this.message
  const color = this.color

  return `
    <h1>Hello, ${message}!</h1>
    
    <style>
      h1 {
        color: ${color};
      }
    </style>
  `
}

Inside template literals, you can use substitutions to expose any expressions:

${ 5 + 6 }

However, with this method of specifying values, the reactivity of the specified properties of the state object is lost. Reactive states are defined only inside double curly braces or inside reactive attributes.

The template() method, like all the static methods of the component class discussed below, can be asynchronous. The example below simulates downloading data from the server:

static async template() {
  // get data one second after method call
  const message = await new Promise(ok => setTimeout(() => ok('Web components'), 1000))

  return `
    <h1>Hello, ${ message }!</h1>
    
    <style>
      h1 {
        color: {{ color }};
      }
    </style>
  `
}

If the component is created in the <template> tags, then the static property template is not specified. The HTML content of the component is determined by the content of this tag:

<!-- create component template MyComponent -->
<template class="MyComponent">
  <h1>Hello, {{ message }}!</h1>
        
  <style>
    h1 {
      color: {{ color }};
    }
  </style>

  <script>
    exports = class {
      message = 'Reacton'
      color = 'red'
    }
  </script>
</template>

By default, all components are created without Shadow DOM. This means that the styles they use affect the DOM of the entire document, not a specific component.

The static mode property determines the level of encapsulation for the component to use local styles and can be either "open" or "closed":

static mode = 'open'

The example below creates a component with a closed Shadow DOM:

class MyComponent {
  message = 'Reacton'
  color = 'red'

  static mode = 'closed' // add closed Shadow DOM

  static template = `
    <h1>Hello, {{ message }}!</h1>
    
    <style>
      h1 {
        color: {{ color }};
      }
    </style>
  `
}

This type of component is the most secure, since access to the state and DOM of such a component is possible only from static methods or inside double curly braces of the component's contents.

The extends static property allows mount the component into a standard HTML element, for example:

static extends = 'header'

The element into which the component is mounted must contain the is attribute with a value corresponding to the name of the component that is mounted into it:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component to the Header element -->
  <header is="my-component"></header>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create component class MyComponent
    class MyComponent {
      message = 'Reacton'
      color = 'red'

      static extends = 'header' // mount the component to the Header element

      static template = `
        <h1>Hello, {{ message }}!</h1>
        
        <style>
          h1 {
            color: {{ color }};
          }
        </style>
      `
    }

    // pass component class MyComponent to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

The static property attributes contains an array with the names of attributes, when changing which, the static method changed() will be called, for example:

static attributes = ['title'] // tracked attributes

// called when the tracked attribute changes
static changed(name, oldValue, newValue) {
  console.log(name, oldValue, newValue)
}

Tracked attributes are a Web Component technology, and the changed() method is a shorthand for the attributeChangedCallback() method.

Add the id and title attributes to the MyComponent component's mount element in the index.html file as shown below:

<!-- mount the MyComponent component -->
<my-component id="mycomp" title="Reacton"></my-component>

The id attribute is used for quick access to the component in the browser console. Now open this console and enter the command:

mycomp.title = 'Web Components'

After pressing the Enter key, the changed() method will print the following line to the console:

title Reacton Web Components

Tracked attributes can be used to determine the state in a component, without having to define the state in a class, for example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp" message="Reacton" color="red"></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create component class MyComponent
    class MyComponent {
      static attributes = ['message', 'color'] // tracked attributes

      // called when the tracked attribute changes
      static changed(name, oldValue, newValue) {
        // update the HTML content of the component based on the new state
        this[name] = newValue
      }

      static template = `
        <h1>Hello, {{ message }}!</h1>
        
        <style>
          h1 {
            color: {{ color }};
          }
        </style>
      `
    }

    // pass component class MyComponent to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

As you can see from this example, there is no state definition in the class:

message = 'Reacton'
color = 'red'

The initial state values are defined in the tracked attributes message and color as shown below:

<!-- mount the MyComponent component -->
<my-component id="mycomp" message="Reacton" color="red"></my-component>

The assignment of these values to properties of the state object occurs in the changed() method, which is called every time values are Assigned/Changed to tracked attributes:

// called when the tracked attribute changes
static changed(name, oldValue, newValue) {
  // update the HTML content of the component based on the new state
  this[name] = newValue
}

This method is called in the context of the component's state object, and inside it, using the this keyword, new properties are assigned to the state object.

Now open the browser console and enter two commands:

mycomp.$state.message = 'Web Components'
mycomp.$state.color = 'green'

The title color and message will change immediately:

The second way to update the component's HTML content based on the new state value is to use the $props special property, which is used to quickly access all of the component's attributes.

Enter the command in the browser console:

mycomp.$props.color = 'blue'

The title color will change immediately:

Special methods and properties will be discussed in the next section. They all begin with a dollar sign and are defined internally by the component.

The static methods connected(), disconnected() and adopted() are shorthand analogs of the connectedCallback(), disconnectedCallback() and adoptedCallback() methods.

They are called when a component is added to the document - the connected() method; removing a component from a document - the disconnected() method; and when moving the component to a new document - the adopted() method.

The most commonly used methods include the connected() method, which allows you to access the HTML content of the component after it has been added to the DOM, for example , add an event to the element:

// called when the component is added to the document
static connected() {
  // output the element that generated the event to the console
  this.$('h1').addEventListener('click', event => console.log(event.target))
}

The static methods before() and after() are called Before and After updating the component's DOM, for example:

static before() {
  console.time('Update')
}

static after() {
  console.timeEnd('Update')
}

This example shows how long it takes for a component's DOM to update.

Another good example is using the before() method to check the type of a new state value:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create component class MyComponent
    class MyComponent {
      name = 'John'
      age = 32

      // called before updating the component's DOM
      static before() {
        // if the value is not a number, then generate an error
        if (typeof this.age !== 'number') {
          throw new Error('Value must be a number...')
        }
      }

      static template = `
        <p>Name: {{ name }}</p>
        <p>Age: {{ age }}</p>
      `
    }

    // pass component class MyComponent to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

If you enter the command in the browser console:

mycomp.$state.age = 'thirty five'

then you will receive an error message:

Error: Value must be a number...

Unlike methods and properties defined by the user in the component class, special methods and properties are defined at the internal level of the component and always start with a dollar sign. It is not recommended to give states names that are the same as special property names. This may lead to errors!

The $shadow property returns the Shadow DOM of the component, which is created if the mode static property was defined in the component class:

static mode = 'open' // add Shadow DOM

However, if the component has a closed Shadow DOM:

static mode = 'closed' // add closed Shadow DOM

then the $shadow property returns «null», as shown below:

mycomp.$shadow
null

The $light property returns True if the component does not contain a Shadow DOM, otherwise it returns False, for example:

mycomp.$light
true

The $host property returns a reference to the component itself if the component has an open Shadow DOM. If the component has a closed Shadow DOM or is created without one, then this property returns «undefined», as shown below:

mycomp.$host
undefined

The $props property allows you to quickly set and get component attribute values. For closed components, calling this property from outside of static methods returns «undefined».

Add the title attribute to the component, as shown below:

<!-- mount the MyComponent component -->
<my-component id="mycomp" title="Reacton"></my-component>

To get the value of the title attribute, enter the command in the browser console:

mycomp.$props.title

To set a new value for this attribute, enter the command:

mycomp.$props.title = 'Web Components'

To initialize the state of a component using attributes passed to its mount element, the component's constructor uses the props parameter, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component message="Reacton" color="red"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h1>Hello, {{ message }}!</h1>
          
    <style>
      h1 {
        color: {{ color }};
      }
    </style>

    <script>
      exports = class {
        // initializing state with values from attributes
        constructor(props) {
          this.message = props.message
          this.color = props.color
        }
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

The $state property allows you to Get/Set the value of any state property. For closed components, calling this property from outside of static methods returns «undefined».

To get the state value of message, enter the command in the browser console:

mycomp.$state.message

To change this state, issue the command:

mycomp.$state.message = 'Web Components'

All of the custom and static methods of the bean class discussed earlier are executed in the context of the state object referenced by the $state property. This object is a proxy. This means that if the requested state does not exist in the given object, then the requested property is searched for in the component itself. However, writing a new value always occurs in the state object.

Thanks to this, any property of the component can be accessed from the state object, such as the attributes property:

mycomp.$state.attributes['id'].value

Inside double curly braces, access to the properties of the component, and not the state, for example, to the property attributes, is carried only using the this keyword:

{{ this.attributes['id'].value.toUpperCase() }}

As shown in the example below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h1>Hello, {{ message }} 
      from the {{ this.attributes['id'].value.toUpperCase() }} component!</h1>
          
    <style>
      h1 {
        color: {{ color }};
      }
    </style>

    <script>
      exports = class {
        message = 'Reacton'
        color = 'red'
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

The $() method is a shorthand analog of the querySelector() method and is used for quick access to a component's DOM element. For closed components, calling this method from outside of static methods returns «undefined».

The method is used, for example, to assign an event listener:

// called when the component is added to the document
static connected() {
  // output the element that generated the event to the console
  this.$('h1').addEventListener('click', event => console.log(event.target))
}

The $$() method is a shorthand analog of the querySelectorAll() method and is used for quick access to a component's DOM element. For closed components, calling this method from outside of static methods returns «undefined».

This method is used, for example, to iterate over a collection of elements:

// called when the component is added to the document
static connected() {
  // output all paragraph elements to the console
  this.$$('p').forEach(elem => console.log(elem))
}

The $entities() method allows you to render harmless data received from unreliable sources:

static async template() {
  // render harmless data received from an unreliable source
  const message = this.$entities(await new Promise(ok => setTimeout(() => ok('<em>unsafe code</em>'), 1000)))

  return `
    <h1>Hello, ${ message }!</h1>
    
    <style>
      h1 {
        color: {{ color }};
      }
    </style>
  `
}

The $event() method is used to create custom events that allow different components to interact with each other, and the \$route() method is used to build routing. The \$refs property allows you to access elements using reference attributes. They will be considered later, since they require separate chapters for their explanation.

In addition to state, class objects can also have methods, for example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp1"></my-component>

  <!-- mount the MyComponent component -->
  <my-component id="mycomp2"></my-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create component class MyComponent
    class MyComponent {
      message = 'Reacton'
      color = 'red'

      // class object method
      printHello() {
        return `Hello, ${ this.message }!`
      }

      static template = `
        <h1>{{ printHello() }}</h1>
        
        <style>
          h1 {
            color: {{ color }};
          }
        </style>
      `
    }

    // pass component class MyComponent to Reacton plugin
    Reacton(MyComponent)
  </script>
</body>
</html>

In this example, the printHello() method of the MyComponent class object has been defined, which simply prints out a hello message for all components of this type.

In order not to create the same methods for different types of components, you can create a separate class for common methods, and then, inherit component classes from this method class, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- mount the NewComponent component -->
  <new-component id="newcomp"></new-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create a Methods class to store common methods
    class Methods {
      printHello() {
        return `Hello, ${ this.message }!`
      }
    }

    // inherit the MyComponent class from the Methods class
    class MyComponent extends Methods {
      message = 'Reacton'
      color = 'red'

      static template = `
        <h1>{{ printHello() }}</h1>
        
        <style>
          h1 {
            color: {{ color }};
          }
        </style>
      `
    }

    // inherit the NewComponent class from the Methods class
    class NewComponent extends Methods {
      message = 'NewComponent'

      static template = `
        <h2>{{ printHello() }}</h2>
      `
    }

    // pass component classes to Reacton plugin
    Reacton(MyComponent, NewComponent)
  </script>
</body>
</html>

This method is also used when defining components in <template> tags, for example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- mount the NewComponent component -->
  <new-component id="newcomp"></new-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h1>{{ printHello() }}</h1>
          
    <style>
      h1 {
        color: {{ color }};
      }
    </style>

    <script>
      // inherit the MyComponent class from the Methods class
      exports = class extends Methods {
        message = 'Reacton'
        color = 'red'
      }
    </script>
  </template>

  <!-- create component template NewComponent -->
  <template class="NewComponent">
    <h2>{{ printHello() }}</h2>

    <script>
      // inherit the NewComponent class from the Methods class
      exports = class extends Methods {
        message = 'NewComponent'
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create a Methods class to store common methods
    class Methods {
      printHello() {
        return `Hello, ${ this.message }!`
      }
    }

    // pass component templates to Reacton plugin
    Reacton(...document.querySelectorAll('template'))
  </script>
</body>
</html>

When defining components in <template> tags, the super class:

// create a Methods class to store common methods
class Methods {
  printHello() {
    return `Hello, ${ this.message }!`
  }
}

must be globally accessible.

To create reactive attributes, precede their name with a colon character «:». All reactive attributes are executed in the context of a state object. Expressions in reactive attributes are specified without the use of double curly braces, as opposed to HTML content, for example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h1 :title="message" :hidden="hide">Hello, {{ message }}!</h1>
    <button :onclick="changeMessage">Change message</button>
    <button :onclick="color = 'green'">Change color</button>
    <button :onclick="hide = !hide">Hide/Show</button>
          
    <style>
      h1 {
        color: {{ color }};
      }
    </style>

    <script>
      exports = class {
        message = 'Reacton'
        color = 'red'
        hide = false

        changeMessage() {
          this.message = 'Web Components'
        }
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

As you can see from this example, not only simple, but also event attributes and boolean attributes can be reactive.

All event attributes receive an implicit event parameter, which allows you to use it to access the elements on which the event was triggered, for example, input fields:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h3>Name: {{ name }}</h3>
    <h3>Age: {{ age }}</h3>
      
    <p>Name: <input type="text" :value="name" :oninput="changeName"></p>
    <p>Age: <input type="number" min="0" max="120" :value="age" :oninput="age = event.target.value"></p>

    <script>
      exports = class {
        name = 'John'
        age = 32

        changeName(event) {
          this.name = event.target.value
        }
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

The is attribute is used to mount components into standard HTML elements. This attribute can be made reactive:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <!-- components mount element -->
    <div :is="view"></div>

    <button :onclick="view = 'component-a'">Component-A</button>
    <button :onclick="view = 'component-b'">Component-B</button>

    <script>
      exports = class {
        view = 'component-a' // initial value
      }
    </script>
  </template>

  <!-- create component template ComponentA -->
  <template class="ComponentA">
    <h1>Component-A</h1>

    <script>
      exports = class {
        static extends = 'div' // mounting element
      }
    </script>
  </template>

  <!-- create component template ComponentB -->
  <template class="ComponentB">
    <h1>Component-B</h1>

    <script>
      exports = class {
        static extends = 'div' // mounting element
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component templates to Reacton plugin
    Reacton(...document.querySelectorAll('template'))
  </script>
</body>
</html>

Mounted components must contain a static property extends with a value corresponding to the name of the element in which they are mounted:

exports = class {
  static extends = 'div' // mounting element
}

In addition, if the element in which the components are mounted contains other reactive attributes, then all of them must be specified before the is attribute, for example:

<!-- components mount element -->
<div :title="view" :onclick="console.log(view)" :is="view"></div>

To quickly access elements within a component, you can use reference attributes that begin with a «#» character followed by the name of the reference attribute without a value, for example:

<h1 #hello>Hello, {{ message }}!</h1>

For closed components, calling this property from outside of static methods returns «undefined».

In this example, the H1 element has been assigned the link attribute hello. To get the element to which the attribute has been assigned, the special property $refs is used, which is an object containing all the reference attributes of the component and the elements they refer to.

This can be used, for example, to assign custom event handlers to elements:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <h1 #hello>Hello, {{ message }}!</h1>
          
    <style>
      h1 {
        color: {{ color }};
      }
    </style>

    <script>
      exports = class {
        message = 'Reacton'
        color = 'red'

        // called when the component is added to the document
        static connected() {
          // output the element that generated the event to the console
          this.$refs.hello.addEventListener('click', event => console.log(event.target))
        }
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

To pass data from parent components to child components, you can use slots, attributes, and events. Slots and attributes allow you to transfer only text data, but with the help of events, you can exchange any data between any components, not just child ones.

Make changes to the index.html file, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent template">
    <!-- mount the SubComponent component -->
    <sub-component :color="color">
      <h1>Hello, {{ message }}!</h1>
    </sub-component>

    <script>
      exports = class {
        message = 'Reacton'
        color = 'red'
      }
    </script>
  </template>

  <!-- create component template SubComponent -->
  <template class="SubComponent template">
    <slot></slot>
          
    <style>
      ::slotted(h1) {
        color: {{ $props.color }};
      }
    </style>

    <script>
      exports = class {
        static mode = 'open' // add Shadow DOM
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    //pass component templates to Reacton plugin
    Reacton(...document.querySelectorAll('.template'))
  </script>
</body>
</html>

In this example, the state value of the color property from the parent component MyComponent is passed to the color attribute of the child component SubComponent, and the value of the state property message is passed to the slot of the child component:

<!-- mount the SubComponent component -->
<sub-component :color="color">
  <h1>Hello, {{ message }}!</h1>
</sub-component>

Within this component, the attribute value can be accessed using the special property $props, as shown below:

<style>
  ::slotted(h1) {
    color: {{ $props.color }};
  }
</style>

To style elements passed to slots, the ::slotted pseudo-class is used. In addition, the child SubComponent uses a <slot> element, which is the default slot:

<slot></slot>

into which the title from the parent component MyComponent will be inserted:

<h1>Hello, {{ message }}!</h1>

When you change the value of the message state property of the parent component, the contents of the slot in the child will also change. For example, enter the command in the browser console:

mycomp.$state.message = 'Web Components'

But when the value of the state property passed through attributes changes, no changes will occur in the child component:

mycomp.$state.color = 'green'

In addition, only text data, not objects, can be passed to child components through slots and attributes. But all these problems are solved with the help of events that will be discussed later.

However, if you add a state property to the constructor of a child component that gets a value from an attribute, and make this attribute trackable, then when the state in the parent component changes, the state in the child will also change.

Make changes to the SubComponent class as shown below:

exports = class {
  constructor(props) {
    // assign the state property the value from the attribute
    this.color = props.color
  }

  static mode = 'open' // add Shadow DOM

  static attributes = ['color'] // tracked attribute

  // called when the tracked attribute changes
  static changed(name, oldValue, newValue) {
    // assign the state property a new value from the attribute
    this.color = newValue
  }
}

Now make changes to the styles of this component so as not to pass the value of the color attribute directly:

color: {{ $props.color }};

and pass the value of the color state property of the child component:

<style>
  ::slotted(h1) {
    color: {{ color }};
  }
</style>

Now the title color in the child component will change to green if you enter the following command in the browser console:

mycomp.$state.color = 'green'

Reacton supports three kinds of «for» loops that are implemented in JavaScript. They are all defined with a special $for attribute and output the contents of their HTML elements as many times as required by the loop condition.

In the example below, the «for» loop outputs 10 paragraphs with numbers from 0 to 9:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <!-- output 10 paragraphs -->
    <div $for="i = 0; i < 10; i++">
      <p>Number: {{ i }}</p>
    </div>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

The $for special attribute cannot use variable definition operators: var, let, and const, respectively. This will result in an error:

<!-- output 10 paragraphs -->
<div $for="var i = 0; i < 10; i++">
  <p>Number: {{ i }}</p>
</div>

The «for-in» loop is used to output the contents of objects, as shown below:

<!-- create component template MyComponent -->
<template class="MyComponent">
  <!-- output the contents of object -->
  <ul $for="prop in user">
    <li>
      <b>{{ prop }}</b>: {{ user[prop] }}
    </li>
  </ul>

  <script>
    exports = class {
      user = {
        name: 'John',
        age: 32
      }
    }
  </script>
</template>

The «for-of» loop is designed to work with arrays:

<!-- create component template MyComponent -->
<template class="MyComponent">
  <!-- output the contents of the array -->
  <ul $for="col of colors">
    <li>{{ col }}</li>
  </ul>

  <script>
    exports = class {
      colors = ['red', 'green', 'blue']
    }
  </script>
</template>

Event attributes of loop HTML elements can be bound to loop variables:

<!-- output the contents of the array -->
<ul $for="col of colors">
  <li :onclick="console.log(col)">{{ col }}</li>
</ul>

Events will always use the current value of the loop variable for their iteration phase, even after the array has been modified:

<!-- create component template MyComponent -->
<template class="MyComponent">
  <!-- array reversal button -->
  <button :onclick="colors.reverse()">Reverse array</button>

  <!-- output the contents of the array -->
  <ul $for="col of colors">
    <li :onclick="console.log(col)">{{ col }}</li>
  </ul>

  <script>
    exports = class {
      colors = ['red', 'green', 'blue']
    }
  </script>
</template>

You can use loops with any nesting depth in Reacton:

<!-- create component template MyComponent -->
<template class="MyComponent">
  <!-- output an array of objects -->
  <div $for="user of users">
    <div>
      <p>
        <b>Name</b>: {{ user.name }}
      </p>
      <p>
        <b>Age</b>: {{ user.age }}
      </p>
      <div $for="category in user.skills">
        <b>{{ category[0].toUpperCase() + category.slice(1) }}</b>:
        <ol $for="item of user.skills[category]">
          <li>{{ item }}</li>
        </ol>
      </div>
    </div>
    <hr>
  </div>

  <script>
    exports = class {
      users = [
        {
          name: 'John',
          age: 32,
          skills: {
            frontend: ['HTML', 'CSS'],
            backend: ['Ruby', 'PHP', 'MySQL']
          }
        },
        {
          name: 'Clementine',
          age: 25,
          skills: {
            frontend: ['HTML', 'JavaScript'],
            backend: ['PHP']
          }
        },
        {
          name: 'Chelsey',
          age: 30,
          skills: {
            frontend: ['HTML', 'CSS', 'JavaScript', 'jQuery'],
            backend: ['Ruby', 'MySQL']
          }
        }
      ]
    }
  </script>
</template>

To create local styles, the component needs to add a Shadow DOM using the static property mode, as shown below:

class MyComponent {
  message = 'Reacton'
  color = 'red'

  static mode = 'open' // add Shadow DOM

  static template = `
    <h1>Hello, {{ message }}!</h1>
    
    <style>
      h1 {
        color: {{ color }};
      }
    </style>
  `
}

To work with slots, the component needs to add a Shadow DOM using the static property mode, as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component>
    <span slot="username">John</span>
    <span slot="age">32</span>
    <span>Hardworking</span>
  </my-component>

  <!-- create component template MyComponent -->
  <template class="MyComponent">
    <div>
      Name: <slot name="username"></slot>
    </div>
    <div>
      Age: <slot name="age"></slot>
    </div>
    <div>
      Character: <slot><slot>
    </div>

    <script>
      exports = class {
        static mode = 'open' // add Shadow DOM
      }
    </script>
  </template>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // pass component template MyComponent to Reacton plugin
    Reacton(document.querySelector('.MyComponent'))
  </script>
</body>
</html>

For interaction between different components, an improved mechanism of custom events is used. This mechanism involves the use of the event() method of the Reacton plugin and the special $event() method that is available in every component.

When the Reacton plugin's event() method is called as a constructor, it returns a new document fragment that is the source and receiver of custom events. And when this method is not called as a constructor, it works similarly to the special method $event(). This allows you to link components not only to each other, but also to any external code.

Make changes to the index.html file as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- mount the NewComponent component -->
  <new-component id="newcomp"></new-component>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create event element myEvent
    const myEvent = new Reacton.event()

    // create component class NewComponent
    class NewComponent {
      colors = ['red', 'green', 'blue']

      static template = `
        <ul $for="col of colors">
          <li>{{ col }}</li>
        </ul>
      `

      static connected() {
        // add a "reverse" event handler to the myEvent element
        myEvent.addEventListener('reverse', () => {
          this.colors.reverse() // reverse array
        })
      }
    }

    // create component class MyComponent
    class MyComponent {
      static template = `
        <button id="btn-reverse">Reverse array</button>
      `

      static connected() {
        // add a "click" event handler for the button
        this.$('#btn-reverse').addEventListener('click', () => {
          // trigger "reverse" event on element myEvent
          this.$event(myEvent, 'reverse')
        })
      }
    }

    // pass component classes to Reacton plugin
    Reacton(MyComponent, NewComponent)
  </script>
</body>
</html>

In this example, a new event element myEvent is first created:

// create event element myEvent
const myEvent = new Reacton.event()

This element will be assigned custom event handlers in some components and invoked in others.

In the static method connected() of the NewComponent component class, the handler for the custom event "reverse" is assigned to the myEvent element. Inside this handler, the array is reverse and the DOM of the component is updated:

static connected() {
  // add a "reverse" event handler to the myEvent element
  myEvent.addEventListener('reverse', () => {
    this.colors.reverse() // reverse array
  })
}

In the static method connected() of the MyComponent component class, a "click" event handler is added to the button, inside which the "reverse" event is called for the myEvent element, as shown below:

static connected() {
  // add a "click" event handler for the button
  this.$('#btn-reverse').addEventListener('click', () => {
    // trigger "reverse" event on element myEvent
    this.$event(myEvent, 'reverse')
  })
}

The first argument of the special $event() method is the event element myEvent, and the second argument is the name of the event to be called:

this.$event(myEvent, 'reverse')

The $event() method can also receive a third argument, in which you can pass parameters that fully correspond to the parameters of the CustomEvent constructor. For example, you can pass the detail property, which allows you to share data between components.

Add a new "new-colors" event handler to the static connected() method of the NewComponent component, as shown below:

static connected() {
  // add a "reverse" event handler to the myEvent element
  myEvent.addEventListener('reverse', () => {
    this.colors.reverse() // reverse array
  })

  // add a "new-colors" event handler to the myEvent element
  myEvent.addEventListener('new-colors', event => {
    this.colors = event.detail // assign new array
  })
}

Note that the event handler now has an event parameter through which you can access the detail property. In addition, it is recommended to add a hyphen to the names of custom events so that they do not overlap with the names of standard events.

Now modify the markup of the MyComponent component by adding a new button to it:

static template = `
  <button id="btn-reverse">Reverse array</button>
  <button id="btn-new">New array</button>
`

and the "click" event handler, inside which a new array of colors is passed to the "new-colors" event handler:

static connected() {
  // add a "click" event handler for the button
  this.$('#btn-reverse').addEventListener('click', () => {
    // trigger "reverse" event on element myEvent
    this.$event(myEvent, 'reverse')
  })

  // add a "click" event handler for the button
  this.$('#btn-new').addEventListener('click', () => {
    // trigger "new-colors" event on element myEvent
    this.$event(myEvent, 'new-colors', {
      // pass a new array to the event handler
      detail: ['blue', 'orange', 'purple', 'gold']
    })
  })
}

In this way, data can be easily exchanged between different components.

To demonstrate the interaction of components with external code, add a button to clear the array in the markup of the index.html file:

<!-- mount the MyComponent component -->
<my-component id="mycomp"></my-component>

<!-- mount the NewComponent component -->
<new-component id="newcomp"></new-component>

<!-- clear array button -->
<button id="btn-clear">Clear array</button>

Add a new "clear-colors" event handler to the static connected() method of the NewComponent component, as shown below:

static connected() {
  // add a "reverse" event handler to the myEvent element
  myEvent.addEventListener('reverse', () => {
    this.colors.reverse() // reverse array
  })

  // add a "new-colors" event handler to the myEvent element
  myEvent.addEventListener('new-colors', event => {
    this.colors = event.detail // assign new array
  })

  // add a "clear-colors" event handler to the myEvent element
  myEvent.addEventListener('clear-colors', event => {
    this.colors.length = 0 //  clear array
  })
}

and the "click" event handler for the new button at the end of the script:

// add a "click" event handler for the button
document.querySelector('#btn-clear').addEventListener('click', () => {
  // trigger "clear-colors" event on element myEvent
  Reacton.event(myEvent, 'clear-colors')
})

// pass component classes to Reacton plugin
Reacton(MyComponent, NewComponent)

Inside this handler, the "clear-colors" event for the myEvent element is called using the event() method of the plugin itself:

// trigger "clear-colors" event on element myEvent
Reacton.event(myEvent, 'clear-colors')

rather than using the special $event() method, which is only available in components, but is essentially just a reference to the event() method of the Reacton plugin.

Below is the full content of the index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyComponent component -->
  <my-component id="mycomp"></my-component>

  <!-- mount the NewComponent component -->
  <new-component id="newcomp"></new-component>

  <!-- clear array button -->
  <button id="btn-clear">Clear array</button>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create event element myEvent
    const myEvent = new Reacton.event()

    // create component class NewComponent
    class NewComponent {
      colors = ['red', 'green', 'blue']

      static template = `
        <ul $for="col of colors">
          <li>{{ col }}</li>
        </ul>
      `

      static connected() {
        // add a "reverse" event handler to the myEvent element
        myEvent.addEventListener('reverse', () => {
          this.colors.reverse() // reverse array
        })

        // add a "new-colors" event handler to the myEvent element
        myEvent.addEventListener('new-colors', event => {
          this.colors = event.detail // assign new array
        })

        // add a "clear-colors" event handler to the myEvent element
        myEvent.addEventListener('clear-colors', event => {
          this.colors.length = 0 //  clear array
        })
      }
    }

    // create component class MyComponent
    class MyComponent {
      static template = `
        <button id="btn-reverse">Reverse array</button>
        <button id="btn-new">New array</button>
      `

      static connected() {
        // add a "click" event handler for the button
        this.$('#btn-reverse').addEventListener('click', () => {
          // trigger "reverse" event on element myEvent
          this.$event(myEvent, 'reverse')
        })

        // add a "click" event handler for the button
        this.$('#btn-new').addEventListener('click', () => {
          // trigger "new-colors" event on element myEvent
          this.$event(myEvent, 'new-colors', {
            // pass a new array to the event handler
            detail: ['blue', 'orange', 'purple', 'gold']
          })
        })
      }
    }

    // add a "click" event handler for the button
    document.querySelector('#btn-clear').addEventListener('click', () => {
      // trigger "clear-colors" event on element myEvent
      Reacton.event(myEvent, 'clear-colors')
    })

    // pass component classes to Reacton plugin
    Reacton(MyComponent, NewComponent)
  </script>
</body>
</html>

To create routing, an improved custom events mechanism is used. This mechanism involves the use of the route() method of the Reacton plugin and the special $route() method that is available in every component.

When the Reacton plugin's route() method is called as a constructor, it returns a new document fragment that is the source and receiver of custom events. And when this method is not called as a constructor, it works similarly to the special method $route(). This allows you to connect the components involved in routing not only among themselves, but also with any external code.

Unlike the event() method, the route() method, called as a constructor, returns document fragments with an improved addEventListener() method, which allows regular expression characters to be used in event names.

Make changes to the index.html file as shown below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reacton</title>
</head>
<body>
  <!-- mount the MyMenu component -->
  <my-menu></my-menu>

  <!-- mount the MyContent component -->
  <my-content></my-content>

  <!-- include Reacton plugin -->
  <script src="reacton.min.js"></script>

  <script>
    // create event element myRoute
    const myRoute = new Reacton.route()

    // create component class MyHome
    class MyHome {
      static extends = 'div' // mounting element
      static template = '<h2>Home</h2>'
    }

    // create component class MyAbout
    class MyAbout {
      static extends = 'div' // mounting element
      static template = '<h2>About</h2>'
    }

    // create component class MyContacts
    class MyContacts {
      static extends = 'div' // mounting element
      static template = '<h2>Contacts</h2>'
    }

    // create component class MyMenu
    class MyMenu {
      static template = `
        <nav>
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/contacts">Contacts</a>
        </nav>
      `

      static connected() {
        // add a "click" event handler to the NAV element
        this.$('nav').addEventListener('click', event => {
          // cancel clicking on the link
          event.preventDefault()

          // trigger a link address event on myRoute element
          this.$route(myRoute, event.target.href)
        })
      }
    }

    // create component class MyContent
    class MyContent {
      page = 'my-home' // initial state value

      // components mount element
      static template = '<div :is="page"></div>'

      static connected() {
        // add a "/" event handler to the myRoute element
        myRoute.addEventListener('/', () => {
          this.page = 'my-home' // assign a value
        })

        // add a "/about" event handler to the myRoute element
        myRoute.addEventListener('/about', () => {
          this.page = 'my-about' // assign a value
        })

        // add a "/contacts" event handler to the myRoute element
        myRoute.addEventListener('/contacts', () => {
          this.page = 'my-contacts' // assign a value
        })
      }
    }

    // pass component classes to Reacton plugin
    Reacton(MyHome, My
3.4.20

7 months ago

3.4.21

7 months ago

3.4.22

7 months ago

3.4.14

7 months ago

3.4.15

7 months ago

3.4.16

7 months ago

3.4.17

7 months ago

3.4.18

7 months ago

3.4.4

7 months ago

3.4.19

7 months ago

3.4.3

7 months ago

3.4.2

7 months ago

3.4.1

7 months ago

3.4.10

7 months ago

3.4.11

7 months ago

3.4.12

7 months ago

3.4.8

7 months ago

3.4.7

7 months ago

3.4.6

7 months ago

3.4.5

7 months ago

3.4.9

7 months ago

3.4.0

7 months ago

3.2.2

7 months ago

3.2.1

7 months ago

3.2.0

7 months ago

3.2.6

7 months ago

3.2.5

7 months ago

3.2.4

7 months ago

3.2.3

7 months ago

3.2.9

7 months ago

3.2.8

7 months ago

3.2.7

7 months ago

3.3.1

7 months ago

3.3.0

7 months ago

3.0.1

8 months ago

3.0.0

8 months ago

3.1.3

8 months ago

3.1.2

8 months ago

3.1.1

8 months ago

3.1.0

8 months ago

3.1.7

8 months ago

3.1.6

8 months ago

3.1.5

8 months ago

3.1.4

8 months ago

2.2.1

1 year ago

2.2.0

1 year ago

2.4.1

1 year ago

2.2.3

1 year ago

2.4.0

1 year ago

2.2.2

1 year ago

2.4.3

1 year ago

2.2.5

1 year ago

2.4.2

1 year ago

2.2.4

1 year ago

2.4.5

1 year ago

2.4.4

1 year ago

2.2.6

1 year ago

2.3.0

1 year ago

2.1.2

1 year ago

2.1.1

1 year ago

2.3.2

1 year ago

2.3.1

1 year ago

2.1.3

1 year ago

2.3.3

1 year ago

2.4.10

1 year ago

2.4.12

1 year ago

2.4.11

1 year ago

2.1.0

1 year ago

2.4.7

1 year ago

2.4.6

1 year ago

2.4.9

1 year ago

2.4.8

1 year ago

1.2.10

2 years ago

1.2.8

2 years ago

1.2.9

2 years ago

1.2.0

2 years ago

1.2.7

2 years ago

1.2.6

2 years ago

1.2.5

2 years ago

1.2.4

2 years ago

1.2.3

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.1.12

2 years ago

1.1.11

2 years ago

1.1.10

2 years ago

1.1.9

2 years ago

1.1.8

2 years ago

1.1.7

2 years ago

1.1.6

2 years ago

1.1.5

2 years ago

1.1.4

2 years ago

1.1.3

2 years ago

1.1.2

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.14

2 years ago

1.0.13

2 years ago

1.0.12

2 years ago

1.0.11

2 years ago

1.0.10

2 years ago

1.0.9

2 years ago

1.0.8

2 years ago

1.0.7

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago