Advanced View Features in AppRun
Tags: apprun, javascript, frontend, framework
Published: 2020-02-28 18:30:29

---
title: Advanced View Features in AppRun
published: true
description: This post describes the advanced usage of the AppRun ref, element embedding, and directive in the JSX view.
tags: #AppRun #javascript #frontend #framework
---

Since the [AppRun book](https://www.amazon.com/Practical-Application-Development-AppRun-High-Performance/dp/1484240685/) was published, there are new features added to [AppRun](https://github.com/yysun/apprun), such as _ref_, _element embedding_, and _directive_. This post describes the advanced usage of these new features.

If you are new to AppRun, it is a JavaScript library for building web applications using the Elm inspired architecture, events, and components. Its architecture concept is to is broken down the application logic into three separate parts.

* State (a.k.a. Model) — the state of your application
* View — a function to display the state
* Update — a collection of event handlers to update the state

AppRun ties the three parts together and drives the applications using events.

## Introduction
AppRun support to HTML string, lit-html, and JSX in the _view_ function. We recommend using JSX. The advanced features apply to JSX only.

### ref

[ref](https://apprun.js.org/#play/12) is a special JSX property, which is a callback function that is called after the _view_ function is executed.
 
```javascript
const view = <div ref={el=>{...}}></div>
```

We can use _ref_ function to update the HTML element, e.g., creating d3 charts to the element. It provides a better method to the one described in the AppRun book, which is to use the _rendered_ lifecycle function. 

>Please think of using _ref_ function, before you use the _rendered_ function.


### Element embedding

Furthermore, AppRun allows [embedding elements directly into JSX](https://apprun.js.org/#play/14).

```javascript
view = state => {
  const canvas = document.createElement('canvas');
  return <div>{canvas}</div>
};
```

We don't need the _rendered_ lifecycle function nor the _ref_ function to integrate with other 3rd libraries. Just create the HTML element and add it to the AppRun _view_. 

>Please think of embedding the element before you use the _ref_ function.

The HTML element can be cached to avoid recreation in every event lifecycle, which will be explained below.

### Directive

The directive is the special property that looks like $xxx. When AppRun is processing the JSX code and finds the properties of $xxx, it publishes the $ event. The event parameters contain the directive key, properties, and tag Name of the HTML element, and component instance.

```javascript
const view = <div $myDirective></div>;
app.on('$', ({key, props, tag, component}) => {
  if (key === '$myDirective') {
  }
}
```

We can subscribe to the $ event and create _custom directives_ to modify the properties of the HTML element, e.g., [adding or removing classes](https://apprun.js.org/#play/11).

Now let's see some use cases of the features described above. 

## Example App

The [admin dashboard app](https://github.com/apprunjs/apprun-bootstrap) is an AppRun app built with Bootstrap. The app uses a few 3rd party libraries such as [Chart.js](https://www.chartjs.org/), [d3](https://d3js.org/), and [DataTables](https://datatables.net/).

![https://apprunjs.github.io/apprun-bootstrap/](https://dev-to-uploads.s3.amazonaws.com/i/a4opk3gkre18w2bhc4id.png)

### Chart

[Chart.js](https://www.chartjs.org/) creates charts using the _canvas_ element. We create a canvas element and then use Chart.js to create a chart. We embed the canvas directly into JSX.

```javascript
export default class extends Component {
  state = { data: {} }
  view = state => {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    state.chart = new Chart(ctx, state.data);
    return <div>{canvas}</div>;
  };

  unload = state => {
    state.chart?.destroy();
    console.log('chart destroyed');
  };
}
```
We also store the Chart object in the state, so that we can destory when the HTML element the component attached to is remove.

### Map

[D3](https://d3js.org/) creates maps using the _svg_ element. The height and width of the map are based on the height and width of the _svg_ element.  We use the _ref_ function to retrieve the _svg_ element for map creation.

```javascript
const map = (element, features) => { ... }

export default class extends Component {
  view = state => <svg ref={el => map(el, state.features)}></svg>;
}
```

We can cache the HTML to avoid re-creating it. Using the _ref_ function, we store the HTML element into the state. We can reuse the stored element inside the _view_ function if it presents.

```javascript
view = state =>
  state.map_card || (
    <Card header={<div>D3 Map</div>} ref={el => (state.map_card = el)}>
      <svg ref={el => map(el, state.features)}></svg>
    </Card>
  );
```

### Data Table

[DataTables](https://datatables.net/) is a plug-in for the jQuery. We create a _custom directive_ called _$data-table_ to attach a _ref_ function, which makes the DataTables out of the _table_ element.

```javascript

// checkout available options: https://datatables.net/reference/option/
const DataTable = options => table =>
  !$.fn.dataTable.isDataTable(table) && $(table).DataTable(options);

app.on('$', ({ key, props }) => {
  key === '$data-table' && (props.ref = DataTable(props[key]));
});
```

Then, we can use the _$data-table_ to make any _table_ into a _data table_.

```javascript
export default class extends Component {
  state = { data };

  view = state => <table
    class="table w-100 table-striped table-bordered"
    $data-table={{
      data: state.data,
      columns,
      pageLength: 10
    }}
    />
  );
}
```

In our real-world business applications, we use the _$data-table_ directive on many components/pages. It is very convenient. 

>We also can use the same method to integrate other jQuery plugins such as auto-complete and date-picker.

Finally, We add the caching to the data table as we did for caching the map. I believe you can recognize the pattern used below.

```javascript
export default class extends Component {
  state = { data };

  view = state => state.dt || <div ref={el => state.dt = el}>
    <table
      class="table w-100 table-striped table-bordered"
      $data-table={{
        data: state.data,
        columns,
        pageLength: 10
      }}
      />
  </div>;
}
```

The benefit of caching the data table is that the UI state of the data table is preserved. You can search, filter, or select the page of the data table. Then you go to another page. When coming back, the data table has the content and page remain as you selected. The data table is not re-created/re-rendered.

You can run the example app from https://github.com/apprunjs/apprun-bootstrap. Or checkout it repository:

{% github apprunjs/apprun-bootstrap%}

## Conclusion

Newly added AppRun features: _ref_, _element embedding_, and _directives_ provide much more power to the developers for building web applications using less code and in a declarative way. Please use the new features going forward.