dna

Evolution-based components.

JSX Templates

JSX is an addon of standard JavaScript with XML syntax support. It was developed by Facebook for its components library React as a sort of template engine but it has been adopted rapidly by other projects. Because its addon nature, JSX requires a transpiling step, usually with babel, that can transform the XML syntax in common javascript, interpretable by (ideally) any other library, so a lot of projects started to make their own transpilers. Right now, you can use Javascript with some awesome projects like:

If you care about interoperability but you can not give up with a powerful syntax, JSX could help you to write reusable templates that work in applications which are using different virtual dom implementations. What you need is just a DNA mixin adapter (@dnajs/idom and @dnajs/react already have their own).

Usage with @dnajs/idom

The DNA IncrementalDOM package exports a wrapper around IDOM api that can be used by JSX transpiled code.

You need to install the babel-plugin-transform-react-jsx plugin:

npm install babel-core babel-preset-es2015 babel-plugin-transform-react-jsx --save-dev

and configure your .babelrc in something like this:

{
    "presets": ["es2015"],
    "plugins": [
        ["transform-react-jsx", { "pragma": "IDOM.h" }],
    ],
}

IDOM is the IncrementalDOM wrapper exported by DNA, while its h method takes care of handle custom elements. Now you can define your component using JSX:

import { BaseComponent, IDOM, define } from '@dnajs/idom';

class MyComponent extends BaseComponent {
    get template() {
        return <div class="hello">Hello world!</div>;
    }
}

define('my-component', MyComponent);

Use external JSX template

For a better code organization and reusability, it is possible to use external JSX templates in a DNA component class. In order to do that, we have to first have a JSX transpiler, like babel, and use specific babel presets that we collect in this custom preset. Our package.json will look like this:

package.json

{
  "name": "dna-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.0.0-beta.55",
    "@babel/preset-env": "^7.0.0-beta.55",
    "@chialab/babel-preset": "^1.0.1",
    "babel-loader": "^8.0.0-beta.4",
    "webpack": "^4.16.3",
    "webpack-cli": "^3.1.0"
  },
  "dependencies": {
    "@dnajs/idom": "^2.7.9"
  }
}

Install the dependencies.

npm install

And modify the .babelrc in order to add our preset.

.babelrc

{
    "presets": [[
        "module:@chialab/babel-preset", {
            "pragma": "IDOM.h",
            "pragmaModule": "@dnajs/idom",
            "targets": { "node": "current" }
        }
    ]]
}

Also we have to modify our Webpack configuration file in order to use .jsx files:

webpack.config.js

const path = require('path');
const webpack = require('webpack');

module.exports = {
    context: path.join(__dirname, 'app'),
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                loader: 'babel-loader',
            },
        ],
    },
    resolve: {
        modules: [
            path.join(__dirname, 'node_modules'),
        ],
    },
};

Then we create the template:

hello-world.jsx

<div>
    <h1> Hello World! </h1>
</div>

We can finally import it and use it as the component's template:

hello-world.js

import { BaseComponent, define, IDOM } from '@dnajs/idom';
import template from './hello-world.jsx';

export class HelloWorld extends BaseComponent {
    connectedCallback() {
        super.connectedCallback();
    }

    get template() {
        return template;
    }
}
define('hello-world', HelloWorld);

Conditions and loops in JSX

It is common to use conditions or loops inside JSX templates so we can create amazing dynamic views.

Let's see how we can do this! In the following component we define some properties

hello-world.js

import { BaseComponent, define, IDOM, prop } from '@dnajs/idom';
import template from './hello-world.jsx';

export class HelloWorld extends BaseComponent {
    connectedCallback() {
        super.connectedCallback();
    }

    get properties() {
        return {
            elements: prop(Array).default(['Hello', 'World']),
            hasToShowHelloWorld: prop(Boolean).default(true),
            sayMyName: prop(Boolean).default(true),
        };
    }

    getCurrentTimeString() {
        return `Current Time is:${Date.now()}`;   
    }

    get template() {
        return template;
    }
}
define('hello-world', HelloWorld);

That we can access from the external template through the this keyword. We can also access every method of the component class in the same way.

hello-world.jsx

[
    this.hasToShowHelloWorld ?
        this.elements.map(element => <h1> {element} </h1>)
        :
        <div> I don't wanna show an Hello World! </div>,
    this.sayMyName && <h2> I'm a DNA component </h2>,
    <h3> {this.getCurrentTimeString()} </h3>,
];