NewFiveFour | Blog | Portfolio


Javascript: Create HTML with Javascript

If you create HTML with javascript you can

  • Insert variables that are available in Javascript
  • Add event listeners which close over javascript variables (i.e. you can use closures as event handlers)

The first user case is handled by the new template literals. The second usecase is not. Although there are tricks around that but this post is not about that.

We want to be able to do

node("div")
node("div", "background-color: red")
node("div", "background-color: red", ["id", "an_id", "class", "something", "attribute", "value"])

With everything but the tag name optional. For example node("div", ["id", "hiya"]) is valid as is node("span").

We also want to add child nodes as a single node or an array of nodes or a mixture. These come after the tag name, optional style and optional attributes.

node("div",
  node("div")
)
node("div",
  node("div"),
  node("div")
)
node("div",
  [node("div"), node("div")]
)

node("div",
  node("div"),
  [node("div"), node("div")],
  node("div")
)

In addition we want to be able to optionally add event listeners. These come after the tag name, optional style and optional attributes.

node("div", 
  ["click", (event) => console.log("click"),
   "focus", (event) => console.log("focus")]
)

And it’s annoying to type node("div") and not div() so let’s fix that now.

function div() { 
  return node.apply(this, ["div"].concat([...arguments]))
}
function span() { 
  return node.apply(this, ["span"].concat([...arguments]))
}
etc...

Plus we need this for text

function text(str) {
  return document.createTextNode(str)
}

Finally here’s the function that does all that:

function node() {
  var iterator = 0;
  var h = document.createElement(arguments[iterator++])
  if(arguments[iterator] && typeof(arguments[iterator]) === "string") h.style = arguments[iterator++];
  if(arguments[iterator] && arguments[iterator] instanceof Array
    && typeof(arguments[iterator][1]) === "string") {
    for(var i=0; i<arguments[iterator].length;) {
      h.setAttribute(arguments[iterator][i++], arguments[iterator][i++])
    }
    iterator++
  }
  if(arguments[iterator])
  for(var i = iterator; i < arguments.length; i++) {
    if(arguments[i] instanceof Array) {
      if(arguments[i][1] instanceof Function) {
        for(var j = 0; j < arguments[i].length; ) {
          h.addEventListener(arguments[i][j++], arguments[i][j++])
        }
      } else {
        arguments[i].forEach(node => h.appendChild(node))
      }
    } else {
      h.appendChild(arguments[i])
    }
  }
  return h
}
div(
  h4("color: blue", 
    text("click me"),
    ["click", () => alert("end of blog post")]
  )
)

(the caveat of using a text node is that you /need/ to use a style string else it will confuse the function)

javascript html

Javascript: Multiple pages and browser history navigation using plain js

You often want different pages in your web app with browser navigation.

Let’s define the HTML. Each page will be a div with the page class and an id attribute that ends with _page. We’ll describe the function event listeners later.

<div id="main_page" class="page">
    <p>
        Home page content
    </p>
    <div onclick="gotoPage('pagetwo')">
        Next page
    </div>
</div>
<div id="pagetwo_page" class="page">
    <div onclick="gotoPage(-1)">
        Previous page
    </div>
    <p>
        Page two content
    </p>
</div>

The gotoPage function will:

  1. Go to a new page using the location hash, i.e. gotoPage("newpage") will go to “localhost/#newpage”
  2. Go to previous page when -1 is passed
  3. If this is the first load of the page, gotoPage(-1) will go to main_page instead.
  4. If no argument is passed, we refresh the current page
  5. Allow us to go to previous page with the browser back button i.e. if we’re on “localhost/#newpage” then the back button will take us to “localhost/”
function gotoPage(hash) { 
    var go = (hash) => {
        // Check if this conflicts with something in your app
        history.state = "from_webapp"
        location.hash = hash
    }
    // if come from another page in our webapp, let's go back to previous page
    if(hash == -1 && history.state == "from_webapp") history.back()
    // if we haven't used gotoPage() before (i.e. this is a direct link therefore no "from_webapp")
    // then let's go to default page on go(-1) 
    else if(hash == -1 ) go("") 
    else if(hash) go(hash)
    else window.newfivefour.onpopstate()
}

We’ll see what window.newfivefour.onpopstate() refers to in a moment.

We’ll now setup our logic, setupPages(). The first thing we’ll do it hide all divs with the .page class. Then on each time go() changes the location hash we’ll call a function.

Each run of this function will then hide all the element again and show a div based on the page name. Going to go("messages") will unhide <div id="messages_page">. If the location hash is blank or “#” we’ll show the <div id="main_page>.

setupPage will take an object whose properties relate to the main of a page id, i.e. <div id="pagetwo"> will relate to { "#pagetwo" : ... }, and the values will be functions.

So each time the function inside setupPages run it will check this object against the page we’re visiting and then run the functions therein, so you can define javascript functions to run when a page is loaded.

Here’s the whole thing

<html>
  <body>
    <div id="main_page" class="page">
      <p>
        Home page content
      </p>
      <div onclick="gotoPage('pagetwo')">
        Next page
      </div>
    </div>
    <div id="pagetwo_page" class="page">
      <div onclick="gotoPage(-1)">
        Previous page
      </div>
      <p>
        Page two content
      </p>
    </div>
  </body>
  <script>
    setupPages({
      "default": () => console.log("main page"),
      "#pagetwo": () => console.log("page two")
    })


    function gotoPage(hash) { 
      var go = (hash) => {
        // Check if this conflicts with something in your app
        history.state = "from_webapp"
        location.hash = hash
      }
      // if we've not directly linked to the page, let's go back to previous page
      if(hash == -1 && history.state == "from_webapp") history.back()
      // if we haven't used gotoPage() before (i.e. this is a direct link therefore no "from_webapp")
      // then let's go to default page on go(-1) 
      else if(hash == -1 ) go("") 
      else if(hash) go(hash)
      else window.newfivefour.onpopstate()
    }
    function setupPages(onLoadPageHash) {
      if(!window.newfivefour) window.newfivefour = {}
      var hideAllPages = () => {
        var pages = document.querySelectorAll(".page");
        for(var i = 0; i < pages.length; i++) pages[i].style.display="none";
      }
      hideAllPages()
      window.newfivefour.onpopstate = () => {
        hideAllPages()
        var hash = location.hash;
        if(hash == "" || hash == "#") {
          document.querySelector("#main_page").style.display = "block"
          if(onLoadPageHash && onLoadPageHash["default"]) onLoadPageHash["default"]()
        } else {
          if(onLoadPageHash && onLoadPageHash[hash]) onLoadPageHash[hash]()
          var page = document.querySelector(hash + "_page")
          if(page) {
            page.style.display = "block"
          }
        }
      }
      window.addEventListener("popstate", window.newfivefour.onpopstate)
      gotoPage() // Go to the default page
    }
  </script>
</html>
javascript html

Android: Continuous Deployment with HockeyApp

Continuous Deployment is automatically distributing new app to your testers, and automatically reporting crashes. This guide shows you how to use HockeyApp for the distribution aspect.

I’m going to assume you’ve got a basic Android App in development.

  1. First, sign up to https://HockeyApp.net
  2. Click add new app, and choose to add it manually
  3. Give it the same package name as appears in your AndroidManifest.xml
  4. Now it’s created, click on integrate HockeyApp quick tutorial. It will tell you add these parts to your app:

app/build.gradle:

    repositories {
      ...
      jcenter()
      ...
    }

    android {
      ...
      defaultConfig {
        ...
        manifestPlaceholders = [HOCKEYAPP_APP_ID: "IT_WILL_TELL_YOU_YOUR_APP_ID"]
        ...
      }
      ...
    dependencies {
      ...
      compile 'net.hockeyapp.android:HockeySDK:4.0.1'
      ....
    }      

app/src/main/AndroidManifest.xml (within the application tag):

    <meta-data android:name="net.hockeyapp.android.appIdentifier" android:value="${HOCKEYAPP_APP_ID}" />

In your Activity:

    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      // Your own code to create the view
      // ...

      checkForUpdates();
    }

    @Override
    public void onResume() {
      super.onResume();
      // ... your own onResume implementation
      checkForCrashes();
    }

    @Override
    public void onPause() {
      super.onPause();
      unregisterManagers();
    }

    @Override
    public void onDestroy() {
      super.onDestroy();
      unregisterManagers();
    }

    private void checkForCrashes() {
      CrashManager.register(this);
    }

    private void checkForUpdates() {
      // Remove this for store builds!
      UpdateManager.register(this);
    }

    private void unregisterManagers() {
      UpdateManager.unregister();
    }
  1. Now build and install that on your phone, and upload the APK to HockeyApp via the upload version button, clicking through all the dialog boxes until you can see the version on the Overview.
  2. Now change something in the app, like some text, and update the versionCode in app/build.grade.
    Build it, but do not install this to your device (so we can see automatic updates on our phone - you don’t normally do this)
  3. With this newly build version, upload it to HockeyApp as before.

Now when you open the app again, or do something to trigger onResume(), it will ask you if you want to update to the latest version.

Click update, and voila - you and your testers will see the newest app, and any crashes will be reported to you with a stacktrace and device information.

We don’t yet automatically upload our APK to HockeyApp via a build server / continuous integration environment, or send up the release notes, but we can do that in a later tutorial.

android hockeyapp

Android and Facebook's Litho: Getting started

Litho allows you to declare your view in code with speed optimisations for lists. It helps with reactive flows.

It uses flexbox layout logic, via the Facebook Yoga library, allowing you to use your existing web knowledge. This applies to iOS too since Yoga also exists for iOS.

It supports one-directional data binding, thereby allowing you dive into the flux architecture a little.

Let’s do the basic getting started first. Let’s bung all the depenedencies into your app’s build.gradle.

compile 'com.facebook.litho:litho-core:0.2.0'
compile 'com.facebook.litho:litho-widget:0.2.0'
provided 'com.facebook.litho:litho-annotations:0.2.0'
annotationProcessor 'com.facebook.litho:litho-processor:0.2.0'
compile 'com.facebook.soloader:soloader:0.2.0'
debugCompile 'com.facebook.litho:litho-stetho:0.2.0'
compile 'com.facebook.litho:litho-fresco:0.2.0'

Create an Application class in your app and ensure your manifest points to it. In the onCreate method add this:

SoLoader.init(this, false);

In your Activity’s onCreate change the view layout code to:

 ComponentContext c = new ComponentContext(this);
 setContentView(LithoView.create(c, MyComponent.create(c).build()));

We’re creating a Litho context, and then creating a Litho view with that context, and a component too. Where does that MyComponent come from?

@LayoutSpec
public class MyComponentSpec {
    @OnCreateLayout
    static ComponentLayout onCreateLayout(ComponentContext c) {
        return Column.create(c)
                .paddingDip(YogaEdge.ALL, 16)
                .child(Text.create(c)
                        .text("sup")
                        .textSizeSp(40))
                .child(Text.create(c)
                        .text("sup2")
                        .textSizeSp(20))
                .build();

    }
}

The annoation @LayoutSpec takes the class name minus Spec, thereby creating the MyComponent class via facebook’s annoation processor.

We create a Column using flexbox terminology, with two Text children, which are not TextViews incidentally.

Later tutorials will focus on using Android Views within Litho and events I should think.

android litho

Android: Cache network requests for offline access with Retrofit2 and OkHTTP3

Let’s first build a OKHTTP client with

  1. a cache
  2. an interceptor that checks for connectivity and, if none, asks for cached data:

Here’s the client.

OkHttpClient client = new OkHttpClient
  .Builder()
  .cache(new Cache(App.sApp.getCacheDir(), 10 * 1024 * 1024)) // 10 MB
  .addInterceptor(new Interceptor() {
    @Override public Response intercept(Chain chain) throws IOException {
      Request request = chain.request();
      if (App.isNetworkAvailable()) {
        request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
      } else {
        request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
      }
      return chain.proceed(request);
    }
  })
  .build();

We first create the cache object with 10 MB, getting the cache directory from a static Application context.

Then the Interceptor uses a utility method in my Application class to check for connectivity. If there is connectivity, we tell the request it can reuse the data for sixty seconds.

If there’s no connectivity, we ask to be given only (only-if-cached) ‘stale’ data upto 7 days ago.

Now make this OkHTTP client your client for Retrofit2 and you will be able to use your old cached data when the app goes offline.

android android-retrofit android-okhttp

Page 1 of 85
Next