DailyJS

DailyJS

The JavaScript blog.


Tagenyo
Featured

tutorials frameworks mobile enyo enyo-kowalski

Enyo Tutorial: Part 2

Posted on .

In my introduction to Enyo, I promised that Enyo is "very modularized, reusable, and encapsulated". Today we'll create a reusable component from our monolithic and minimalistic application by refactoring the tip calculator. Afterwards we will style the application to make it ready for app stores and the web.

As mentioned in the previous part of the tutorial, the Enyo style guide suggests using double quotes instead of single quotes. Enyo also uses tabs for indentation. Although I prefer two spaces and single quotes, I will follow these rules during this tutorial.

This tutorial builds on the previous part, which is available here:

Loading Mechanism

Enyo is using files called package.js to load dependencies. If you look into the folder source/, which contains the core of the application, then you'll find a file named package.js from the bootplate project. Everything in this file will be loaded when the application starts up. Let's create a file called with the filename calc.percent.js, and add calc.percent.js to the end of the package.js:

enyo.depends(  
  "$lib/layout",
  "$lib/onyx",
  "App.css",
  "App.js",
  "calc.percent.js"
);

Components

Component objects are using events to communicate with their parent kinds. As described in the first part, components can nest other components. It would be nice to split the app into a reusable percent-calculator kind which could be used in other projects.

Published Properties

The calc.percent.js file should look like the following example -- I'll explain it in detail below.

enyo.kind({  
  name: 'PercentCalculator',
  kind: enyo.Component,
  published: {
    sum: 0, //optional default values
    percent: 0
  },
  events: {
    onCalculated: ''
  },
  create: function() {
    this.inherited(arguments);
  },
  calculate: function() {
    var result;

    result = (this.sum * this.percent) / 100;

    this.doCalculated({percentValue: result});
  }
});

Like the previous kind, this component has a name: PercentCalculator. This time the kind is not a control - we have chosen a component with kind: enyo.Component.

The next lines are the published properties of our kind. They can -- but must not -- have a default value, and it's 0 in this example. Enyo automatically creates setters and getters for our exposed properties. We will use the setters from that pair later but in this file we access them with this.sum and this.percent.

I mentioned previously that components are communicating with events. This example registers onCalculated, which is exposed to the public. It can be triggered with this.doCalculated({percentValue: result}); in the calculate method. The results are communicated to the parent kind.

Refactoring and Integration

In order to use our kind we have to add the component to our first kind from the file App.js.

{ kind: "PercentCalculator", name: "percentCalculator", onCalculated: "updateControls" }

Every time the event calculated is fired the method updateControls is called. This method is just getting the value and setting the new value of the corresponding DOM node. Here is the snippet:

updateControls: function(inSource, inEvent) {  
  this.$.tipAmount.setContent(inEvent.percentValue);

  return true; // stop bubbling
}

Notice the result is available as a property of the second argument: inEvent.percentValue.

The app, however, is not working yet. We have to give the values from the input fields to the component so it's able to calculate and pass back the result. I deleted the old calculate method and introduced the method calculateWithComponent. Also, please don't forget to update the ontap handler of the button. Here is the method:

calculateWithComponent: function(inSource, inEvent) {  
  var sum = this.$.sumControl.hasNode().value;
  var percent = this.$.percentControl.hasNode().value;

  this.$.percentCalculator.setSum(sum);
  this.$.percentCalculator.setPercent(percent);

  this.$.percentCalculator.calculate();
}

As before, the kind is accessed with this.$ and its name. The automatically generated setters are used for the published properties, and afterwards calculate can be called on our kind. At this point the component is passing the calculated result back. There are also change-Handler available for changing properties, but we do not use them here.

Here is the updated kind in full:

enyo.kind({  
  name: "App",
  kind: enyo.Control,
  style: "",
  classes: "onyx",
  components: [
    {kind: "onyx.InputDecorator", components: [
      {kind: "onyx.Input", name: "sumControl", placeholder: "Enter sum"}
    ]},
    {kind: "onyx.InputDecorator", components: [
      {kind: "onyx.Input", name: "percentControl", placeholder: "Enter percent"}
    ]},
    {kind: "onyx.Button", content: "Calculate tip", ontap: "calculateWithComponent"},
    {tag: "div", name: "tipAmount"},
    {kind: "PercentCalculator", name: "percentCalculator", onCalculated: "updateControls"}
  ],
  create: function() {
    this.inherited(arguments);
  },
  updateControls: function(inSource, inEvent) {
    this.$.tipAmount.setContent(inEvent.percentValue);

    return true; // stop bubbling
  },
  calculateWithComponent: function(inSource, inEvent) {
    var sum = this.$.sumControl.hasNode().value;
    var percent = this.$.percentControl.hasNode().value;

    this.$.percentCalculator.setSum(sum);
    this.$.percentCalculator.setPercent(percent);

    this.$.percentCalculator.calculate();
  }
});

The commit is 8f931.

Styles

I reduced the styles in the App.css to a simple background-color: #c6c6c6;, and one CSS class:

.center {
  text-align: center;
}

Then I changed the kind in our App.js from the basic enyo.Control to the kind enyo.FittableRows. A basic control was a nice choice to show you the basics of Enyo and kinds, but we want to use a more complex one which is provided by the framework.

In commit 8bb19 I've added an onyx.Toolbar as the first child of the components block:

{kind: "onyx.Toolbar", class: "center", content: 'Tip calculator'},

This will display a bar across the top of the screen (or page), in a similar fashion to the UINavigationBar used in iOS applications. The end result looks something like this:

Enyo Tip Calc

Production Build

You can run deploy.sh in the tools/ folder to start a deploy. It will minify and merge the source files of the project. The result will be saved to deploy/, and can be used with Cordova or simply uploaded to a web server.

Conclusion

You should now have learned the core concepts of Enyo and built a small application. Here is a short summary:

Part 1

  • Concept of kinds
  • Controls, and how to use and when
  • Events
  • Getters and setters
  • Constructors and destructors

Part 2

  • Components
  • Loading mechanism
  • Published properties
  • More on getters and setters
  • Production builds

References

Featured

tutorials frameworks mobile enyo enyo-kowalski

Enyo Tutorial: Part 1

Posted on .

Enyo is a mobile and desktop framework that uses JavaScript and HTML5. Developed by HP and the open source community, it's licensed under the Apache License. If you're looking to build mobile applications with JavaScript, then Enyo is a great choice. In this tutorial series, I'll introduce the major concepts through some worked examples that you can build or download and try out. The Enyo styleguide is suggesting double quotes instead of single quotes. Enyo also uses tabs for indentation. Although I prefer 2 Spaces as indentation and single quotes, I will follow these rules during this tutorial.

Kinds

Enyo applications are built around object prototypes called kinds. These can be either components or controls. Kinds in Enyo are very modularized, reusable and encapsulated.

Controls, on the other hand, are for controlling DOM nodes and manipulating them. Controls can nest other controls or components -- they're the "building blocks" for applications. A good example would be an app consuming an XML feed: a component would process the feed to JSON and rearrange the data. The views of that application representing the feed would be controls.

The Tip Calculator

There are hundreds of tip calculator apps on all the major vendor-specific app stores. We can go one better though -- let's build a tip calculator that can run on pretty much anything. Also, our Enyo-based app will be publishable using Cordova (formerly PhoneGap).

Setup

The Enyo project provides Bootplate -- Enyo's template project. All we have to do is clone the repository and init the submodules:

git clone https://github.com/enyojs/bootplate.git  
cd bootplate  
git submodule update --init  

First Steps

Next open debug.html. You should now see the example app. In our developer console, type the following:

new enyo.Control({ content: 'Hello World' }).write();  

This creates an Enyo control. It's rendered as a simple div:

<html>  
  <head>
  </head>
  <body>
    <div id="control">Hello World</div>
  </body>
</html>  

Going Deeper: Controls, Getter and Setter, Events

To start developing the tip calculator, delete the contents of source/App.js -- we'll replace it with a new file.

Let's add a kind called App to our emptied App.js:

enyo.kind({  
  name: "App",
  kind: enyo.Control,
  style: "",
  classes: "onyx",
  components: [
    {kind: "onyx.InputDecorator", components: [
      {kind: "onyx.Input", name: "sumControl", placeholder: "Enter sum"}
    ]},
    {kind: "onyx.InputDecorator", components: [
      {kind: "onyx.Input", name: "percentControl", placeholder: "Enter percent"}
    ]},
    {kind: "onyx.Button", content: "Calculate tip", ontap: "calculate"},
    {tag: "div", name: "tipAmount"}
  ],
  create: function() {
    this.inherited(arguments);
  },
  calculate: function(inSource, inEvent) {
    var sum = this.$.sumControl.hasNode().value;
    var percent = this.$.percentControl.hasNode().value;

    var result = (sum * percent) / 100;
    this.$.tipAmount.setContent(result);

    return true; // stop bubbling
  }
});

An instance is created and rendered into the DOM in our debug.html file with renderInto:

new App().renderInto(document.body);  

Now I'll explain the previous example in detail. The kind property is set to enyo.Control, and there are currently no inline styles -- the style property is empty. The onyx CSS class should be added, which is an included submodule in the Bootplate that themes our Enyo application and the applications elements.

The components property is used to nest more kinds -- I've used several to define the required input controls, a button, and a div to display the results. All of these components have a name property, which is important and will be explained later.

The button has an event attached, the tap-event. Enyo supports many other events, e.g. dragging over the screen, flick, or self-created events.

When a kind is created the method create is automatically called by the Enyo framework. The method inherits from its parent kind with this.inherited(arguments); and can be overwritten. There are several other constructor and constructor-like functions in the lifecycle of a kind: destroy, create or constructor.

Every time we tap our button the calculate method is called. this.$ is used to address and access controls within the kind.

var sum = this.$.sumControl.hasNode().value  

This line is where the value of the control with the name sumControl is accessed. After that we calculate the tip and render the result into the element.

this.$.tipAmount.setContent(result);  

Every control has some handy getters and setters:

.setContent();
.getContent();
.getClasses();
.setClasses();
.getStyle();
.setStyle();
.getAttribute();
.setAttribute();

At the end of the method return true is used to prevent the tap event from bubbling up.

Get the Source

The source is available at my GitHub account: robertkowalski/enyo-dailyjs.

Conclusion

We've now hacked together a very basic tip calculator. This example could be built on to add a canvas element for diagrams, localStorage for statistics, or simply to add validation to user input.

In the next part of this series we'll have a closer look at components and exposing the published properties of kinds. We'll also create more reusable components from our app.