Sony Arouje

a programmer's log

Archive for the ‘JavaScript’ Category

RF Communication using nrf24L01 and Nodejs addon

leave a comment »

Recently I started experimenting with radio communication with low cost nrf24L01 modules. These modules are very cheap compare to XBee modules which I used earlier. With these nrf24 modules we could enable wireless communication between Arduinos and Raspberry pi very effectively and economically. For my experiment I used two nrf24 modules, one connected to an Arduino Uno and another to a Raspberry pi 1.  Here is the pin connection details

Seq NRF24L01 RPi Arduino Uno
1 GND 25 (GND) GND
2 VCC 17 (3.3v) 3.3v
3 CE 15 7
4 CSN 24 8
5 SCK 23 13
6 MOSI 19 11
7 MISO 21 12
8 IRQ

 

For testing the communication, I used the RF24Network library, which is very good and has good documentation. Also it comes with e.g for both Arduino and RPi. So I didn’t write any single code just used the e.g and able to see the communication working, initially I had some troubles and at the end every thing worked well, I can see the data coming from Arduino in RPi. 

My intention is to use these modules in RPi and write code in nodejs. Unfortunately there is no nodejs support for this library. So last night I decided to write a nodejs addon for this C/C++ library. I didn’t had any experience in writing a nodejs addon, I spend an hour understanding the Nan and creating very simple addons. Then I started writing the addon for RF24Network, this task was very hard than trying with simple hello world addons.

Node-gyp was keep on failing when it tries to compile the RFNetwork modules. In my searches I realized that node-gyp uses make utility and I need to add the C/C++ files of this library. At the end I could compile the node addon. See the binding.gyp file

{ "targets": [ { "target_name": "nrf24Node", "sources": [ "nrf24Node.cc", "RF24/RF24.cpp", "RF24/utility/RPi/spi.cpp", "RF24/utility/RPi/bcm2835.c", "RF24/utility/RPi/interrupt.cpp", "RF24Network/RF24Network.cpp", "RF24Network/Sync.cpp" ], "include_dirs": [ "<!(node -e \"require('nan')\")", "RF24Network", "RF24" ], "link_settings": { "libraries": [ "-RF24", "-RFNetwork" ] } } ] }

 

I should say, I am just a beginner in node-gyp and this binding.gyp might need some improvements. Anyway with this gyp file, the compilation succeeded.

Next is to create the addon file. Here I had to learn more about the data types of Nan and Callbacks. I started simple functions to begin with and compile again, then moved on to next. I took more time in understanding callbacks which allows the addon to call javascript callback functions. Also spend a lot of time in understanding threading and creating a module to continuous listening of incoming messages and trigger the callback function, so that nodejs can process those incoming messages. I use libuv for threading, it seems more easy to understand than Async worker modules in Nan.

That whole night I spend learning and writing and refactoring the addon, I finished the module by early morning. By that time I could write a nodejs app and could listen to incoming messages.

Here is the sample code in node js to listen and acknowledge the message back to the sender.

var rf24 = require('./build/Release/nrf24Node.node'); rf24.begin(90,00); rf24.printDetails(); rf24.write(1,"Ack"); rf24.readAsync(function(from, data){ console.log(from); console.log(data); rf24.write(from,"Ack"); }); process.on('SIGINT', exitHandler); function exitHandler() { process.exit(); rf24.close(); }

 

Here is the complete addon. The code is uploaded to github, with the steps to compile and use it your own nodejs applications.

#include <nan.h> #include <v8.h> #include <RF24.h> #include <RF24Network.h> #include <iostream> #include <ctime> #include <stdio.h> #include <time.h> #include <string> using namespace Nan; using namespace v8; RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); RF24Network network(radio); Nan::Callback *cbPeriodic; uv_async_t* async; struct payload_t { // Structure of our payload char msg[24]; }; struct payload_pi { uint16_t fromNode; char msg[24]; }; //-------------------------------------------------------------------------- //Below functions are just replica of RF24Network functions. //No need to use these functions in you app. NAN_METHOD(BeginRadio) { radio.begin(); } NAN_METHOD(BeginNetwork){ uint16_t channel = info[0]->Uint32Value(); uint16_t thisNode = info[0]->Uint32Value(); network.begin(channel,thisNode); } NAN_METHOD(Update) { network.update(); } NAN_METHOD(Available) { v8::Local<v8::Boolean> status = Nan::New(network.available()); info.GetReturnValue().Set(status); } NAN_METHOD(Read) { payload_t payload; RF24NetworkHeader header; network.read(header,&payload,sizeof(payload)); info.GetReturnValue().Set(Nan::New(payload.msg).ToLocalChecked()); } //-------------------------------------------------------------------------------- NAN_METHOD(Begin){ if (info.Length() < 2) return Nan::ThrowTypeError("Should pass Channel and Node id"); uint16_t channel = info[0]->Uint32Value(); uint16_t thisNode = info[1]->Uint32Value(); radio.begin(); delay(5); network.begin(channel, thisNode); } NAN_METHOD(Write){ if (info.Length() < 2) return Nan::ThrowTypeError("Should pass Receiver Node Id and Message"); uint16_t otherNode = info[0]->Uint32Value(); v8::String::Utf8Value message(info[1]->ToString()); std::string msg = std::string(*message); payload_t payload; strncpy(payload.msg, msg.c_str(),24); RF24NetworkHeader header(otherNode); bool ok = network.write(header,&payload, sizeof(payload)); info.GetReturnValue().Set(ok); } void keepListen(void *arg) { while(1) { network.update(); while (network.available()) { RF24NetworkHeader header; payload_t payload; network.read(header,&payload,sizeof(payload)); payload_pi localPayload; localPayload.fromNode = header.from_node; strncpy(localPayload.msg, payload.msg, 24); async->data = (void *) &localPayload; uv_async_send(async); } delay(2000); } } void doCallback(uv_async_t *handle){ payload_pi* p = (struct payload_pi*)handle->data; v8::Handle<v8::Value> argv[2] = { Nan::New(p->fromNode), Nan::New(p->msg).ToLocalChecked() }; cbPeriodic->Call(2, argv); } NAN_METHOD(ReadAsync){ if (info.Length() <= 0) return Nan::ThrowTypeError("Should pass a callback function"); if (info.Length() > 0 && !info[0]->IsFunction()) return Nan::ThrowTypeError("Provided callback must be a function"); cbPeriodic = new Nan::Callback(info[0].As<Function>()); async = (uv_async_t*)malloc(sizeof(uv_async_t)); uv_async_init(uv_default_loop(), async, doCallback); uv_thread_t id; uv_thread_create(&id, keepListen, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); } NAN_METHOD(PrintDetails) { radio.printDetails(); } NAN_METHOD(Close){ uv_close((uv_handle_t*) &async, NULL); } NAN_MODULE_INIT(Init){ Nan::Set(target, New<String>("beginRadio").ToLocalChecked(), GetFunction(New<FunctionTemplate>(BeginRadio)).ToLocalChecked()); Nan::Set(target, New<String>("beginNetwork").ToLocalChecked(), GetFunction(New<FunctionTemplate>(BeginNetwork)).ToLocalChecked()); Nan::Set(target, New<String>("update").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Update)).ToLocalChecked()); Nan::Set(target, New<String>("printDetails").ToLocalChecked(), GetFunction(New<FunctionTemplate>(PrintDetails)).ToLocalChecked()); Nan::Set(target, New<String>("available").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Available)).ToLocalChecked()); Nan::Set(target, New<String>("read").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Read)).ToLocalChecked()); Nan::Set(target, New<String>("readAsync").ToLocalChecked(), GetFunction(New<FunctionTemplate>(ReadAsync)).ToLocalChecked()); Nan::Set(target, New<String>("write").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Write)).ToLocalChecked()); Nan::Set(target, New<String>("close").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Close)).ToLocalChecked()); Nan::Set(target, New<String>("begin").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Begin)).ToLocalChecked()); } NODE_MODULE(nrf24Node, Init)

All the credit goes to the developers of RF24 and RF24Network library, I just created an addon for the great library. Along the way I learned a lot and could finish the nodejs addon.

 

Happy coding…

Written by Sony Arouje

February 5, 2017 at 4:57 pm

Communication between Raspberry Pi and Arduino using XBee

with 9 comments

Recently I was doing some experiments to establish a wireless communication between a Raspberry pi and Arduino. To establish wireless communication I used XBee Pro Series 2 from Digi International. The idea behind this test setup is to test, whether I can control devices like motor or read different sensors remotely.

One of the biggest advantage of using XBee is, you can find a lot of tutorials and libraries for any kind of system and programming languages. For this test app, I used Node js in RPi and C in Arduino.

Test Setup

XBee: I configured two xbee, one as Coordinator and another as Router. Both in API mode 2 (AP =2). I used XCTU to configure both the device. Only reason to choose API 2 is because the Arduino library I used only support API mode 2.

Raspberry pi: connected Coordinator XBee to one of my RPi. You can see more about the connection in one of my earlier post.

Arduino Uno: connected the Router xbee to one of my Arduino. The connection is pretty simple as below.

  • XBee Rx –> Arduino Tx
  • XBee Tx -> Arduino Rx
  • XBee 3.3v-> Arduino 3.3v
  • XBee Gnd –>Arduino Gnd

 

Raspberry Pi Node js code

Modules used

  • xbee-api: npm install xbee-api
  • serialport: npm install serialport

 

var util = require('util'); var SerialPort = require('serialport').SerialPort; var xbee_api = require('xbee-api'); var C = xbee_api.constants; var xbeeAPI = new xbee_api.XBeeAPI({ api_mode: 2 }); var serialport = new SerialPort("/dev/ttyAMA0", { baudrate: 9600, parser: xbeeAPI.rawParser() }); var frame_obj = { type: 0x10, id: 0x01, destination64: "0013A200407A25AB", broadcastRadius: 0x00, options: 0x00, data: "MTON" }; serialport.on("open", function () { serialport.write(xbeeAPI.buildFrame(frame_obj)); console.log('Sent to serial port.'); }); // All frames parsed by the XBee will be emitted here xbeeAPI.on("frame_object", function (frame) { console.log(">>", frame); if(frame.data!== undefined) console.log(frame.data.toString('utf8')); });

 

 

Arduino Sketch

This sketch uses a XBee library, to add the library, goto Sketch->Include Library->Manage Libraries. From the window search for XBee and install the library. I am using Arduino IDE 1.6.7.

I use SoftwareSerial to establish serial communication with XBee, Pin 2 is Arduino Rx and Pin 3 is Arduino Tx.

 

#include <Printers.h> #include <XBee.h> #include <SoftwareSerial.h> unsigned long previousMillis = 0; const long interval = 4000; // the interval in mS XBee xbee = XBee(); // XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX) // XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX) SoftwareSerial soft(2,3);// RX, TX Rx16Response rx16 = Rx16Response(); ZBRxResponse rx = ZBRxResponse(); XBeeAddress64 addr64 = XBeeAddress64(0x0013a200,0x407a25b5); char Buffer[128]; char RecBuffer[200]; void setup() { // put your setup code here, to run once: soft.begin(9600); Serial.begin(9600); xbee.setSerial(soft); } void print8Bits(byte c){ uint8_t nibble = (c >> 4); if (nibble <= 9) Serial.write(nibble + 0x30); else Serial.write(nibble + 0x37); nibble = (uint8_t) (c & 0x0F); if (nibble <= 9) Serial.write(nibble + 0x30); else Serial.write(nibble + 0x37); } void print32Bits(uint32_t dw){ print16Bits(dw >> 16); print16Bits(dw & 0xFFFF); } void print16Bits(uint16_t w){ print8Bits(w >> 8); print8Bits(w & 0x00FF); } void loop() { // put your main code here, to run repeatedly: xbee.readPacket(); if (xbee.getResponse().isAvailable()) { if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) { xbee.getResponse().getZBRxResponse(rx); if (rx.getOption() == ZB_PACKET_ACKNOWLEDGED) { // the sender got an ACK Serial.println("got ACK"); } else { // we got it (obviously) but sender didn't get an ACK Serial.println("not got ACK"); } Serial.print("Got an rx packet from: "); XBeeAddress64 senderLongAddress = rx.getRemoteAddress64(); print32Bits(senderLongAddress.getMsb()); Serial.print(" "); print32Bits(senderLongAddress.getLsb()); Serial.println(' '); // this is the actual data you sent Serial.println("Received Data: "); for (int i = 0; i < rx.getDataLength(); i++) { print8Bits(rx.getData()[i]); Serial.print(' '); } //Received data as string to serial Serial.println(' '); Serial.println("In string format"); for (int i = 0; i < rx.getDataLength(); i++) { if (iscntrl(rx.getData()[i])) RecBuffer[i] =' '; else RecBuffer[i]=rx.getData()[i]; } Serial.println(RecBuffer); String myString = String((char *)RecBuffer); if(myString=="MTON"){ Serial.println("Switching on Motor"); } else if(myString=="MTOFF"){ Serial.println("Switching off Motor"); } } //clear the char array, other wise data remains in the //buffer and creates unwanted results. memset(&RecBuffer[0], 0, sizeof(RecBuffer)); memset(&Buffer[0], 0, sizeof(Buffer)); } //Send a packet every 4 sec. unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; strcpy(Buffer,"RSLOW"); uint8_t payload[20]= "RSLOW"; // ZBTxRequest zbtx = ZBTxRequest(addr64,(uint8_t *)Buffer,sizeof(Buffer)); ZBTxRequest zbtx = ZBTxRequest(addr64,payload,sizeof(payload)); xbee.send(zbtx); } }

 

Burn the sketch to Arduino.

Testing

Run node js code in RPi and you start receiving the frames from Arduino.

 

 

Happy coding….

Written by Sony Arouje

January 21, 2016 at 12:06 am

VS2013 Multi-Device Hybrid Apps and Network Error in Windows8

with 2 comments

Last couple of days I was working on Node js REST service running in one of my Raspberry Pi. The idea is to do certain stuffs remotely via mobile devices. Later I will write a post explaining the project in detail. I completed Phase 1 of the application running in Raspberry pi. So next task was creating a mobile app, I decided to use Cordova with VS 2013 Multi Device Hybrid app template.

Yesterday night I started working on the Hybrid app in VS 2013 for Windows 8. First thing I wanted to test was the connectivity to the REST service running in Raspberry pi. I used Angular js $http to connect to the REST service. To my surprise what ever I do, the app is throwing error while doing http post with status 0. The same code works successfully if I run as a web app.

I spend a hell lot of time to figure out the issue but no success. Then I decided to use fiddler to test the traffic, when fiddler is running the app could able to communicate with the REST service. I got a hint, when we run Fiddler in Win8, it adds apps to AppContainer Loopback exemption. If I exempt my windows 8 app then http request starts failing. This stage I realized that, the error is because of some app configuration issue. Also in VS Java Script console I could see the error as shown below.

XMLHttpRequest: Network Error 0x2efd, Could not complete the operation due to error 00002efd.

All my google search return nothing. After a long hours of desperate research I came to know that to access private network I need to add Private Network capability to windows 8 app. But I didn’t know how to add it for Hybrid app as there is no Capabilities section in the config.xml. Some more search leads me to the Faq page of Multi device hybrid app template. As per the FAQ page we can override the default AppManifest.xml as shown below.

1. Create a Package.appxmanifest file under res/cert/windows8. Create a windows8 solution folder if not exist.

2. Open the project folder and go to bld/windows-AnyCpu/Debug and open AppxManifest.xml in notepad, copy the content and paste it in the above Package.appxmanifest file.

3. Double click on Package.appxmanifest file then goto Capabilities tab and choose ‘Private Networks (Client & Server)’.

4. Save it and run the app again, this time it worked I could connect to my Node js REST api.

 

Hope this will help some one to solve the XMLHttpRequest issue.

 

Happy Coding…

Written by Sony Arouje

October 29, 2014 at 3:50 pm

Bind JSON REST result using Knockout

leave a comment »

Last couple of hours I was trying to bind json data from a WCF service to a simple HTML UI using Knockout js. This is a very simple application I created to learn Knockout. This application is also a tracer to test  whether I can bind json result from the service directly to the UI, without creating an elaborate javascript view model at the client side. In real world services returns complex json, so it’s not possible to create the same model at the client side.

ko.mapping

Knockout provides mapping as a plugin, you can see more details here. Please  go through the link before you proceed.

So let’s see how I created my view model and the html UI.

I created a js file named customerviewmodel.js and added the  below code.

function customerViewModel() {
    var self = this;

    self.customerList = ko.observableArray();

    self.getCustomers = function () {
        $.ajax({
            type: 'GET',   
            url: 'http://10.0.0.2:8081/CustomerService/customers',
            contentType: "application/javascript",
            dataType: "jsonp",
            success: function(data) {
                var observableData = ko.mapping.fromJS(data);
                var array = observableData();
                self.customerList(array);
            },
            error:function(jq, st, error){
                alert(error);
            }
        });
    };
}

$(document).ready(function () {
    ko.applyBindings(new customerViewModel());
});

And the html UI. For this tracer app, I created a MVC4 web application and removed all the html code from the Views/Home/index.cshtml and replaced it with the below code.

<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/knockout-2.2.0.debug.js"></script>
<script src="~/Scripts/knockout.mapping-latest.debug.js"></script>
<script src="~/MyScripts/customerviewmodel.js"></script>

<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: customerList">
        <tr>
            <td data-bind="text: Id"></td>
            <td data-bind="text: Name"></td>
        </tr>
    </tbody>
</table>
<br/>

<input type="button" id="btnGetCustomers" value="Get Customers" data-bind="click: getCustomers">

 

customerList is a ko.observablearray and the data will get filled when the user clicks the button. I was able to call the WCF  REST api and get the json but unable to populate customerList or show the data to the  UI.  I fixed the issue with the  help of a post from Stackoverflow, see the code snippet below.

 

            success: function(data) {
                var observableData = ko.mapping.fromJS(data);
                var array = observableData();
                self.customersResult(array);}
WCF Configuration changes

As you can see, my WCF REST service is running in a different machine, and when I do an ajax call with datatype:json I am getting some errors. After some googling I came to know that I have to call the service with datatype:jsonp. So I changed it to jsonp and started getting the below error.

 

jQuery18206380491638767738_1396378264364 was not called

 

So I modified the  WCF service configuration as shown below to work with jsonp request.

 

    <bindings>
        <webHttpBinding>
          <binding name="webHttpBindingJson" crossDomainScriptAccessEnabled="true" />
        </webHttpBinding>    
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServicesBehavior">
           <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="webby">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
    </behaviors>

I configured the service as shown below. I use a custom hosting, so need to provide baseaddresses.

    <services>
      <service name="WCFRestTest.Services.CustomerService" behaviorConfiguration="ServicesBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://10.0.0.2:8082/CustomerService/Details"/>
          </baseAddresses>
        </host>
        <endpoint name="customerServiceRest" address="http://10.0.0.2:8081/CustomerService" 
               bindingConfiguration="webHttpBindingJson" binding="webHttpBinding" 
               contract="WCFRestTest.Services.ICustomerService" behaviorConfiguration="webby"/>
      </service>
    </services>

 

After this WCF configuration changes I could able to get json from the REST  api and bind the result to html UI using Knockout.

Let me know if you have any questions.

 

Happy coding…

Written by Sony Arouje

April 2, 2014 at 12:46 am

Posted in JavaScript

Tagged with , , ,

DDPClient.NET–.NET client for Meteor’s Distributed Data Protocol

with 3 comments

Last couple of hours I was working on a small .NET client library to connect to Meteor js application using Distributed Data Protocol (DDP). This post will give some insight to the library I am working on. You can see more details of DDP here.

Using DDPClient.NET you can subscribe to published items or call a Meteor method and display the same in your ASP.NET or Desktop applications.

First let’s go through the server code in my meteor application.

if(Meteor.is_server)
{
    Meteor.publish("allproducts", function(){
        return Products.find();
    });
    Meteor.startup(function(){
        console.log("starting Point of sale application.....");
      });

    Meteor.methods({
        addProduct: function (prodCode, prodDesc) {
            return "Product Name: " + prodDesc + " Product Code: " + prodCode;
        }
    });
}

 

From the Meteor application we are publishing an item called ‘allproducts’ and have a server method called ‘addProduct’ that takes two parameters. Now let’s see how to subscribe to published item and call the server method using DDPClient.NET.

Subscribe to Published items

 

class Program
{
    static void Main(string[] args)
    {
        IDataSubscriber subscriber = new Subscriber();
        DDPClient client = new DDPClient(subscriber);

        client.Connect("localhost:3000");
        client.Subscribe("allproducts");
    }

}
public class Subscriber:IDataSubscriber
{
    public void DataReceived(dynamic data)
    {
        try
        {
            if (data.type == "sub")
            {
                Console.WriteLine(data.prodCode + ": " + data.prodName + 
                                         ": collection: " + data.collection);
            }
        }
        catch(Exception ex)
        {
            throw;
        }
    }
}

 

As you can see it’s very easy to connect to Meteor application using DDPClient.NET. Just call the Connection function with the url. Then call the subscribe function to subscribe to any published item in Meteor application.

In the above code I subscribed to ‘allproducts’ item. After you subscribed successfully to the meteor application, we will receive all the products stored in the db. Once our client is subscribed any insert/delete/update will streamed to the .NET client. You can test it by adding a some products via the web application and you can see the newly added product get displayed in the console.

 

How to call a Meteor Method?

class Program
{
    static void Main(string[] args)
    {
        IDataSubscriber subscriber = new Subscriber();
        DDPClient client = new DDPClient(subscriber);

        client.Connect("localhost:3000");
        client.Call("addProduct", "NS5", "IRobot");
        Console.ReadLine();
    }
}
public class Subscriber:IDataSubscriber
{
    public void DataReceived(dynamic data)
    {
        try
        {
            if (data.type == "method")
                Console.WriteLine(data.result);
        }
        catch(Exception ex)
        {
            throw;
        }
    }
}

As you can see the Meteor method ‘addProduct’ take two arguments prodCode and prodDesc. So when we call the method we have to pass the parameter as well. We can do that by invoking the ‘Call’ function with the method name and arguments as shown below.

client.Call(“addProduct”, “NS5”, “IRobot”);

 

Also you will get notification if any data get deleted from the database. You can check the same as shown below.

else if (data.type == "unset")
{
    Console.WriteLine("deleleted item with id: " + data.id); 
}

so you will get the id of the data it deleted, you can search for the product with this id and remove or do what ever you wanted to do.

 

DDPClient.NET is checked into Github. The meteor code I referred here is added to the one I used in  Getting Started with meteor.

Written by Sony Arouje

September 22, 2012 at 6:17 pm

Getting started with Meteor

with one comment

This post is an introduction to Meteor and intended to developers who are new to Meteor.

So what is Meteor?

Meteor is an ultra-simple environment for building modern websites. What once took weeks, even with the best tools, now takes hours with Meteor.

Yes Meteor is a java script web framework that can do wonders with less line of code. Watch this Screen cast before you proceed. One of the feature that I really liked is the real time pushing of changed data set to all the connected clients in real time, without any complex code from developers.

Installing Meteor

I use Windows as my development environment, so we need to use the Meteor msi installer. We can get the latest installer from http://win.meteor.com/. Download the setup and install it. After installation we need to start CMD / Bash as administrator or reboot for it to catch the environment changes.

Developing Meteor App

Open the command prompt and change directory to your desired folder, say f:\sony\tracers\meteor. Then issue the create command as shown below.

>meteor create helloworld

image

Here ‘helloworld’ is my application name. You can give any name for your application. The command creates a folder with the application name with a .meteor folder and sample html and js file. To check whether the application created correctly, type ‘meteor’ in the command prompt as shown below.

image

As you can see our application is running in localhost, we can fire any browser to that url and can see your first meteor app running. I am leaving the screen shot, see it your self.

Explore Meteor in Detail

It’s time to see some of the cool features of Meteor. I created a small application called pointofsale. Rest of the post will be around this application. It’s a small application I started in Meteor with below functionality.

  • Product Entry
  • Order entry

I completed only Product Entry and Listing.

Disclaimer: I am very bad in Web UI development, so you will see a very ugly UI

Folder Structure

You can find more details about file structuring here. My app’s folder structure is shown below

fileStructure

I use meteor Knockout as my MVVM framework.

Templates

Templates are like User controls in ASP.NET. That has set of controls and logic. Meteor has a nice documentation about Templates, check it out before you move on.

Let’s see the Product Entry Template to get some insight.

<template name="productEntry">
    <table> 
        <tr>
            <td><Label >Product Code</Label></td>
            <td><label>Product Name</label></td>
        </tr>
        <tr>
            <td><input type="text" id="prodCode" data-bind="value: productCode" placeholder="Code"/></td>
            <td><input type="text" id="prodName" data-bind="value: productName" placeholder="Name"/></td>
            <td><input type="button" value="Add" id="addProduct"/></td>
        </tr>
    </table>
    <br>
</template>

 

As I said I use knockout js, so for data binding I added knock out attribute data-bind=”value: productCode” to input text.

Let’s see the java script of Product Entry.

if(Meteor.is_client)
{
  var productCode= ko.observable();
  var productName= ko.observable();

  var viewModel={
    productCode:productCode,
    productName:productName
  };

  Meteor.startup( function() {ko.applyBindings(viewModel); });

  Template.productEntry.events={
      "click #addProduct": function(){
            var id=Products.insert({prodCode:this.productCode(), prodName:this.productName()});
      }
  };

}

 

Meteor.is_client will return true if the code is running in client, based on this condition we can add client specific codes. As you can see, some of the code above are knockout specific and am not going in detail, except the event handling. You can learn knockout from http://learn.knockoutjs.com/.

Template.productEntry.events={
    "click #addProduct": function(){
          var id=Products.insert({prodCode:this.productCode(), prodName:this.productName()});
    }
};

 

The above code means, capture all the events in productEntry template. Right now productEntry template has only one button named addProduct and we need to handle the click event. That’s what we are doing in the above code. Leave the Products.Insert now, will come back to that later. So how do we handle a click event of another button, it’s very simple as shown below, just separate two functions with a comma.

Template.productEntry.events={     "click #addProduct": function(){         var id=Products.insert({prodCode:this.productCode(), prodName:this.productName()});     },     "click #showAlert": function(){         alert ("Hellow world");     }
};
 

Meteor Collection

In the above code we are referring an object called Products, what is it? It’s a Meteor collection, meteor uses collection to handle data storage and manipulations. Where is it declared? as this Products collection may require in Server side as well. So I created a file in the root called dbRef.js and declared the collection there as shown below.

Products =  new Meteor.Collection("product");
 

We can use this Products collection to add, remove or search products to display in the client. Have a look at the product listing template to see how to use Products collection to display as list.

<template name="productListing">
    <label>Product Listing</label>
        {{#each products}}
            {{>product}}
        {{/each}}
</template>

<template name="product">
    <div class="product {{selected}}">
        <span class="code">{{prodCode}}</span>
        <span class="name">{{prodName}}</span>
        <span><input type="button" value="Delete" id="delete"></input> </span>
    </div>
</template>

 

Below is the js file of the template.


if(Meteor.is_client)
{
    Template.productListing.products=function (){
        return Products.find();
    };

    Template.product.events ={
        'click': function(){
            Session.set("selected_product", this._id);
        },

        "click #delete": function(){
            Products.remove({_id:Session.get("selected_product")});
        }
    };

    Template.product.selected = function () {
        return Session.equals("selected_product", this._id) ? "selected" : '';
    };

}

 

The highlighted ‘products’ function uses the Products collection to return all the products entered. Rest of the code is for deleting and selecting the item in the list.

I think I covered the basics of Meteor, will cover more in coming days. You all can download the source code of pointOfSale from Google Drive

Written by Sony Arouje

September 14, 2012 at 6:37 pm

Posted in JavaScript

Tagged with ,

%d bloggers like this: