Static sites and Functions

This is the fifth and last part of a series of posts I am writing about building a static site with VueJS. In this post, I will walk-through how you could use Functions-as-a-Service for your next project…or your current too.

Static sites typically don’t get all of the infrastructure attention, that other stuff does. Many developers still think that SPAs, whether static sites or not, need to be hosted with an always-running server infrastructure. It is unnecessary. With the advent of ServiceWorkers and the Progressive Web App movement, you really don’t need a server running all of the time.

Most devs are familiar with using a CMS like WordPress and then buying a domain to serve a website. Most websites don’t need all of that infrastructure. The price is a modest $4/mo according to their pricing page, you just get the basic with the paid plan. Not a big deal, though. But if you wanted to do SEO, custom analytics etc., you are looking at the next pricing tier or perhaps the most expensive one, at $25/mo, if you are looking for a few more knobs/levers to turn.

This is the architecture I used for Net Your Problem.

Static websites architecture with Functions-Page-1
Fig.1 – A simple cloud architecture for SPAs.

I have automated the part where I build the VueJS app and upload it to the Azure Storage account using a PowerShell script (see this gist), which is purely based on the AzureRM PS module.

Great. Now, let’s talk about how these infrastructure systems talk to each other, to cohesively power your next project.

The inner details

A CDN works by aggressively caching static resources (JS, CSS, HTML, PNGs etc.). You can also give a hint to a CDN service to cache additional mime-types by setting the Cache-Control header attribute. A typical CDN service has “edge” nodes all over the world. When you provision a CDN service from any of the cloud providers, you are not choosing the edge nodes in a particular region. CDN services are globally distributed, by default. Each CDN service does their billing differently. For example, I know that the Azure CDN service offers a tiered pricing model based on a group of countries in each tier. So traffic from different countries will be billed at different rates, based on the amount of data transferred from the CDN to the clients (browsers).

As shown in fig.1, the CDN is connect to the Function App, meaning that the CDN will source the static assets from the Function App. But the Function App in turn is connected to a storage account. Technically, all of these can be services from any of the 3 major cloud providers (Azure, AWS, GCP). It doesn’t make sense, though, to create individual services in multiple clouds. You would be charged more for inter-data center data transfer. So it is best to co-locate all of these, except of course, the CDN, which is always global, regardless of whose CDN service you end up using.

The connection between the CDN and the Function App is pretty simple, as it is just a matter of specifying the origin settings for the CDN. The connection between the Function App and the Storage Account requires a little bit more than just specifying the URL. We have to detect the incoming request at the Function and proxy it to the storage account, to let the storage account serve the static asset. Essentially, the Function App serves as a reverse-proxy here for some URL patterns, and for others, as a service that may or may not return a response, i.e., if there is an actual function that can handle the request.

Bonus! Let’s talk about automation

Let’s introduce a CI/CD pipeline into the mix. Azure has a really good all-in-one Ops platform called Azure DevOps, previously known as Visual Studio Team Services or VSTS, and even before that, Visual Studio Online. Anyway, the point of the platform is like Bitbucket or GitHub, where you have everything in one place. CI/CD pipeline integrations, release management, project planning (Kanban as well as sprint-based, whichever you are into), private/public repos, wikis, and also a private feed for your Nuget (.Net packages), npm or maven packages too!

Don’t take my word for it, though. After all, I am just some random programmer on the internet. People say a lot of things. But seriously, you should check it out.

Static websites architecture with Functions-Page-2

Here’s the screenshot of the pipelines page showing you the CI and PROD build pipelines for Net Your Problem.

Screenshot_2018-10-09 Builds - Pipelines

Here’s the CI/CD pipeline in Azure DevOps.

The pipeline is simple. Run npm ci –> npm install –> npm run build –> upload to Az Storage –> store artifacts. That’s it. I have a trigger for this pipeline, which would kick-off this build every time a branch is updated through a merge.

Admittedly, all of this may look like overkill. Trust me, it is not. I spend about a minute to run the build and then to upload the files to the Azure Storage. Then, sometimes, I have to purge the CDN cache, because, well, it works too well sometimes :). Overall, I could spend anywhere between 1-10 mins, and on average ~5 mins, deploying some changes. Now, repeat this several times as I am actively developing something or want to see how things look in Dev, or I want to show something to my ahem client (my girlfriend), the time investment adds-up really quickly. This setup allows me to focus just on the coding part and push up my changes for a quick review on the dev site, before I create a release for the PROD and have approved changes go to the live site immediately. All of this is a pleasant experience for me. To my girlfriend, it makes no difference, and that’s a good thing. She just sees the dev site and the live site. That’s it.

You see, the delays in development, often affects our customers. When I have a pipeline, that works well, my customer doesn’t get affected by it. They just see what they need to see. To them, in the end, it matters if they are able to see what they want and if it works. If the process gets in their way, they simply won’t get it. If I could use the overused automobile world for an analogy. This experience would be akin to taking our car to a shop for an oil change. At the end of it, we just want to drive our car out of the shop with a new oil filter, and fresh oil. We don’t care and most of us don’t want to know how they were able to do an oil change without an appointment. On the other hand, if the oil change took too long, then we want an explanation and all of the shop’s fancy equipment and ISO certifications wouldn’t save them from our negative experience.

Advertisements

Data-driven sites

This is the fourth part in a 5-part series about building a static site using VueJS. In this part, I’ll show you an example of how I built Net Your Problem by thinking of static content as data, rather than…well, static content.

We’ll go through this article looking at one specific section of Net Your Problem – the Projects section. There are two cards, with a title, a cover image for the card, and a button that opens a modal dialog.

Screenshot_2018-10-09 Net Your Problem
The Projects section of Net Your Problem.

Let’s look at the template for this,

There are two projects shown on the page, the template only shows one <card> tag with some data-bindings, along with some code to track some events with Google Analytics. Nothing too crazy here. But where is all of the content coming from? Netlify CMS.

If you think of your site as simply the presentation layer, that needs to serve content, think of a CMS as the database that stores your data, i.e., content. Netlify is a bit special. It actually doesn’t use any database, well, technically no. But one could argue that its use of a version-controlled filesystem is like a database. After all, a database has files too. Anyway, back to Netlify and how it works. Netlify basically provides a content editing platform on top of popular git version control systems like GitHub, and BitBucket. You can read about them here.

What I have done for Net Your Problem is, use Netlify CMS as the content editing platform, almost like WordPress. I italicized “almost” because, although WordPress is a CMS too. It differs in many ways. Well, first major difference is that, WordPress uses databases. You also need to host your content on their platform. On top of that, the articles, very much like this one, can only be published to a sub-domain of their own domain, at least under the Free plan. You can install WordPress on your own servers if you are adventurous and want to deal with all of the jazz of the setup and maintenance.

The thing with Netlify is, that the content simply gets stored as Markdown, JSON, or TOML files in your favorite version control SaaS platform. The files are organized in directory structures, that makes it easy for you to read the files. You can simply make ajax calls using the public APIs for GitHub or Bitbucket. The downside (if at all!) is, that you have to make your repository public in order to be able to call it anonymously, i.e., without authentication, from your website’s JS.

Let’s look at the code to fetch the content, which Netlify stores in our content repository. The gist below shows the script portion of the same Projects component, for which we saw the <template> portion above.

A few things to note,

  • axios is used as the HTTP client library
  • There are two API calls
    • GET the list of projects to show the cards.
    • GET the full content for a project when the user clicks on the READ MORE button in the card.

Once the content is downloaded from the JSON file, which is stored in the Bitbucket repo, I just update the data property that has a template binding attached to it and since the content is stored as a JSON file, the response from the Bitbucket API is…yep, JSON. This link will show you the response for the Projects lists JSON file.

And here’s the modal dialog that shows the full content of a “project” from Net Your Problem. The content itself is a markdown string, which is fed to the <vue-markdown> component, which you can see in the template above.

Screenshot_2018-10-09 Net Your Problem(1)
A modal dialog showing the content for one of the “projects” on Net Your Problem.

We just looked at one section in the site, but I am happy to report that 100% (…ok ok 99.9%..the header navigation is hard-coded) of the site is built this way. At first, all of the content was hard-coded in the site, and I slowly started to convert each component to be completely driven by data fetched through the APIs.

Convinced? Head over to the Netlify CMS docs to get started.

Components in a static site too

In the 2nd part of this series, we learned about some basics of rendering a view. We ended that topic by having a look at the router. The router just had one path, the root (/), which was mapped to a single component called the HelloComponent.

Here’s what we’ll do in this post:

  • Examine the HelloComponent
  • Add a new route, and a new component to handle the route
  • Add a nav link to take the user to the new route
  • Render another component inline without routing

HelloComponent

As you can see, the HTML code for most of what you see when you navigate to http://localhost:8080 comes from this file. So how does Vue know where to render the contents of this component? If you recall, the App.vue, the parent component, has a tag called and I mentioned that this is the output for any component that is routed to by the router.

Add a new route, and a new component to handle the route

Create a new file under the components/ folder. Let’s call it TestComponent.vue. And paste the following content.

Yes, I realize it doesn’t do much. I wanted to show you what a basic component looks like. It doesn’t need to have anything more than just that. With just that much content, you get a valid component. You should be able to imagine now, how easy it is to create a component and have your entire site split into pieces (components), that all come together eventually in the browser.

But wait. We are not done adding the component. We just added a file called TestComponent.vue but we haven’t really used it anywhere. So let’s add a new route to the router/index.js file.

Your router should now look like this:

We just added a new route to the router called /test, which routes to the newly-created TestComponent. Let’s test this out by going to: http://localhost:8080/#/test. You should see most of the content replaced with just the word “Test”. This means our new component has rendered. Great. We confirmed our new component to be working by manually going to the /test route.

Add a nav link to take the user to the new route

Let’s look at adding a route-link so that the user can navigate to this newly-created component of ours. Update the App.vue with this. Somewhere inside the <template> tag, add this markup

<router-link to="/test">Go to Test</router-link>

Vue will take care of the rest. It will render the router-link as an anchor (<a>) tag. You could do this programmatically too, if you don’t want to use an anchor tag. Refresh your browser and you should see a Go to Test link on your page. Click it. You should now see the contents of the TestComponent. That was it.

That’s it. We just learned how to use components in Vue to compose our app of little pieces, which are building blocks for a larger site or web app. I highly recommend reading more about VueRouter here.

Render another component inline without routing

So we saw how we could link to a custom component. What if we simply want to render another component inline, in the context of a parent component? Yep. You can do that too.

To do that, let’s first remove the <router-link> tag, and update your App.vue to this:

Then, go to http://localhost:8080 and you should see the contents of the TestComponent rendered inside the contents of App.vue, which also happens to consist of the contents from HelloComponent. So instead of replacing the contents from HelloComponent, we just augmented the contents of our new component in there.

Since you are rendering the component inline, there is no need for that new route, that we added to the router/index.js. You can remove the /test route from it as well, if you’d like.

 

Remote debugging an embedded WebView in Android

And there I was, thinking that I was done with AzureStorageExplorer for Android v1.0.0. But, nope. In my last round of testing, I couldn’t even login using my work account. The Sign In button didn’t do anything. I went back a few versions to see if the Android update for WebView could have done something to break it. That wasn’t it. The OAuth client library I was using had the recommended code for enabling Javascript in the WebView client so I knew that wasn’t it. I was able to login through the Azure OAuth flow using my personal Azure account. So, I knew there was something about the enterprise account login screen, which could have changed recently that ended up breaking the authentication in the app. It turns out that it did. Read on to know how I found out.

The first step was to prove that something was indeed broken in the login screen for enterprise accounts in Azure in embedded WebViews. To do this, I looked up Chrome’s remote debugging options and found this page describing how to remotely debug WebViews in mobile apps. So, I enabled the setting as recommended by Google, which required adding this piece of code in my activity’s onCreate(Bundle) method.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  WebView.setWebContentsDebuggingEnabled(true);
}

I ran the app, launched Chrome’s Developer Tools, and connected to the app’s WebView. This is what I found..

2016-12-26_09-49-21

That last error in the console is what was logged when I pressed the Sign In button after entering my work account credentials for Azure. It turns out that the page’s JS now has code to store something in the browser’s localStorage. The reason this isn’t a problem, if you were logging into Azure through a desktop or even a mobile browser, is because localStorage is enabled for every site by default in the non-embedded browser view. But for a WebView in an Android app you need to explicitly enable the DOM storage, just like you need to enable JS through the WebSettings class.

After finding this, it was easy to know where I needed to make the fix. So, I set out to fork the source repo of the OAuth library I was using, made the necessary changes and created a pull request. If you are using the same library and have run into this issue, you could clone my repo and build the project to produce the patched .jar (or an .aar), which you can use in your project directly until the author of the library can get to my pull request (if at all!).

canvas-charts

I just committed my first open source project on GitHub. You check it out here.

Here’s the example client with graphs:

Horizontal Bar Graph

 

Line graph

Simple validation of required fields in html5 mobile apps

I am working on an html5 mobile app and wanted a simple validation of required fields. Nothing fancy. Just a simple validation that would let the user know they left something blank. Since I was not interested in checking the format of the values entered in these fields, I felt that I should create something very generic that can be reused across platforms especially by using html5 features. A micro validation script, if I may.

So the setup is pretty easy. You need to mark your required input tags with the required attribute. Now, remember that this is an html5 attribute, so while older browsers won’t understand this it shouldn’t break anything. The next, optional, attribute to set is the data-tab-id, which when set tells validating function that it needs to mark that tab with an indication that it contains errors. This will be useful when you have data entry in multiple tabs.

<input id=”name” type=”text” placeholder=”name” maxlength=”25″ required=”required” data-tab-id=”#tab1″/>

Next is the CSS, simple, nothing extreme:

input.inputrequired{
border: 1px solid #ff0000;
}

a.haserrors{
color: #ff0000;
font-weight: bold;
}

The haserrors class is for the tab. You can change the selector for this class to match whatever tag you would have used with your tabs. Again, this is optional, you do not need to set the data-tab-id in the first place, if don’t need this and you will, in a moment, see why.

Finally, the script:

var _validateInput = function(e){
var currentTarget = e.currentTarget;
var tabId = currentTarget.dataset[“tabId”];
var tab = $(tabId);
var result = false;
if(currentTarget.required && currentTarget.value == “” && tab != null && typeof tab !== “undefined”){
$(“#” + currentTarget.id).css(“class”, “inputrequired”);
tab.addClass(“haserrors”);
}
else {
currentTarget.setAttribute(“class”, “”);
//before removing the haserrors from the tab class check if any other siblings are in error
if($(“#” + currentTarget.id).siblings(“.inputrequired”).length <= 0 && tab != null && typeof tab !== “undefined”){
tab.removeClass(“haserrors”);
}
//set result to true since this input is fine
result = true;
}
tab = null;
currentTarget = null;
return result;
};

The actual event trigger:

//validates onblur the inputs that have required attribute
$(“input”).on(“blur”, function(e){
validateInput(e);
}, false);

Although the required attribute is backward compatible, meaning no existing functionality would break, there is no need to worry about old browsers for mobile anyway. The data- attributes are an html5 addition as well. They won’t be recognized by older browsers and you cannot access the DOM string map dataset from an element in older browsers. And again, there is no need to worry about these things unless you are planning to use this for the desktop in which case you will need to replace the use of dataset with a JS framework’s data() method which provides cross-browser support.

One nice thing about the “required” attribute is that when you place fields with required attributes inside a <form> element, when the form is submitted, the browser automatically validates them and shows a nice tooltip that the a value is required for the element. I wrote this script because I wasn’t using a <form> element and don’t think the required attribute validation is supported in mobile webview at least it is not widely supported.

iOS Style Scroller For PhoneGap App

I have moved the files to GitHub at https://github.com/praneetloke/SpinningWheel-Wrapper

I am going to show you the extensions I made to the spinningwheel script created by the developer of the popular iScroll script. The spinningwheel script by Matteo works great just out-of-the-box. It’s much better than mobiscroll. Spinningwheel allows you to add slots of custom data which means if you are about to use spinningwheel for a date picker then you have to write your own logic for handling the days/months logic and then there’s the leap year calculation for February. But working with spinningwheel was easy and interesting. Matteo’s code is always organized as you can see from the source of his iScroll and SwipeView scripts. It was just a matter of writing down what extensions I wanted to make to his script. So, I first started out by realizing that I wanted a wrapper around the spinningwheel; that’s where the second file in the attachments comes to use. Most importantly his scripts are always written in regular javascript and always has a destroy method (which is very useful when you want to manage memory). Writing in regular javascript means no external dependencies. Following the same guidelines I too made the extensions in regular javascript and also the new events you can subscribe to in the spinningwheel are written in regular javascript. Another advantage of using spinningwheel over mobiscroll is spinningwheel uses translate3d for the transitions which means it will look and work awesome on iOS devices and even on higher end Android devices (phones and tablets). Oh and another one again is that since spinningwheel is written in regular javascript it easy for you to make extensions to it based on your needs unlike mobiscroll which relies of jQuery selectors heavily which means if you are not using jQuery, good luck!

The ScrollWheel class I wrote allows you to use the SpinningWheel instance as a singleton object. You can create an instance of ScrollWheel and pass in parameters like ‘date’, ‘time’ or ‘custom’ (if you want to use custom slots). Most of you would want to use the date and time presets that I created. The custom presets allow you to simply bind a click listener to text input and show the spinning wheel without having to do any extra work.

As you will see in the ScrollWheel.custom file, I have set the cancel action to call the post callback (which you would have passed when an instance of ScrollWheel was created) immediately and a null value for the results. In your post callback be sure to add a check for the results array so you don’t accidentally update the existing value in the text box (if any) to null. You can see what I have done with my post callback in ScrollWheel_Init.

The HTML element that you need to have to actually show the spinningwheel is pretty simple. Just use something like this (or exactly):

<input id=”timePicker” type=”text” readonly=”readonly” onfocus=”preventFocus(this)” onclick=”showTimePicker(event, this.id, this.value);”/>

or you can modify the showTimePicker function to take just the event object as a parameter and modify the click handler in the element to:

<input id=”timePicker” type=”text” readonly=”readonly” onfocus=”preventFocus(this)” onclick=”showTimePicker(event);”/>

Or you could alternatively ‘bind’ to the click event using jQuery or Zepto as well. I will not explain the logic of updateDates since it is pretty straight forward. In spinningwheel.js, you will find that I have added a new event handler that you can set via an instance of ScrollWheel (or directly via SpinningWheel) called scrollEndAction. This is what you need to set if you want to subscribe to the scrollEnd event and have a handler invoked every time the user stops scrolling a slot.

A snippet of the handleEvent function in spinninwheel where I added the invocation of the scrollEndAction

else if (e.type == ‘touchend’) {

if (e.currentTarget.id == ‘sw-cancel’ || e.currentTarget.id == ‘sw-done’) {

this.tapUp(e);

} else if (e.currentTarget.id == ‘sw-frame’) {

this.scrollEnd(e);

//console.log(“calling scroll end action”);

setTimeout($.proxy(this.scrollEndAction(this.activeSlot), this), 100);

var self = this;

setTimeout(function () {self.scrollEndAction.apply(this, [self.activeSlot]);}, 100);

}

}

There are two new public functions I have added to the spinningwheel.js, called showSlotValuesAfter and hideSlotValuesAfter. These functions will allow you to pass the slot index (0 based), the last key of the json object that the slot holds as values, and the number of values you want to show/hide after that last key. The reason for these is to help with hiding the dates that don’t apply for a particular month and then showing them again when they are applicable. Look at updateDates to understand how this is done. I have also made sure that when you call on these functions the corresponding slot’s max scroll boundary is updated so that it doesn’t allow you to scroll over the ‘hidden’ values. Find out why I call them ‘hidden’ values. 

The good thing about the wrapper I have written is that it checks if an instance of a ScrollWheel is already open and if so it won’t allow you to open another one. This was the only problem with spinningwheel out-of-the-box. SpinningWheel is a global instance and you have to manage it. If you open too many instances at the same time you are likely to chew up memory from the browser and can see significant impact to the performance of the page and even states and values will be corrupted. This is why I wrote the wrapper to make sure that I take care of this.

There is no technical reason for me to name the wrapper as ScrollWheel so you can change the name of the class to whatever you like. Just remember to retain the names and the original license at the top of the files. Although I claim no responsibility for the stability of these extensions and no support for them, I do know that they work because I use them and you can ask me for help as long as you don’t blame me for anything after all you downloaded them, I did not force you to.

A word of caution about spinningwheel’s goal. It is to be used with webkit based browsers. It was created to be used with iOS and Android phones as they implement the WebKit framework and also it is original intended to be used with touch enabled WebKit platforms which means it will not work with desktop browsers (Chrome, Safari, or Opera) out-of-the-box but there’s good news, download the touché framework I blogged about last week and it should work like a charm. I may look into porting this for the WP7 but not yet. I’ll tackle that when I get there but if you do be sure to let me know. It’ll save me some time 🙂

Be sure to ask me if you don’t understand the sources. Just don’t be a duck about it. That’s it for now. Enjoy your new spinningwheel!