Let's take this offline: Intro to Service Workers
Note: This is a pretty old post, which didn’t include the most fully-featured version of a beginner service worker. If you’re looking for a better getting started post, I recommend checking out this post.
Service Workers are a super cool bit of tech that allows your user’s browser to cache files and assets. Why is this so cool? Because eventually your user will lose connection. What happens then? Without something to bridge the gap between the time user loses connection and the time they regain connection, your user is just out of luck.
But no more! Service workers are here to help bridge the gap (at least in certain browsers). With the browser caching the files and images you choose, your user can continue using your site until the connection comes back.
To start using a service worker you’ll need some JavaScript for your application, as well a service worker file. I’ve created a demo Let’s get started!
Step One: Register your service worker
Registering your service work is very simple. The code looks like this:
if(navigator.serviceWorker) {
navigator.serviceWorker.register('service-worker.js');
}
The above code would go in your application’s JavaScript file. All it’s doing is letting the browser know to go ahead and register that service worker. Be sure to wrap your register function in an if-statement because cross-browser support for service workers isn’t quite there yet.
Step Two: Install your service worker
This code lives in your service-worker.js
file:
// service worker
self.oninstall = function() {
caches.open('cash').then(function(cache) {
cache.addAll([
'index.html',
'/img/my-image-name.jpg'
]);
});
};
Service workers expose certain events, including oninstall
and fetch
. The method above opens a cache in the browser and adds files to the cache. This way, if your app goes offline, the browser already has files and can load them in. Another benefit of this is speeding up your site’s response time. Since the files are cached, the browser no longer needs to serve files from a remote server. This ends up giving your users a great performance increase.
Step Three: Specify what happens on fetch
This code also lives in your service-worker.js
file:
// fetch function
// called when connection is lost
self.onfetch = function(event) {
// this will grab the very last request
event.respondWith(caches.match(event.request));
};
Note: This step was not a part of the original post.
Step Four: Handle for requests that aren’t in your cache:
// fetch function
// called when connection is lost
self.onfetch = function(event) {
// this will grab the very last request
event.respondWith(
caches.match(event.request)
.then(function(cachedFiles) {
if(cachedFiles) {
return cachedFiles;
} else {
fetch(event.request);
}
})
);
};
This is the function that will be called when your files need to be fetched. When files are cached, they are saved like a stack. Older caches are closer to the bottom, with newest caches being on top. The above function will grab the latest cache and send that to the browser where it can be rendered.
And with that, you’re done! You can grab this code and run it in a server (I like to use https-server). Once you stop the server, you can refresh the page and see your site is still there! Pretty cool, huh?
Offline Camp: Oregon
I was fortunate enough to spend a weekend at Offline Camp in Oregon this month. It was wonderful!
I’ve been really passionate about offline accessibility for a long time, although I’ve only recently learned it has a name: Offline First. Right around the time I learned that, I also had the opportunity to attend Offline Camp and meet a whole bunch of like-minded people. Spending the weekend with lots of socially-conscious individuals and brainstorming ways Offline First can help underprivileged individuals is one of the best ways to spend a weekend.
I’ve talked about the importance of reliable internet access here and here, but I’ll briefly summarize the idea of Offline First.
Offline First is a practice which considers the inevitable – what will happen when your web app goes offline? Offline First knows that your app will at some point go offline, so the idea is to put technology into place to help when that happens.
Additionally, there are many people who live their lives offline. In a world of online services, how can the needs of underprivileged people be met? Offline First attracts many people who are interested in how to help with this problem, and at camp we spent a lot of time talking about what we can do to help.
We talked about using Service Workers to cache online pages in the user’s browser. This would help users move from an online to an offline state, and back again. And we also talked about the future of Electron and Service Workers and how these two technologies work to achieve the same goals. Mesh networks came up in discussion time and again as a potential solution for providing internet access in remote locations.
One of my favorite discussions was talking about the economics of Offline First. I genuinely believe that the only way we’ll get Offline First into the wider consciousness of developers is if we can make a solid business case for it. We focused on making a business case for this and I’m super excited about what comes next from that.
We weren’t focused on Offline all the time though. Some of the best parts were from Passion Talks. Passion Talks are short, five-minute talks about anything you’re passionate about. I really enjoyed getting to know my fellow campers better through hearing about their interests.
Overall, I’d say attending Offline Camp was one of the best decisions I made in 2017. I learned so much, and I’m so hopeful for the future of offline. Many thanks to the organizers who put the camp on: Steven, Gregor, Bradley, and Teri. I know putting camp on was a lot of hard work, but I so appreciate you for doing it.
Note: Offline Camp offers scholarship opportunities for campers. If you’re able to help, it could be life-changing to the scholarship recipient. If you find that you’d like to attend camp, but need assistance, I would highly recommend contacting the organizers.
Let's take this offline: determine internet connection
More than 10 percent of Americans are without home internet. As such, it’s really important to be considerate of users with inconsistent internet. A user is without internet is effectively stuck on an island, unable to communicate with the outside world.
One of the simplest things we can do is check for network connection. To do this we can use: navigator.onLine
. This function – which is built into the JS spec and also has great cross-browser compatibility – returns a boolean value based on whether or not the user has a network connection. It might look like this:
if(navigator.onLine) {
console.log("User is online!");
} else {
console.log("User is not online. :(");
}
This is really handy because we can test for this before running any ajax calls and offer the user a much nicer experience because they won’t lose connection and suddenly hit a bunch of connection errors.
We can set up an event listener and continuously monitor network connection.
window.addEventListener('offline', function(event) {
console.log("We are offline! :(");
});
window.addEventListener('online', function(event) {
console.log("We are online! :)");
});
While no network connection will mean you are not on the internet, a true value can be misleading. Just because you are on a network doesn’t mean you have internet connection – for instance you can be on an internal network but the network doesn’t have an external internet connection. This might return true.
So what can we do to test if our true is actually true? If we get true, we can test if we can access an image.
fetch('some/img/url/here').then(
function(response) {
console.log(response);
})
.catch(function(err) {
console.log(err);
});
And when we look at the response, we can see the status code. If you have no connection, the catch will return your error with something like TypeError: Failed to fetch
.
And there you have it! You can check for network connection with navigator.onLine
, and to be sure you are truly online, you can use fetch
to pull an image and check the status code.
Let's take this offline: the importance of Internet-independent apps
We all know what the Internet is. It’s an online network of servers loading files, images, and other data from one location, across a network of servers, all the way to your monitor (or cellphone!) where you are reading this post.
The Internet is a collection of technologies that has come together to create something universal. It connects people to each other, even if they are oceans away. It has nearly all the knowledge of our day at your fingertips – if you know what to search for.
Most of all, the internet provides us opportunities. Opportunities to learn and connect and improve.
So imagine that you are in the middle of the bustling city. You’re surrounded by people, but you can’t talk to any of them. This is the reality of someone without consistent internet access.
Internet access is determined by many things, but the most likely predictor of access is income. With in-home internet ranging anywhere from $50 a month to more than $200 (not to mention hardware), it’s little wonder why many less fortunate individuals forego internet service.
A study based in Illinois revealed the number one reason people forego internet is because of the recurring monthly price. The same 2012 study revealed that 65% of those without Internet cited cost as a reason, and 29% cited cost as the number one reason. Roughly 12 percent of Americans are without in-home Internet, and that number increases for underrepresented minorities, and those under the poverty line.
So what can tech do? A free, universal network is out of reach for the foreseeable future, so what is the next best thing? Hybrid desktop apps and hybrid mobile apps can help bridge the gap between access and none.
Electron is a JavaScript framework that allows us to take our websites and wrap them in a Chromium window. The entire website lives on the user’s computer and the files are served from their hard drive – no network required.
Imagine the possibilities for users to be able to use your app with or without Internet access. Apps no longer requiring Internet – and in turn money – for use. Electron makes this all possible and also allows for seamless transitions between Internet access and no Internet access.
In the next few posts, I’ll show you how.
Make an ajax call into a c# controller without jQuery
Frequently I see devs needing to make calls into C# controllers and return some data to the front end. The most common tool I’ve seen these devs reach for is the jQuery $ajax
wrapper.
I asked the question: “Can this be done without jQuery?” And, overwhelmingly, the answer I got back was no. I didn’t think that made a lot of sense since jQuery is really just using JavaScript under the hood. So it would seem that you must be able to achieve this with plain old js.
After a bit more digging, I found that it is possible. Here’s how:
Let’s assume we have Hello World
being sent to us from the controller, and then we want to show it in an alert. Our front end won’t know what is going to be displayed until it is returned from the controller. Our controller method might look like this:
[HttpGet]
public ActionResult RespondToAjax()
{
return Json("Hello World!", JsonRequestBehavior.AllowGet);
}
Things to note here are that we’ve added the [HttpGet]
decorator above our method, thus denoting that this is a get and not a post. Also, with the JSON string we’re returning, we added JsonRequestBehavior.AllowGet
. Without this you’ll get an error because MVC doesn’t just allow gets willy-nilly.
Our js function might look like this:
function callCtrler() {
var req = new XMLHttpRequest();
req.open('GET', '@Url.Action("RespondToAjax", "Home")', true);
req.setRequestHeader('Content-Type', 'application/json');
req.onload = function () {
if (req.status >= 200 && req.status < 400) {
alert(req.responseText);
} else {
alert('We encountered an error!');
}
}
req.send();
}
One thing I want to note here is that we are using razor with @Url.Action
. It’s important to use @Url.Action
over a relative path (./file/path/
) because relative paths won’t always work in MVC. Using the above method won’t allow you to move your javascript into its own file. However, you can attach the @Url.Action
to an element with a data attribute and access it that way.
<form id="myForm" data-ctrl-url='@Url.Action("RespondToAjax", "Home")'></form>
Then in your JavaScript you can use:
var ctrlUrl = document.getElementById('myForm').getAttribute('data-ctrl-url');
Now let’s look at how we might do a POST. We’ll need both a controller method and a class this time.
[HttpPost]
public ActionResult PostFromAjax(TestModel postParams)
{
// do something with data, probably create db record
return Json("Success!");
}
public class TestModel
{
public string Name { get; set; }
public int Age { get; set; }
public string FavoriteColor { get; set; }
}
We have our action method using [HttpPost]
as the decorator this time. This allows us to post to the method. We also created a class for the data. You aren’t able to use built in types such as Dictionary
or object
, so you’ll need to create a custom class for the data to map to.
Now let’s take a look at what the js function looks like.
function postToCtrl() {
var tmpObj = {
Name: 'Carmen',
Age: 26,
FavoriteColor: 'Green'
};
var req = new XMLHttpRequest();
req.open('POST', '@Url.Action("PostFromAjax", "Home")', true);
req.setRequestHeader('Content-Type', 'application/json');
req.send(JSON.stringify(tmpObj));
}
Notice we made an object with the exact same properties as our class – this allows the data to be mapped to the class properly. We also stringified the data before sending it across.
And there you have it! GET and POST to C# methods without jQuery!
Checkout the test app I made for this.