Quantcast
Viewing latest article 1
Browse Latest Browse All 5

A Basic WebOS Application and the Depot

A lot of people are trying to understand how to write applications for WebOS so I figured the very first tutorial I would create for this site would be a simple walk-through of creating a very basic application. I am going to go a bit deeper than just a basic application like they cover in the Hello World example. The application created in this walk-through will explain the concepts behind creating a basic application, explaining what each part is and why you have to do it that way. I will also introduce the Depot in this walk-through. Are you ready?

The Tools I Use
Everyone will have their own set of tools that they use and they do not have to be the same as mine, however please note that I am not going to give the terminal commands for pushing the app to the emulator etc. My tools do that all for me and allow me to focus on developing my application.

The very first thing you should install is the Palm Developer SDK which you can get from the Palm Developer Website. For my Editor I use Komodo IDE with the very nice add-on that Templarian has created. If you do not have a license for Komodo IDE you can use the free version of Komodo Edit. You can download the add-on on Templarian’s website and installing is as easy as dragging and dropping on your editor window. The last thing I use is a file that you import into your editor that provides code completion for your editor. You can download this file here. Please note that you want to click the Download link and download the rawmojo.zip file. Installation instructions can be found on the same page. You can ignore the tool-kit as the tool-kit is the ancestor of the add-on.

Once you have these things installed you are all ready to start developing your first WebOS application.

Building the Application
Fire up your editor and make sure the WebOS add-on is seen on your screen, if it is not then click on the View menu and at the very bottom click on WebOS. This will open the WebOS panel that will give you access to everything you need while developing WebOS applications. Since this tutorial is about creating an application I am not going to cover every feature of the Komodo WebOS add-on. Below is a screen shot of the WebOS add-on window. I will explain what you need to do using this image as a basis so be sure to reference this image if necessary.

Image may be NSFW.
Clik here to view.
Main Window

The first thing you need to do is click the icon in the top left corner and choose New Project. Fill out the text boxes in the screen that follows. Make sure you fill everything in and select the path for the application by clicking on the icon to the right of the path field. Once you have created the project the add-on will create the directory structure and put the project in the Projects explorer which is located on the left side of the screen. If you do not see it try clicking on the View menu and select Tabs & Sidebars and make sure projects is checked.

Now that we have the project all setup it is time to actually start coding the application. I am not going to cover the directory structure in this tutorial, you can learn about all of that at the Palm Developer Website. The very first thing that we need to do is to create our first scene. We are going to name this scene ‘main’ so click on the same icon you did to create the project but this time click on New Scene. After you click on New Scene a text box will appear type the word ‘main’ without the quotes into that box and click the icon immediately to the right of the text box. This has now created the scene for you. It has put the scene html file in place as well as the scene assistant and has also modified your sources.json so that the application will be able to find this scene.

Now that we have the main scene we are going to alter the html that is in the file found in app/views/main/main-scene.html. You can go ahead and delete any text that is currently in that file because it is auto generated by the SDK. Below is the source code for the main-scene.html file.

1
2
3
4
5
<div id=“main” class=“palm-hasheader”>
    <div class=“palm-header”>WebOS Boston</div>
    <div id=“addBtn” name=“addBtn1″ x-mojo-element=“Button”></div>
    <div id=“getBtn” name=“getBtn1″ x-mojo-element=“Button”></div>
</div>

You will notice a few things in the HTML code above one the fact that I am using a palm-hasheader css class. This comes from the Mojo framework and is not defined in my sources. Next you will notice that I define 2 div elements that have a strange attribute called x-mojo-element. This tells the framework that you want to use a widget and to have it display in this spot. I have defined two buttons, one for showing a scene where you can add a value to the depot and the other shows the scene where you can get the value. Now that we have this in place it is time to actually write the code for the assistant. Below is the code I created for the assistant.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
function MainAssistant() {
 
}
 
MainAssistant.prototype.setup = function() {
  // Setup our Add Value Button and Event Handler
  this.addBtnAttributes = {};
 
  this.addBtnModel = {
    buttonLabel : ‘Add Value’,
    buttonClass :,
    disabled : false
  };
 
  this.controller.setupWidget(
    “addBtn”,
    this.addBtnAttributes,
    this.addBtnModel
  );
 
  Mojo.Event.listen(
    this.controller.get(‘addBtn’),
    Mojo.Event.tap,
    this.showAddValueScene.bind(this)
  );
 
  // Setup our Get Value Button and Event Handler
  this.getBtnAttributes = {};
 
  this.getBtnModel = {
    buttonLabel : ‘Get Value’,
    buttonClass :,
    disabled : false
  };
 
  this.controller.setupWidget(
    “getBtn”,
    this.addBtnAttributes,
    this.getBtnModel
  );
 
  Mojo.Event.listen(
    this.controller.get(‘getBtn’),
    Mojo.Event.tap,
    this.showGetValueScene.bind(this)
  );
}
 
MainAssistant.prototype.showAddValueScene = function()
{
  this.controller.stageController.pushScene(‘add’);
}
 
MainAssistant.prototype.showGetValueScene = function()
{
  this.controller.stageController.pushScene(get);
}
 
MainAssistant.prototype.activate = function(event) {
 
}
 
 
MainAssistant.prototype.deactivate = function(event) {
 
}
 
MainAssistant.prototype.cleanup = function(event) {
    this.controller.stopListening(this.controller.get(‘getBtn’), Mojo.Event.tap, this.showGetValueScene);
    this.controller.stopListening(this.controller.get(‘addBtn’), Mojo.Event.tap, this.showAddValueScene);
}

I am going to start by describing what these methods are and when they are called. The constructor function is the first thing called when this scene is run. We are not doing anything in the constructor function however we could have set some class properties etc if any parameters were passed into the object. The set-up method is called next in our case when this scene is created. The other methods activate and deactivate are called when your application is pushed to the background or pulled back to the foreground. The final method named cleanup is called when the application is closed and this is where we are going to remove our event handlers. If you fail to remove any event handlers that you setup with the listen method you will find that your application will probably leak memory. The setup method is where I am setting up our buttons and event handlers for the scene to render. You will notice that each button has an attributes object and a model object. You can learn what parameters can be specified by looking at the palm documentation for the Button Widget. You will notice that I am calling a method called setupWidget. This is the method that will actually get the framework to setup the widget on your scene. You *must* pass this function 3 parameters.

The first is the ID of the widget in the HTML scene. The second is the attributes object followed by the model object. Once you have this in place your button should show on your scene, however you will want to do something when a user taps your button. We need to set-up the event listener and you can see that on the line following the setupWidget function call. In order to set-up an event listener we need to pass the method 3 things. The actual widget element which is handled with the this.controller.get method call. We also need to pass it the type of event we wish to respond to. In this case we want to respond to Mojo.Event.tap events. The last item is a callback function, when the event takes place you tell it that you want this method executed. You can see in the assistant method for showAddValueScene and showGetValueScene I am just making a call to this.controller.stageController.pushScene(). This is basically just saying I want to get the applications stage controller from this assistants controller and I want to tell the stageController to push a different scene.

Now that we have that code in place we need to tell the stage-assistant that we want it to push our newly created scene when the application starts. To do this all we have to do is add one line of code to the stage-assistant. You can see the source code below.

1
2
3
4
5
6
7
function StageAssistant() {
 
}
 
StageAssistant.prototype.setup = function() {
  this.controller.pushScene(‘main’);
}

Since we have that in place we need to create our next 2 scenes. As you recall in the main-assistant.js file we created the two methods that we are using for the event handlers for the buttons we created. In those methods we got an instance of the stage-controller and told it to push a scene. Now is the time to create both of those scenes. In the WebOS add-on click the same icon you did earlier and create 2 more scenes. Name these scenes add and get. Once you have done that make sure your emulator is open and running.

Now that we have prepared the basics of our application we are going to run it in the emulator. Click the button in the WebOS addon that looks like a brown box with the green circular plus sign on it. This will package your application, install it to the emulator and launch it. When the application launches you should see the two buttons that you created. Go ahead a click on one of the buttons. Notice it changes the scene to show the newly created scenes. They do not show anything significant as we left the stock scene templates in place. That will change soon don’t worry.

With all of that out of the way I want to explain a bit about what the Depot is exactly and why you would want to use it. When dealing with storage on the WebOS Device you have three options to choose from. You can use a Cookie, Depot or an Sqlite database. One question many people have is when should I use one over the other? If you are just storing preferences for your application or some other small bits of data I would suggest you use a cookie. However if you find that you want to store quite a bit of data then you have another choice to make. Should you use a Depot or an Sqlite database? Well it all depends on how much data you are storing and it will depend on where you store your depot database. If you just use the default depot storage location you are limited to 1 MB for the database because this is stored in a spot on your device which is limited in space. It will also depend on whether or not you want to use a structured database, if so you should use the HTML5 Database (Sqlite). You can opt to specify that the database be stored as EXT which will store it on the media partition of the device and you are then only limited by the space which the user has on the device. In order to specify that a database be stored on the media partition you just have to prefix the name of the database with ext: so for instance you would do something like the following. Don’t worry if you do not understand this code you will see it again shortly I just wanted to show it so that you could see how to store a database on the media partition of the device. One important thing to mention is what had been tripping me up all week while messing with the depot. For some reason it only likes to store objects and not any other type of value. For instance you cannot set var value = true; and try to set that with the depot. You have to do { “value” : true } and store that object into the depot. I do not recall getting any errors from the depot it just would not retrieve my stored value.

1
2
3
4
5
var options = {
    name: “ext:webosboston_basic_tutorial”, //Name used for the HTML5 database name. (required)
    version: 1, //Version number used for the HTML5 database. (optional, defaults to 1)
    replace: false // open an existing depot
  };

So now that we know the different types of storage for developers on WebOS we will need to go over what the depot actually does. I have been fiddling with the Depot for about a week now and I have gone over the source code for the Depot which is included in the SDK. The Depot is basically an abstraction layer to the Sqlite database. It uses Sqlite in the background but gives you an API to use rather than having to know how to write proper SQL statements. You first need to open or create the database which is done by the SDK when you tell it to.

Now we are going to work on our Depot connection so go back into the main-assistant.js file and make it look like the code below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
function MainAssistant() {
  var options = {
    name: “webosboston_basic_tutorial”, //Name used for the HTML5 database name. (required)
    version: 1, //Version number used for the HTML5 database. (optional, defaults to 1)
    replace: false // open an existing depot
  };
 
  //Create a database when the scene is generated
  this.depot = new Mojo.Depot(options, this.dbConnectionSuccess, this.dbConnectionFailure);
}
 
MainAssistant.prototype.setup = function() {
  // Setup our Add Value Button and Event Handler
  this.addBtnAttributes = {};
 
  this.addBtnModel = {
    buttonLabel : ‘Add Value’,
    buttonClass :,
    disabled : false
  };
 
  this.controller.setupWidget(
    “addBtn”,
    this.addBtnAttributes,
    this.addBtnModel
  );
 
  Mojo.Event.listen(
    this.controller.get(‘addBtn’),
    Mojo.Event.tap,
    this.showAddValueScene.bind(this)
  );
 
  // Setup our Get Value Button and Event Handler
  this.getBtnAttributes = {};
 
  this.getBtnModel = {
    buttonLabel : ‘Get Value’,
    buttonClass :,
    disabled : false
  };
 
  this.controller.setupWidget(
    “getBtn”,
    this.addBtnAttributes,
    this.getBtnModel
  );
 
  Mojo.Event.listen(
    this.controller.get(‘getBtn’),
    Mojo.Event.tap,
    this.showGetValueScene.bind(this)
  );
}
 
MainAssistant.prototype.showAddValueScene = function()
{
  this.controller.stageController.pushScene(‘add’, this.depot);
}
 
MainAssistant.prototype.showGetValueScene = function()
{
  this.controller.stageController.pushScene(get, this.depot);
}
 
MainAssistant.prototype.activate = function(event) {
 
}
 
 
MainAssistant.prototype.deactivate = function(event) {
 
}
 
MainAssistant.prototype.cleanup = function(event) {
    this.controller.stopListening(this.controller.get(‘getBtn’), Mojo.Event.tap, this.showGetValueScene);
    this.controller.stopListening(this.controller.get(‘addBtn’), Mojo.Event.tap, this.showAddValueScene);
}

Improvements: In the constructor we create an options object to store some configuration values for the database. We are telling the framework to create or open the database for the name we specified and also telling it to use a few methods as a callback for the database connection. When you create/open a connection the framework will call back to a success or failure function that you specify so that you can handle any errors.

We’re passing this.dbConnectionSuccess and this.dbConnectionFailure however we never define them in the main-assistant.js class file. This is because they will not be called on this object. They will be called from the scene assistant that we are doing the database work from, we will create this code shortly. One last thing I want to make clear to all of you is in the add and get callback methods we created to push the scenes when a button is clicked we have added this.depot to the call. This is perfectly valid. In-fact you can put as many parameters as you want in the pushScene() method. After it knows the scene name any additional parameters will be passed to that scene assistant for processing internally.

We will first start with the add-scene and the scene assistant. We will need to have two text boxes and a button on this scene along with a hidden element which will display any errors or success messages when we store the value into the database. So let’s get started. Open up the add-scene.html and make it have the same code as I have in mine below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<div id=“main” class=“palm-hasheader”>
  <div class=“palm-header left” id=“list-header”>
    Add / replace data
  </div>
</div>
<div class=“palm-group”>       
  <div class=“palm-group-title”><span x-mojo-loc=“”>Database Info.</span></div>
    <div class=“palm-list”>
      <div class=‘palm-row first’>
        <div class=“palm-row-wrapper textfield-group” x-mojo-focus-highlight=“true”>
          <div class=“title”>                           
            <div class=“label”>Key</div>                       
            <div id=“keyField” name=“keyField” x-mojo-element=“TextField”></div>
          </div>
        </div>                      
      </div>
      <div class=‘palm-row last’>
        <div class=“palm-row-wrapper textfield-group” x-mojo-focus-highlight=“true”>
          <div class=“title”>                           
            <div class=“label”>Text</div>                       
            <div id=“textField” name=“textField” x-mojo-element=“TextField”></div>
          </div>
        </div>                        
      </div>                                                                                 
    </div>           
</div>
</div>
<div id=“depotResult” class=“info-text”></div>
 
<div class=“palm-button” id=“add_button”>Add entry</div>
<div class=“palm-button” id=“back_button”>Back to main view</div>

The first thing I want to point out is how we are using the palm-group css style. This is how you will group controls together so that they look uniform and not out of place. When we get to the point of running the application you will see what it makes it look like. For now just understand that we are grouping items together. You will also see a reference to x-mojo-focus-highlight=”true” in the code above. This tells the framework that we want this item to have focus when the scene is loaded. Since we are telling it to give focus to a text field the cursor will be in that field whenever we load that particular scene. Now that we have the HTML out of the way it’s time to actually work on the code that will make the application work. We are going to alter the add-assistant.js code to give this scene the functionality that we require.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
function AddAssistant(arg){
    this.depot = arg;
}
 
AddAssistant.prototype.setup = function() {
 
    //Create the attributes for the textfield
    this.keyFieldAtt = {
            hintText:     ‘Enter key here’,
            modelProperty:  ‘originalValue’,
                        textCase:       Mojo.Widget.steModeLowerCase,
            focus:      true,
            maxLength:   30
    };
    //Create the model for the text field
    this.keyModel = {
        originalValue :};
 
    this.textFieldAtt = {
        hintText:     ‘and some text here’,
        modelProperty:  ‘originalValue’,
                textCase:       Mojo.Widget.steModeLowerCase,
        multiline:    false,
        focus:    false,
        maxLength:   30
    };
    this.textModel = {
        originalValue :};
 
    //Setup the textfield widget and observer
 
    this.controller.setupWidget(‘keyField’, this.keyFieldAtt, this.keyModel);
    this.controller.setupWidget(‘textField’, this.textFieldAtt, this.textModel);
 
    Mojo.Event.listen(this.controller.get(‘add_button’),Mojo.Event.tap, this.add.bind(this));
    Mojo.Event.listen(this.controller.get(‘back_button’), Mojo.Event.tap, this.back.bind(this));
 
    this.depotResult = this.controller.get(“depotResult”);
}
 
AddAssistant.prototype.add = function(){
    Mojo.Log.info(%s : %s”, this.keyModel.originalValue, this.textModel.originalValue);
 
    this.data = { “val” :this.textModel.originalValue };
 
    this.depot.simpleAdd(this.keyModel.originalValue, this.data, this.dbSuccess, this.dbFailure);
}
 
AddAssistant.prototype.back = function(){
    Mojo.Controller.stageController.popScene();
}
 
AddAssistant.prototype.dbSuccess = function() {
    Mojo.Log.info(***** depot operation success!);
    this.depotResult.innerHTML = (“Add success!);
}
 
AddAssistant.prototype.dbFailure = function(transaction, result) {
    Mojo.Log.info(***** depot failure: %s”, result.message);
    this.depotResult.innerHTML = (“Add failure!);
}
 
AddAssistant.prototype.cleanup = function()
{
        /* Cleanup our listeners so there is no memory leak. */
        this.controller.stopListening(this.controller.get(‘add_button’), Mojo.Event.tap, this.add);
        this.controller.stopListening(this.controller.get(‘back_button’), Mojo.Event.tap, this.back);
}

In this assistant, Mojo.Log.info() is a logging class, it logs messages to the /var/log/messages file on the emulator or the device. This allows you to see the progress the app is making when running and any errors should you have any. If you are not familiar with how it works you should read up on the Mojo.Log.* methods. The next thing you will notice is that each of our text fields have properties set in the attributes and model objects. Most of these are self-explanatory but I will say that the modelProperty property in the attributes object tells the widget what field to look for in the model for the value of the field. You will also notice the addition of a cleanup method. This is the method that is called when the scene is destroyed and you should use it to clean up any event listeners that you have created. If you do not clean up after your event listeners they are just going to be hanging around eating up the memory on the device. You will also notice that in the attribues object we are calling a Mojo.Widget object to get the textCase. There are other ways that you can do this however this is the standard the other methods have been depreciated. All this method call does is set the case for the text in the text field. We are just turning off the auto capitalization so that when you type the first letter is not capitalized. We will be using this throughout the project. The one last thing that is different is how the depot is passed into this scene. In the constructor you can see that arg is passed in and then we are calling this.depot = arg; This allows us to have access to the depot for adding the values.

The next part is the get scene where we will be able to look-up values for the keys we have set using the add scene. On the get scene all we will need is to have one text field and a button. We will enter our depot key into the text field and when you tap the button it will fetch the value from the depot and update an element on the page. Below is the HTML code we are going to use for our view file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div id=“main” class=“palm-hasheader”>
  <div class=“palm-header left” id=“list-header”>
    Depot Sample
  </div>
</div>
<div class=“info-text”>Which data entry you want to get?</div>
 
<div class=“palm-group”>
<div class=“palm-group-title”>Enter key string</div>
  <div class=“palm-list”>
    <div class=“palm-row single”>
      <div class=“palm-row-wrapper textfield-group” x-mojo-focus-highlight=“true”>
        <div class=“title”>
          <div id=“entryNum” x-mojo-element=“TextField”></div>
        </div>
      </div>
    </div>
  </div>
</div>
<br />
<div id=“response”></div>
<br />
<div class=“palm-button” id=“get_button”>Get Entry</div>
<div class=“palm-button” id=“back_button”>Back to main view</div>

There is not anything new in the code above we setup a single text field and 2 buttons. One for calling the depot to get the value and the other for going back to the main scene. The next step is to actually create the assistant javascript code so that we can capture events when buttons are tapped.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function GetAssistant(arg){
    this.depot = arg;
}
 
GetAssistant.prototype.setup = function(){
    this.keyAtt = {
            hintText:       ‘Enter key here’,
            modelProperty:  ‘originalValue’,
                        textCase:       Mojo.Widget.steModeLowerCase,
            multiline:    false,
            focus:    true,
            maxLength: 30
        };
        this.keyModel = {
            originalValue :};
 
        //Setup the textfield widget and observer
 
        this.controller.setupWidget(‘entryNum’, this.keyAtt, this.keyModel);
        Mojo.Event.listen(this.controller.get(‘get_button’),Mojo.Event.tap, this.get.bindAsEventListener(this));
        Mojo.Event.listen(this.controller.get(‘back_button’),Mojo.Event.tap, this.back.bindAsEventListener(this));
}
 
GetAssistant.prototype.get = function(){
 
    this.depot.get(this.keyModel.originalValue, this.dbGetSuccess, this.dbFailure);
}
 
GetAssistant.prototype.back = function(){
    Mojo.Controller.stageController.popScene();
}
 
GetAssistant.prototype.dbGetSuccess = function(response){
 
    var recordSize = Object.values(response).size();
    if(recordSize == 0) {
        $(‘response’).update(“No such record in the database”);
    } else {
        $(‘response’).update(“Data entry is:+ response.val);
    }
}
 
GetAssistant.prototype.dbFailure = function(transaction, result) {
    Mojo.Log.info(***** depot failure: %s”, result.message);
}

There is one new part in the dbGetSuccess method. The new part is the section where you will see recordSize = Object.values(response).size(). This is actually a feature of the prototype framework that will allow you to take an object and treat it as a hash. All it really does it turn an object’s properties into an array. The size() method returns how many items are in the array so this is just checking to see if there is at-least 1 value returned from the depot. If not it shows a nice message about not being able to find the item in the depot. Once the item is found the element on the page is updated to show the value as the message. Another part that is new is the way that it updates the element on the scene which shows the results of the depot calls. You will notice the $(‘response’).update(“”); All this is doing is getting the response element from the scene and updating the value to be whatever the message is between the quotes.

At this point the project is completed, however we need to update the emulator with the latest version. Since we launched last time the old version is still on the emulator. You will need to click the icon in the WebOS panel that looks like a brown box with a – (minus sign) in a red circle. It’s the 3rd box icon from the left. Once you have done that click the box icon with the plus sign in the green circle to install the new version to the emulator. When the app launches you will be able to store and fetch values from the depot.

This has been a very basic introduction to creating an application with WebOS and using the Depot. The depot has much more functionality which you can see by checking out the API documentation. Remember you can *only* store objects in a depot if you try to store any other type of value you are going to run into problems with your application.

The source code for this project can be downloaded from here so that you can follow along. Feel free to write the code yourself if you want to learn the most from this tutorial.

If you would like to write tutorials for us please contact me and let me know. We would be more than happy to have more contributors on the team.

Disclaimer: Portions of the code above were taken from the Data Sample application that Palm provides with the SDK.


Viewing latest article 1
Browse Latest Browse All 5

Trending Articles