Enyo Application Migration Guide

Overview

Migrating from Enyo to Enact can be a challenge. There are many new concepts to learn and, while many of the same Moonstone components exist, their APIs have changes. Given this, you can look at this as an opportunity to aggressively refactor your app.

***Please do not attempt to directly port your application. Enact is a wholly different approach to application structure

which is incompatible with the older techniques.***

This guide serves as a basic checklist of things to consider before you begin and is separated into General Considerations (developer tools, Spotlight, data management, etc.) and webOS Platform Support (platform APIs, services, etc.).

General Considerations

This section highlights some of the generic things that should be reviewed when moving an Enyo application to Enact, regardless of platform.

Application Structure

***The framework team strongly encourages the use of the enact cli tools to

create, test, and deploy Enact applications.***

Source files are generally arranged in the project like so:

project_root/   (package.json lives here)
  assets/       (images and other non-source content)
  resources/    (ilibmanifest.json lives here)
  src/          (this directory may be important)
  webos-meta/   (helpful companion files for packaging webOS applications)

Component Usage

While many Moonstone components retain the same names they have in Enyo, some have changed. We have prepared the Enyo to Enact Component Map to help with the transition.

In general, the content property is now handled by the implicit children property of components. Boolean properties can be shortened to just the property name. For example, moonstone/Button in Enyo was configured like this:

{ name: 'MyButton', kind: Button, small: true, content: 'Click Me!'}

In Enact, the same effect is achieved like this:

<Button size="small">Click Me!</Button>

Enact declarations are similar yet simpler than their Enyo counterparts. Further, some options and components that were not used have been removed in Enact. Please refer to module documentation to see the exact APIs for each component.

components Block to render() Method

With Enyo, you can declare which components are contained in a given kind or component. This is done by specifying them in the components property of the kind.

...
var InnerComponent = kind({
	name: 'InnerComponent',
	kind: enyo.Control,
	content: 'This is just a &lt;div&gt; with some text'
});
...
var OuterComponent = kind({
	name: 'OuterComponent',
	kind: enyo.Control,
	components: [
		{kind: InnerComponent}
	]
});
...

In Enact, a component can contain other components just as easily, but you use the render() method to declare them.

...
const InnerComponent = kind({
	name: 'InnerComponent',
	render: () => (
		<div>This is just a &lt;div&gt; with some text</div>
	)
});

const OuterComponent = kind({
	name: 'OuterComponent',
	render: () => {
		<div>
			<InnerComponent />
		</div>
	}
});
...
Getters/Setters

Enyo provides the ability to set or get any arbitrary property on a component:

...
var bar = '';
MyComponent.set('foo', 'bar');
bar = MyComponent.get('foo');  // bar === "bar"
...

This is further enhanced by allowing you to specify ‘published’ (or ‘public’ in later Enyo versions) properties and mapping individual set[Property]() and get[Property]() methods:

...
name: 'MyControl',
kind: enyo.Control,
published: {
	foo: 'bar'
}
...
var bar = '';
MyControl.setFoo('nobar');
bar = MyControl.getFoo();  // bar === "nobar"
...

In Enact, you ‘set’ properties by providing them to the rendered component(s):

...
import PropTypes from 'prop-types';
...
const MyComponent = kind({
	name: 'MyComponent',
	propTypes: {
		foo: PropTypes.string
	},
	defaultProps: {
		foo: 'bar'
	},
	render: ({foo}) => (
		<div>{foo}</div>
	)
});
...
// (another component that renders an instance of MyComponent)
...
	render: () => (
		<MyComponent foo="nobar" />
	)

Due to the one-way nature of data-flow in Enact, ‘get’ functionality is unnecessary. You will know the value from either the data state or store, depending on how you have implemented your component and application.

Computed Properties

Computed properties are almost identical in Enyo and Enact with one major difference. In Enyo, any computed property of a kind can access any other computed property of that kind. In Enact, computed properties are isolated from other computed properties.

Event Handling

Enyo has several different ways to handle events (enyo.Signals, the handlers property of a component, the onClick property of UiComponents, etc.). In Enact, event handling is a bit different.

To handle an event, use @enact/core/handle to create a handler. It accepts one or more input functions that will process or filter the event. The input functions will be processed in order until one returns false (or any falsy value).

...
const myHandler = handle(
	preventDefault, // imported from `@enact/core/handle`; convenience method for preventing default event; returns `true`
	(ev, props) => { // custom handling
		console.log('handling event');
	}
);

handle returns a function (MyHandler) that accepts an event, a properties object, and a context object. To use it, specify it as the value for an event property, such as onClick.

...
render: () => (
	<div>
		<Item onClick={myHandler} />
	</div>
)

The @enact/core/handle module exports some input methods designed to be used with handle, such as forward and preventDefault. Please see the module documentation for a complete list.

enact cli vs. enyo-dev

The Enact developer tools simplify, yet remain similar to, the Enyo developer tools.

Activity enact cli enyo-dev
Create a new project enact create enyo init
Create a development build npm run pack enyo pack
Create a production build npm run pack-p enyo pack -P
Serve a local version of your project for testing npm run serve enyo pack; enyo serve

Spotlight

Refactoring an Enyo application to Enact affords an opportunity to reevaluate the application’s Spotlight usage. Here are some things to consider:

  • Does this application use Spotlight containers?
  • Are there any custom Spotlight components?
  • Does the application listen for Spotlight events?
  • Can the overall structure of Spotlight components/navigation be simplified (for example, does the application handle

a majority of the Spotlight navigation events or are there multiple levels of nested containers)?

Spotlight Containers

If your application uses Spotlight containers, be sure to review the Spotlight container documentation.

Custom Spotlight Components

If you have created custom Spotlight components in your application, make sure to review the Spottable documentation.

Spotlight Events

Spotlight now uses native DOM events and does not dispatch synthetic events to the currently spotted control. Please review the Spotlight event documentation.

Data Management

If an application makes use of enyo.Model or enyo.Collection, it will need to be adapted to some changes in Enact. Notably, there are no framework-provided collections or models. Enact relies on its underlying Flux architecture to provide state and property updates through its component hierarchy. Therefore, it is necessary to manage application and/or component state to affect logic or UI changes.

A typical Enyo application pattern is to create a model and a collection to hold instances of the model. So that every view can have access to the collection, it can be made ‘public’. Finally, using bindings, a view can map the collection based on its requirements. All that remains is to add models to the collection, usually as the result of an asynchronous operation.

// MyModel.js
...
name: 'MyModel',
kind: enyo.Model,
...
// MyCollection.js
...
name: 'MyCollection',
kind: enyo.Collection,
model: MyModel,
...
// App.js
...
components: [
	{name: 'allData', kind: MyCollection, public: true}
],
view: MainView
...
// MainView.js
...
components: [
	{name: 'list', kind: enyo.DataList, ...}
],
bindings: [
	{from: 'app.allData', to: '$.list.collection'}
],
...

The above example is quite simplistic, so it can be re-implemented in Enact without using additional libraries. For complex data management and application state management, third-party solutions (such as Redux) exist.

// App.js
...
class App extends React.Component {
	...
	constructor (props) => {
		super(props);
		this.state = {
			recentData: props.data || []
		};
	}
	// response handler from some async request (not shown)
	dataFetched = ({data}) => {
		this.setState({recentData: (data && data.length) ? data : []});
	}
	render () {
		<div>
			<MainView data={this.state.recentData} />
		</div>
	}
}
// MainView.js
...
const MainView = kind({
	name: 'MainView',
	...
	render: ({data}) => (
		<VirtualList
			...
			data={data}
			...
		/>
	)
});
...

View Management

Enact provides @enact/moonstone/Panels, which is sufficient for most existing Enyo applications to use. However, it extends a more generic @enact/ui/ViewManager component that might offer the opportunity to improve an application’s overall UX.

webOS Platform Support

The @enact/webos module provides many useful utilities and methods to interact with the webOS platform.

  • @enact/webos/application - provides information about the application metadata
  • @enact/webos/deviceinfo - returns various details about the webOS device where the application is running
  • @enact/webos/keyboard - use to see if the keyboard is currently visible
  • @enact/webos/LS2Request - without this, your application cannot use the myriad webOS services that are available!

Almost every Enyo webOS application utilizes service calls. * Luna Service API example

  • @enact/webos/platform - returns various details about the webOS platform where the application is running (SmartTV, Open webOS, legacy devices (Palm, HP), etc.)
  • @enact/webos/pmloglib - system-level logging for your application
  • @enact/webos/VoiceReadout - reads alert text when accessibility VoiceReadout enabled