Saturday, August 5, 2017

Why Phoenix Framework is great for building realtime backends for mobile apps

Basic useful feature list:
  • Fast Response Time : Speed is usually top of the requirement for apps and when tallking about native apps you want people to do what they want to to on the go.
  • Fault Tolerant : If you have millions of users, you want all of them to have a great user experience.
  • Concurrent : From the technology stand point everyboy wants concurrency, and with Elixir/Phoenix you get to spawn thousands and millions of processes and the speed you get in doing things the same time than doing it one at a time gives your app an even insane speed.
  • Scalable : You want your backend not to ever die. You want your app to scale and your team to scale as well because of the community that is passionate about the Phoenix framework.
  • Maintainable : It’s easy to maintain, having hot-code reloading allows you to deploy without disturbing the users experience. The expressive nature of the code also makes it easy to maintain even as your code base grows.
So Phoenix contains features neccessary for handling the requirements for world-class mobile application.
Continue Reading...

How to create a user authentication systems with email verification in phoenix framework

Phoenix Framework is built with the powerful and resilient programming language called Elixir Lang. It is gaining momentum by the day thanks to it’s expressive syntax which makes you productive and it’s built upon the shoulders of the Erlang vm called BEAM which makes your code performant.
Here will build a user registration with Email verification and a login systems. Phoenix framework can be used to build normal html 5 crud apps, json api and real time backends. We’ll be building a real time backend using Phoenix channels and an Elixir library Guardian jwt.
You can find installation instructions on the Elixir site and Phoenix framework .
We be using Phoenix 1.3 which is the current version.
Lets create our app called sample with this command:
$ mix phx.new sample --no-html --no-brunch
* creating sample/config/config.exs
* creating sample/config/dev.exs
* creating sample/config/prod.exs
* creating sample/config/prod.secret.exs
* creating sample/config/test.exs
* creating sample/lib/sample/application.ex
* creating sample/lib/sample/web/channels/user_socket.ex
* creating sample/lib/sample/web/views/error_helpers.ex
* creating sample/lib/sample/web/views/error_view.ex
* creating sample/lib/sample/web/endpoint.ex
* creating sample/lib/sample/web/router.ex
* creating sample/lib/sample/web/web.ex
* creating sample/mix.exs
* creating sample/README.md
* creating sample/test/support/channel_case.ex
* creating sample/test/support/conn_case.ex
* creating sample/test/test_helper.exs
* creating sample/test/sample/web/views/error_view_test.exs
* creating sample/lib/sample/web/gettext.ex
* creating sample/priv/gettext/en/LC_MESSAGES/errors.po
* creating sample/priv/gettext/errors.pot
* creating sample/lib/sample/repo.ex
* creating sample/priv/repo/seeds.exs
* creating sample/test/support/data_case.ex
* creating sample/.gitignore
Fetch and install dependencies? [Yn] n
We are almost there! The following steps are missing:
    $ cd sample
    $ mix deps.get
Then configure your database in config/dev.exs and run:
    $ mix ecto.create
Start your Phoenix app with:
    $ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
    $ iex -S mix phx.server
cd into your sample app and go to the mix.ex file and add these libraries in your deps function : {:guardian, “~> 0.14”} , {:swoosh, “~> 0.8.1”}, {:secure_random, “~> 0.5.1”}. These will provide us with Json web token for user authentication , sending email for the user to verify their emailaddress before they can fully loging into our app, and generating a email verification token.
Lets now setup swoosh to send emails, we’ll be using Mailgun api to signup for a free sandbox accout to start sending email with their api.
# In your config/config.exs file
config :sample, Sample.Mailer,
  adapter: Swoosh.Adapters.Mailgun,
  api_key: "x.x.x"
  
# In your lib/sample folder create mailer.ex
defmodule Sample.Mailer do
  use Swoosh.Mailer, otp_app: :sample
end
defmodule StockitServer.UserEmail do
    import Swoosh.Email
    
    
 # In your lib/sample folder create user_email.ex   
defmodule StockitServer.UserEmail do
    import Swoosh.Email
    def welcome(user) do
        new()
        |> to({user.name, user.email})
        |> from({"your app name", "your-appxxx@gmail.com"})
        |> subject("Your sample verification email")
        |> html_body("<p>Thanks for signing up with us</p>
            <p>Please click the link below to verify your email address</p>
            <a href=https://sxxxx.com/v1/api/auth/verify_email/#                    {user.token}>Verify address</a>"
          )
    end
end
Lets config Guardian library:
# In your config/config.ex  
 
 config :guardian, Guardian,
  allowed_algos: ["HS512"], # optional
  verify_module: Guardian.JWT,  # optional
  issuer: "Sample",
  ttl: { 30, :days },
  allowed_drift: 2000,
  verify_issuer: true, # optional
  secret_key: System.get_env("GUARDIAN_SECRET") || "xx...xxxx",
  serializer: Sample.Web.GuardianSerializer
  
  
  # In your lib/sample/web folder create guardian_serializer.ex
  
  defmodule Sample.Web.GuardianSerializer do
  @behaviour Guardian.Serializer
  alias Sample.Repo
  alias Sample.Account.User
  def for_token(user = %User{}), do: { :ok, "User:#{user.id}" }
  def for_token(_), do: { :error, "Unknown resource type" }
  def from_token("User:" <> id), do: { :ok, Repo.get(User, id) }
  def from_token(_), do: { :error, "Unknown resource type" }
end
Phoenix framework directory structure now favours creating context to access your data model. So lets create Accounts context with apis to access our User model.
#In your lib/sample create account/account.ex
defmodule Sample.Account do
  @moduledoc """
  The boundary for the Account system.
  """
  import Ecto.Query, warn: false
  alias Sample.Repo
  alias Sample.Account.User
  @doc """
  Returns the list of users.
  ## Examples
      iex> list_users()
      [%User{}, ...]
  """
  def list_users do
    Repo.all(from u in "account_users",
      order_by: u.inserted_at,
      select: %{name: u.name, username: u.username, email_verified: u.email_verified, token: u.token, email: u.email, id: u.id})
  end
  @doc """
  Gets a single user.
  Raises `Ecto.NoResultsError` if the User does not exist.
  ## Examples
      iex> get_user!(123)
      %User{}
      iex> get_user!(456)
      ** (Ecto.NoResultsError)
  """
  def get_user!(id), do: Repo.one(from u in "account_users",
                                      where: u.id == ^String.to_integer(id),
                                      select: %{name: u.name, username: u.username, email_verified: u.email_verified, token: u.token, email: u.email, id: u.id})
  @doc """
  Creates a user.
  ## Examples
      iex> create_user(%{field: value})
      {:ok, %User{}}
      iex> create_user(%{field: bad_value})
      {:error, %Ecto.Changeset{}}
  """
  def create_user(attrs \\ %{}) do
    %User{}
    |> User.registration_changeset(attrs)
    |> Repo.insert()
  end
  @doc """
  Updates a user.
  ## Examples
      iex> update_user(user, %{field: new_value})
      {:ok, %User{}}
      iex> update_user(user, %{field: bad_value})
      {:error, %Ecto.Changeset{}}
  """
  def update_user(%User{} = user, attrs) do
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end
  @doc """
  Deletes a User.
  ## Examples
      iex> delete_user(user)
      {:ok, %User{}}
      iex> delete_user(user)
      {:error, %Ecto.Changeset{}}
  """
  def delete_user(%User{} = user) do
    Repo.delete(user)
  end
  @doc """
  Returns an `%Ecto.Changeset{}` for tracking user changes.
  ## Examples
      iex> change_user(user)
      %Ecto.Changeset{source: %User{}}
  """
  def change_user(%User{} = user) do
    User.changeset(user, %{})
  end
  def get_by(username) do
    Repo.get_by(User, username: username)
  end
  def get_by_token(token) do
    Repo.get_by(User, token: token)
  end
  def verify_email(user) do
    user
    |> User.verify_changeset()
    |> Repo.update()
  end
  def get!(id) do
    Repo.get!(User, id)
  end
end
#In your lib/sample create  account/user.ex for our user schema
defmodule Sample.Account.User do
    use Ecto.Schema
    import  Ecto.Changeset
    alias Sample.Account.User
    schema "account_users" do
        field :email, :string
        field :name, :string
        field :username, :string
        field :password, :string, virtual: true
        field :password_hash, :string
        field :email_verified, :boolean
        field :token, :string
        timestamps()
    end
    def changeset(%User{} = user, params) do
        user
        |> cast(params, [:name, :email, :username])
        |> validate_required([:name, :username, :email])
        |> validate_length(:username, min: 5, max: 20)
        |> validate_format(:email, ~r/@/)
        |> unique_constraint(:email)
        |> put_not_verified()
        |> put_token()
    end
    defp put_not_verified(changeset) do
        case changeset do
            %Ecto.Changeset{valid?: true} ->
                put_change(changeset, :email_verified, false)
            _ ->
                changeset
        end
    end
    defp put_token(changeset) do
        case changeset do
            %Ecto.Changeset{valid?: true} ->
                put_change(changeset, :token, SecureRandom.urlsafe_base64())
            _ ->
                changeset
        end
    end
    def registration_changeset(model, params \\ %{}) do
        model
        |> changeset(params)
        |> cast(params, [:password])
        |> validate_length(:password, min: 4, max: 200)
        |> put_pass_hash()
    end
    defp put_pass_hash(changeset) do
        case changeset do
            %Ecto.Changeset{valid?: true, changes: %{password: password}} ->
                put_change(changeset, :password_hash, Hasher.salted_password_hash(password))
            _ ->
                changeset
        end
    end
    def verify_changeset(%User{} = user, params \\ %{}) do
        user
        |> cast(params, [])
        |> put_verify_email()
    end
    defp put_verify_email(changeset) do
        case changeset do
            %Ecto.Changeset{valid?: true} ->
                put_change(changeset, :email_verified, true)
            _ ->
                changeset
        end
    end
end
# Let create our migration from your command line
$ mix ecto.gen.migration create_user
cd into your priv/repo/migrations folder. You’ll see the already created migration file.
defmodule Sample.Repo.Migrations.CreateAccountUser do
  use Ecto.Migration
  def change do
    create table(:account_users) do
        add :name, :string
        add :username, :string
        add :email, :string
        add :token, :string
        add :email_verified, :boolean
        add :password_hash, :string
        timestamps()
    end
    create unique_index(:account_users, [:email, :username])
  end
end
Now all that’s left is to migrate up our database
$ mix ecto.migrate
Now let’s create our authentication module. cd into lib/sample/web folder and create aan auth.ex file
defmodule Sample.Web.Auth do
    import Hasher
    alias Sample.Account
    def login_by_username_and_pass(username, given_pass) do
        user = Account.get_by(username)
        if user do
            cond do
            user.email_verified && check_password_hash(given_pass, user.password_hash) ->
                {:ok, user}
            user.email_verified && check_password_hash(given_pass, user.password_hash) == false ->
                {:err, :unauthorized}
            not user.email_verified ->
                {:err, :notverified}
            end
        else
            {:err, :notfound}
        end
    
    end
  
  end
Let’s now create our route to verify our email address
defmodule Sample.Web.Router do
use Sample.Web, :router
pipeline :api do
  plug :accepts, ["json", "json-api"]
  plug JaSerializer.Deserializer
end
pipeline :api_auth do  
  plug :accepts, ["json", "json-api"]
  plug Guardian.Plug.VerifyHeader, realm: "Bearer"
  plug Guardian.Plug.LoadResource
  plug JaSerializer.Deserializer
end 
  scope "/", Sample.Web do
  pipe_through :api # Use the default browser stack
  get "/", PageController, :index
end
scope "/v1/api/auth", Sample.Web do
  pipe_through :api
   get "/verify_email/:token", AccessTokenController, :verify_email 
end
end
Lets create our AccessTokenController.
#In your lib/sample/web/controllers/access_token_controller.ex
defmodule Sample.Web.AccessTokenController do
    use Sample.Web, :controller
    alias Sample.Account
    alias Sample.Account.User
     action_fallback Sample.Web.FallbackController
    def verify_email(conn, %{"token" => token}) do
        user = Account.get_by_token(token)
        if user do
           with {:ok, %User{} = user} <- Account.verify_email(user) do
               
                conn
                |> put_status(200)
                |> render("verify_email.json", user: user)
              
            end 
        else 
            conn
            |> put_status(:not_found)
            |> json(%{error: "invalid token"})
        end
    end
end
Lets create our access token view.
defmodule Sample.Web.AccessTokenView do
    use Sample.Web, :view
    def render("verify_email.json", %{user: user}) do
        %{
            verified: user.email_verified
        }   
    end
end
Lets create an access token channel to handle our user registration and login.
#In lib/sample/web/channels create access_token_channel.ex
defmodule Sample.Web.AccessTokenChannel do
  use Sample.Web, :channel
  alias Sample.Account
  alias Sample.UserEmail
  alias Sample.Mailer
  def join("access:lobby", _payload, socket) do
      {:ok, socket}
  end
  def handle_in("create:user", %{"user" => user_params}, socket) do
    case Account.create_user(user_params) do
      {:ok, user} ->
        UserEmail.welcome(user)|>Mailer.deliver
        resp = %{data: %{id: user.id,
                    name: user.name,
                    username: user.username,
                    email: user.email,
                    token: user.token,
                    verified: user.email_verified }
                   }
        push socket, "create:user", resp
        {:reply, :ok, socket}
      {:error, _changeset} ->
        {:reply, {:error, %{errors: "Could not create user"}}, socket}
    end
  end
  
def handle_in("create:login", %{"user" => %{"username" => username, "password" =>  password}}, socket)  do
      case Sample.Web.Auth.login_by_username_and_pass(username, password)  do
        {:ok, user} ->
          {:ok, jwt, _claims} = Guardian.encode_and_sign(user, :access)
          resp = %{data: %{access_token: jwt}}
          push socket, "create", resp
          {:noreply, socket}
        {:err, :notverified} ->
          {:reply, {:error, %{errors: "please verify your email address"}}, socket}
        {:err, :unauthorized} ->
          {:reply, {:error, %{errors: "user password incorrect"}}, socket}
        {:err, :notfound} ->
          {:reply, {:error, %{errors: "user not found"}}, socket}
      end
  end
end
#In your lib/sample/web/channels/user_socket.ex file add to the list of channels
channel "access:*", Sample.Web.AccessTokenChannel
This concludes our user signup and login system for a real time backend server.
Continue Reading...

Friday, August 4, 2017

Instacalc - A sales calculator app





Instacalc is an offline sales and inventory app for small retail shops. It handles the basis of up todate inventory of your shops and instant report of all sales for the day. All data are store in the phone and so no need for an external backend server.

Continue Reading...

Soccer Fans Chat app




FanzChat is the best way to see and chat with other soccer fans on your favorite clubs or championship cup events. Share your pics, videos, and messages real time the way you like it. Please note this app will be ready soon.
                                                  
Continue Reading...

Stock Taking app for small shops




This is stockIT a stock management app for small shops with a real time backend written in Elixir lang. The backend is hosted in heroku. Shops can have up to date inventory of products in their shops at anytime.
Continue Reading...

Thursday, August 3, 2017

Realtime native apps made easy with Fuse tools and Phoenix Channels

Elixir’s Phoenix framework is an ideal choice for building real-time applications and Fuse tools is even easier for building native mobile apps.


In this tutorial i'll show how to build an app using Elixir's Phoenix framework channels and Fuse tools.We won't worry about persistance or authentication.


Installation and Setup


This tutorial will assume the following language and framework versions:


  • Erlang 19.0
  • Elixir 1.4
  • Phoenix 1.2
  • Fuse 1.0

If you need to insall Erlang, Elixir and Fuse tools, i recommend checking out their websites.


To install Phoenix Framework and its dependencies, follow the Phoenix installation docs. Once you are able to install Phoenix withIn the same file, we’ll also need to uncomment line 5, which I’ll explain next:



$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

you should be ready to proceed.


Create New Phoenix App


I'will call it "MessageGram", and create it with



$ mix phoenix.new message_gram

When asked “Fetch and install dependencies?” enter “Y”. Finally, when the new app is generated, you should see the following message:


```
We are all set! Run your Phoenix application:


$ cd message_gram
$ mix phoenix.server

Before moving on, configure your database in config/de.exs and run:


$ mix ecto.create 

```
Next, we’ll need to modify the default MessageGram.UserSocket module. In the same file, we’ll also need to uncomment line 5, which I’ll explain next:


```


Channels


channel "room:*", MessageGram.RoomChannel
```


Of course, our MessageGram.RoomChannel module doesn’t exist yet. Let’s fix that


```
defmodule MessageGram.RoomChannel do
use MessageGram.Web, :channel
def join("room:lobby", payload, socket) do
{:ok, socket}
end


def join("room:" <> _private_room_id, _params, _socket) do
    {:error, %{reason: "Unauthorized"}}
end

def handle_in("new_msg", %{"message" => message, "at" => at}, socket) do
    broadcast! socket, "new_msg", %{message: message, at: at}
    {:noreply,  socket}
end

def handle_out("new_msg", payload, socket) do
    push socket, "new_msg", payload
    {:noreply, socket}
end

end


```


It's finally time to write our Fuse app.


First we'll need the Phoenix Channel javascript client to enable our Fuse app communicate with our Phoenix realtime backend. Since the client that comes with Phoenix by default is written in Es6, we'll need an Es5 version because Fuse tools only support Es5.
Go ahead and download a Phoenix Channel javascript client Es5 version


We are all set! Create our Fuse app:



$ fuse create app MessageGram


Let's add the phoenix-common.js file to the root of our app and modify our MessageGram.unproj file:


```
{
"RootNamespace":"",
"Packages": [
"Fuse",
"FuseJS"
],
"Includes": [
"*",
"phoenix-common.js:Bundle",
]
}


```
This will bundle the phoenix javascript client with our app to enable us use require("phoenix-common") in our ux file.


Lets build our message app in our MainView.ux file:


```


var Observable = require("FuseJS/Observable");
var messages= Observable();
var txt = Observable("");
var Phoenix = require("phoenix-common");
var channel = null;

var socket = new Phoenix.Socket("ws://localhost:4000/socket",{params: {userToken: "123"}})

fetch("http://localhost:4000", {
    method: "get"
}).then(function(resp) {

        socket.connect();

}).catch(function(err) {
    console.log("Error connecting to server")
})


channel = socket.channel("products:lobby", {});
channel.on("new_msg", function(payload) {
    console.log("Main mesage: "+payload.message)

    messages.add(payload)
})
channel.join().receive("ok", function(resp) {
    console.log("Joined successfully")
}).receive("error", function(resp) {
    return console.log("Unable to join", resp)
})

function sendClick() {
    channel.push("new_msg", {message: txt.value, at: new Date().toDateString()})
    txt.value = ""

} 

module.exports = {
    sendClick,
    txt,
    messages
};

</JavaScript>
<Panel Background="#183f">
<Grid Rows="1*,1*">
    <ScrollView>
        <StackPanel ItemSpacing="10">
            <Each Items="{messages}">
                <StackPanel Orientation="Horizontal" ItemSpacing="5">
                    <Text TextColor="White" Value="{message}"/>

                    <Text TextColor="White" Value="{at}"/>
               </StackPanel>
            </Each>
        </StackPanel>
    </ScrollView>
    <StackPanel HitTestMode="LocalBoundsAndChildren">
        <TextInput PlaceholderText="text" PlaceholderColor="#ffffff80" TextColor="White" Width="250" Value="{txt}" />
         <Rectangle Color="#127799" Height="40" Margin="20" Width="100" CornerRadius="80">
          <Text  Value="Send" Alignment="Center" TextColor="White" Clicked="{sendClick}"/>
        </Rectangle>
    </StackPanel>
</Grid>



```
Then run our app with:



$ fuse preview

And that’s it! We can test this by opening our app in multiple browser tabs to simulate multiple users talking to each other in real-time and with our fuse native app.

Continue Reading...

Wednesday, January 4, 2017

Building an ecommerce android app with fuse tools

Fuse Tools is a javascript framework for building world-class ios and android apps using UX markup and javascript. UX markup is for navigation, layout, animation while javascript is for business and application logic. To learn more visit fusetools.com
Getting Started
To get started you need to download Fuse Tools installer from fusetools.com and if you are using windows after Fuse Tools is installed; from the command line you run fuse install android to instal android sdk and its dependencies. Create a new project Once you have fuse tools installed, to create a new project you simply call
$ fuse create app ProjectName
Then run your app by calling:
fuse preview
So this point lets jump into the application and see what we are going to build out today — A Bookstore app. In the app you can browse the list of books in the store, select one or more books to add to the cart where you can then checkout to see your purchases. When you bootstrap a new fuse app, what you see is two files; MainView.ux and a unoproj file. The MainView.ux file is the entry to your application while the unoproj file list your app dependecies. When you open MianView.ux what you see


What you notice is that instead of Html what you will be using is UX markup. Compared to other javascript frameworks like ReactNative and Nativescript which where built specifically for javacript developers, fuse tools is built with collaboration of designers and developers in mind. Now it’s good practice to structure project folder into different folders depending on its functions. Components : will host our shared components Modules : our context and backend files definitions Pages : our app pages Assets : our images, fonts Lets start by adding some data to our app. We’ll be creating a mock backend for now.
So create a Backend.js file in Modules folder
var books = [
{
id: 1,
title: “Beginning Android Programming”,
author: “J.F DiMarzio”,
authorbio: “About DiMarzio”,
publicationdate: “2017 by John Wiley & Sons”,
introduction: “This book is written to help start beginning Android developers “,
picture: “Assets/books/android.png”,
cost: 25.00
},
{
id: 2,
title: “ES6 & Beyound”,
author: “Kyle Simpson”,
authorbio: “Kyle Simpson is a thorough pragmatist.”,
publicationdate: “2015–5–5”,
introduction: “This book is about shaking up your sense of understanding by exposing you “,
picture: “Assets/books/es6.png”,
cost: 35.99
},
{
id: 3,
title: “ng-book 2”,
author: “Ari Lerner”,
authorbio: “Full stack web developer and trainer.”,
publicationdate: “2016–5–10”,
introduction: “A complete refernce book on angular 2. “,
picture: “Assets/books/ngbook21.png”,
cost: 25.99
},
{
id: 4,
title: “Pro Git”,
author: “Scott Chacon and Ben Straub”,
authorbio: “Full stack web developer and trainer.”,
publicationdate: “2016–5–10”,
introduction: “Welcome to the second edition of Pro Git. “,
picture: “Assets/books/progit.png”,
cost: 45.99
},
{
id: 5,
title: “Reactjs Blueprints”,
author: “Sven A. Robbestad”,
authorbio: “Sven A. Robbestad is a developer with a keen interest in the Web .”,
publicationdate: “2016–7–10”,
introduction: “ReactJS was developed as a tool to solve a problem with the application state. “,
picture: “Assets/books/reactjsblue.png”,
cost: 20.99
},
{
id: 6,
title: “ReAwaken The Giant Within”,
author: “Tony Robins”,
authorbio: “Tony Robbins is one of the great influences of this generation.”,
publicationdate: “2013–5–10”,
introduction: “I’m sending you this gift of a condensed version of my 544-page original book in the hope”,
picture: “Assets/books/awaken.png”,
cost: 22.00
},
{
id: 7,
title: “SurviveJS”,
author: “Juho Vapsalainen”,
authorbio: “Full stack web developer and trainer.”,
publicationdate: “2016–5–10”,
introduction: “Front-end development moves forward fast. “,
picture: “Assets/books/survivejs.png”,
cost: 25.99
},
{
id: 8,
title: “Switching To Angular2”,
author: “Minko Gechev”,
authorbio: “Minko Gechev is a software engineer who strongly believes in open source software. “,
publicationdate: “March 2016”,
introduction: “It is the modern framework you need to build performant and robust web applications.”,


picture: “Assets/books/switchingto.png”,
cost: 21.00
},
{
id: 9,
title: “Unlimited Sales Success”,
author: “Brian Tracy”,
authorbio: “A world class motivational and sales consultant.”,
publicationdate: “2013–2–10”,
introduction: “A complete refernce book on todays selling. “,
picture: “Assets/books/selling.png”,
cost: 25.99
},
{
id: 10,
title: “Web Development with Node and ExpressJS”,
author: “Ethan Brown”,
authorbio: “A senior software engineer at PoP Art.”,
publicationdate: “2014–6–27”,
introduction: “Learn to build modern web applications with node and expressjs “,
picture: “Assets/books/node.png”,
cost: 19.99
}
];
Lets create a promise to return the books
function getBooks() {
return new Promise(function(resolve, reject) {
resolve(books)
})
}
//Now lets export these functions
module.exports = {
getBooks: getBooks
}
Now lets create a context, this will allow our pages view model to interface with it rather than directly with our backend thereby allowing object caching which will reduce unnecceary call to our backend server and so reducing bandwidth cost and saving battery.
Lets create a Context.js file in the Modules folder
first we create an observable to store app state
var Backend = require(“./Backend”)
var Observable = require(“FuseJS/Observable”)
var store = Observable() // this will hold our data coming from the backend
var books = Observable() // this will hold our initial books from the store
var cart = Observable() // this will hold the contents of our cart
var total = Observable(0) // total price of cart items
var isCartEmpty = Observale(true) // whether the cart is full
var totalAmount = Observable() // total amount expression
Lets get the books from our backend first
function getBooks() {
Backend.getBooks()
.then(function(books) {
store.replaceAll(books)
//Now pass the books to our books observable
store.forEach(function(book) {
books.add(book)
})
})
.catch(function(error) {
console.log(“can not load books”)
})
}
Now call this function to load the books when the app starts getBooks() Now lets create a function to add books to cart pasing the id, cost, title, picture, author and a harcoded qty of 1
function addToCart(id, cost, title, picture, author) {
cart.add({id: id, cost: cost, title: title, picture: picture, author: author, qty: 1})
// Now calculate the total amount of cart items
cart.forEach(function(book) {
total.value = (total.value + (book.price * book.qty))
})
isCartEmpty.value = cart.length ? false : true
totalAmount.value = “You Paid : $ “ + total.value.toFixed(2)
}
Now lets add a function to remove an item from the cart
function removeFromCart(item) {
cart.remove(item)
total.value = 0
isCartEmpty.value = cart.length ? false : true
totalAmount.value = “You Paid : $ “ + total.value.toFixed(2)
}
Now lets export these functions and observables
module.exports = {
totalAmount,
cart,
isCartEmpty,
addToCart,
removeFromCart
}
We now have some data we can add to our app components view model Since this app i a multi-page app, we will create a navigation component to handle routing to each of the page.
In our MainView.ux file








Lets create our home page Home.ux in Pages folder and also Home.js for the buisness logic for this page First our business and data logic for the home page
Now we create the Home.js file in the Pages folder
var Context = require(“Modules/Context”);
function getBookDetail(args) {
var book = args.data
router.push(“detail”, book)
}
function goToCart() {
router.goto(“cart”)
}
module.exports = {
books: Context.books,
getBookDetail: getBookDetail,
goToCart: goToCart
}
Home.ux
































Lets create our detail page Detail.ux which will contain more details about a particular book we want to buy. First we create the buisness and data logic for the detail page.
Detail.js
var Context = require(“Modules/Context”);
var book = this.Parameter; // the parameter data we passed from the home page which is an observable
var title = book.map(function(x) {
return x.title
});
var id = book.map(function(x) {
return x.id
});
var publicationdate = book.map(function(x) {
return x.publicationdate
});
var author = book.map(function(x) {
return x.author
});
var authorbio = book.map(function(x) {
return x.authorbio
});
var picture = book.map(function(x) {
return x.picture
});
var cost = book.map(function(x) {
return x.cost
});
var introduction = book.map(function(x) {
return x.introduction
});
var price = book.map(function(x) {
return “Price : $” +x.cost
});
function addToCart() {
Context.addToCart(id.value, cost.value, title.value, picture.value, author.value);
router.goBack()
}
module.exports = {
title,
id,
introduction,
picture,
cost,
authorbio,
publicationdate,
author,
addToCart,
price
}
Detail.ux


































Now we create our cart page along with the cart business and data logic. First the Cart.js file in the Pages folder for the cart page business logic
var Context = require(“Modules/Context”);
function remove(args) {
var item = args.data
Context.removeFromCart(item)
}
function goHome() {
router.goto(“home”)
}
var todaysDate = “Purchases made on “ + new Date().toDateString() + “ at “ + new Date().toLocaleTimeString();
var qty = Context.cart.count() // this will return the number of items in cart as an observable
module.exports = {
goHome: goHome,
noItems: Context.isCartEmpty,
cart: Context.cart,
remove: remove,
todaysDate: todaysDate,
totalAmount: Context.totalAmount,
qty: qty
}
Cart.ux































































To build our app on our android device for testing; make sure you have android adb usb driver installed. Then connect your device to the pc and use the command:
Fuse build bookstore-tutorial.unoporj -tandroid -DGRADLE -r
Please checkout the tutorial app on github.com/egaleme/bookstore-tutorial
Talk to Me And that’s it, a scalable and performant e-commerce app that you can easily add to and enhance for your needs. If you have any questions or comments then please let me know.


Continue Reading...
Powered By Aleme Gabriel