KnockoutJS: An Introduction

Share this article

The rise of sophisticated web applications, such as GMail, has given birth to an army of javascript frameworks. These frameworks allow you to, basically, create a client-side application within your web application. Terms such as “MVC” and other design patterns, once saved for only the server-side of the web, are now applied to just the the client bits of your code. The more popular frameworks these days are http://backbonejs.org, http://emberjs.com, and http://knockoutjs.com. This article will take a whirlwind look at the latter.

(NOTE: It has taken all of my willpower not to entitle this article ‘Momma Said KnockoutJS You Out!’ or something similar. In fact, my willpower needed the help of narcotics.)

Knockout takes the Model-View-View-Model (MVVM) approach to application architecture. MVVM is a architectural pattern that has (some of) its roots in Microsoft and their Windows Presentation Foundation/Silverlight/Failed Attempt at making ASP.NET Web Forms usable. Being honest, I avoided Knockout due to these roots, and I am guessing that many developers do as well. It wasn’t until I inherited an application that uses Knockout that I was forced to I actually learn about it.

Yet again, I have been pleasantly surprised to know that my groundless developer bias was very wrong.

At Least it’s Not MVC

Looking at the Knockout home page, the key concepts it lists are:

  • Declaritive Bindings
  • Automatic UI Refresh
  • Dependence Tracking
  • Templating

I am not going to run through each of these, as you have the same access to their site. I will say what I think it means, in a nutshell. As I see it, the strength of Knockout is it’s data-binding. If you’ve never used a framework that binds the UI to a model (the view model, it this case) the core concept is that any changes to that model are immediately reflected in the view/UI. Adobe Flex is, in my opinion, the most well-known data binding framework. Data binding is enormously powerful, especially if you have pieces of your model strewn about the UI. Updating a value in one place and having 10 UI elements change as a result will bring a smile to any trollface.

Data-binding is the sweetest sauce that Knockout provides, but it is more than that. The templating is very powerful, and you can use any javascript templating library you like (jQuery Templates, mustache, etc). Knockout is also extensible, providing a plugin framework that we will take advantage of in the demo application I’ve put together to show Knockout in action.

Lick it, Lick it Good

The demo application is a simple, yet kind of disgusting, app called LinkLicker. The idea is that links are submitted and you lick the ones you like. Yes, I am a 4 year-old.

The server-side of this application is both a Rails app and irrelevant. Your server side application simply needs to spit back JSON, so it can be Sinatra or Rails or Cuba or whatever. I shant bore you with the details of the web app, but they are in the github repository if you simply must know.

The app shows a form that allows links to be submitted, a list of submitted links, and a list of links that have been licked by the current user. When a link has been licked, it’s icon changes. This, I am sure, is the next FacebookTwitter.

Setup

You will need the Knockout code available in your web app to make this work. I’ve put mine in the vendor directory, along with the most commonly used Knockout plugin. This plugin is the Knockout Mapping plugin, and it takes all the hard work out of mapping JSON from the server to javascript objects on the client.  Note: I won’t really cover the Mapping plugin in this article, but the important bit is it creates observables out of all our JSON properties when it maps the objects. You’ll see what observables are shortly.

Structurally, I like to put each “model” class in it’s own file on the client. LinkLicker really only has one model: Link. I also like to have a global class that represents my application that I call, unremarkably, App. The final directory sturcture for the javascript looks like:

UI

The UI for LinkLicker was driven heavily by my desire to do as little as possible while still getting the Knockout concepts across. As I mentioned above, there is a form, as well as three lists of links. The three lists are All Links, New (meaning, unlicked), and licked links. As you may have surmised, a link can be in more than one of these lists at any given time. The screenshot of the UI is below:

Bootstrap

Bootstrapping LickLinker consists of binding to the form and creating our three lists. We’ll make all of these properties on our App. Easy stuff.

var App = function() {
  this.newLink = ko.observable(new Link());
  this.mylinks = ko.observableArray();
  this.lickedLinks = ko.observableArray();
  this.allLinks = ko.observableArray();
};

You are likely wondering what all this observable madness be. When you make a property an observable, it becomes a super-powerful notifier of changes to that property. Knockout will keep track (as best it can, and it’s pretty good at it) of things that are interested in your property and notify then when it changes. The Observables docs on the Knockout site are great, and there are interactive tutorials to use as well.

In short, and if you can think ahead, when we bind our UI (form) to the newLink it will then notify our form of changes and vice-versa.

The other three properties are observableArrays. These are, conceptually, exactly the same as observables, except that they work on a collection. An observableArray will track what objects are in the array, and it has many helper methods to add and remove these objects. You can easily pop or push items onto an observableArray, slice it, get its length, reverse it, and many other methods you’d expect.

Again, since observables and observableArrays are notifiers of change, when we bind the lists in our UI to these lists of links, everything will be updated to reflect the latest state. Hopefully, this concept is sinking in.

The Model

The model for LinkLicker is just a link and it has 3 properties: url, description, and lickedByCurrentUser.

The Form

Knockout is all about data-binding, remember? As such, we can bind a new Link to the form and, then, as the user enters data it will populate this newly bound Link. In the code snippet above, we create our newLink property as an observable. Now, to bind it to the form:

<form id="create_form" class="one-third column alpha" data-bind="submit: createNewLink"><!-- ko with: newLink -->

 <label for="link_url">Link:</label>
 <input id="link_url" type="text" name="linkhttps%3A%2F%2Feditor.sitepoint.com" data-bind="value: url" />

 <label for="description">Description:</label>
 <input id="link_description" type="text" name="link[description]" data-bind="value: description" />

 <!-- /ko -->
 <input type="submit" value="Add" /></form>

In the above snippet, focus on the <!-- ko with: newLink --> line. Knockout offers a couple of different ways to bind parts of the DOM to the view model. This example uses “containerless bindings”, which means, it doesn’t create a DOM element for that declaration.

The other way to do it is to create a DOM element (such as a div) and add a data-bind attribute specifying the template. There are loads of examples on the Knockout site of the latter if you are confused.

That ko with: newLink tells Knockout to find the newLink property of whatever this view is bound to and scope everything within the block to it. This should be a bit clearer if you look at the input tags. Each input tag has a data-bind attribute that binds the value of that input to a property on newLink.

The Ties the Bind

With our UI in place, we need some mechanism to bind it to our view model. In this app, the view model is our global App class on the client. The App is bound to the view when the page is loaded, as shown here:

$(function(){
  App.globalApp = new App();
  ko.applyBindings(App.globalApp);

  App.globalApp.loadLinks();
});

Easy peasy.

Filtering a list can be done by creating two “computeds” (formerly dependentObservables). Here, let me show you:

this.allLinks = ko.observableArray();

this.lickedLinks = ko.computed(function() {
  return this.allLinks().filter(function(link) {
    return link.isLicked();
  });
}, this);

this.newLinks = ko.computed(function() {
  return this.allLinks().filter(function(link) {
    return !link.isLicked();
  });
}, this);

A computed is an observable that depends on the values of other observables. You can see that we filter the allLinks observableArray based on the value of isLicked() for each link. The second argument to the computed (this) is the context to bind the function.

Computeds are enormously helpful and you’ll find yourself using them everywhere.

Extending KnockoutJS

While Knockout has many, many bindings that you can use, such as text, html, css, visible, you will find occasions that require a new binding. Writing a custom binding is very simple. As an example, I am going to write a hidden binding which is the opposite of the visible binding. It’s is below:

ko.bindingHandlers.hidden = {
  update: function(element, valueAccessor){
    var value = ko.utils.unwrapObservable(valueAccessor());

    ko.bindingHandlers.visible.update(element, function() {
      return !value;
    });

  }
};

In the binding, I grab the value of the element being bound, and then pass it to the existing visible binding. The result is negated and returned.

Obviously, this is a trivial binding, but you can get very complex if needed. Google will show you bindings for jQuery UI elements, for example, along with thousands of other custom bindings that the community has authored.

Momma Said Wrap This Up!

There were many other aspects of Knockout I would like to cover, but I think we’ve taken a nice look across many of it’s features. Hopefully, when you are evaluating javascript frameworks, this article will help you make a decision.

Remember, the entirety of the LinkLicker source is yours to browse, use, mock, whatever on github.

If you have questions on LinkLicker or Knockout, hit me in the comments. Thanks for reading!

Glenn GoodrichGlenn Goodrich
View Author

Glenn works for Skookum Digital Works by day and manages the SitePoint Ruby channel at night. He likes to pretend he has a secret identity, but can't come up with a good superhero name. He's settling for "Roob", for now.

Share this article
Read Next
Leveraging Progressive Web Apps (PWAs) for Enhanced Mobile User Engagement
Leveraging Progressive Web Apps (PWAs) for Enhanced Mobile User Engagement
SitePoint Sponsors
10 Artificial Intelligence APIs for Developers
10 Artificial Intelligence APIs for Developers
SitePoint Sponsors
The Ultimate Guide to Navigating SQL Server With SQLCMD
The Ultimate Guide to Navigating SQL Server With SQLCMD
Nisarg Upadhyay
Retrieval-augmented Generation: Revolution or Overpromise?
Retrieval-augmented Generation: Revolution or Overpromise?
Kateryna ReshetiloOlexandr Moklyak
How to Deploy Apache Airflow on Vultr Using Anaconda
How to Deploy Apache Airflow on Vultr Using Anaconda
Vultr
Cloud Native: How Ampere Is Improving Nightly Arm64 Builds
Cloud Native: How Ampere Is Improving Nightly Arm64 Builds
Dave NearyAaron Williams
How to Create Content in WordPress with AI
How to Create Content in WordPress with AI
Çağdaş Dağ
A Beginner’s Guide to Setting Up a Project in Laravel
A Beginner’s Guide to Setting Up a Project in Laravel
Claudio Ribeiro
Enhancing DevSecOps Workflows with Generative AI: A Comprehensive Guide
Enhancing DevSecOps Workflows with Generative AI: A Comprehensive Guide
Gitlab
Creating Fluid Typography with the CSS clamp() Function
Creating Fluid Typography with the CSS clamp() Function
Daine Mawer
Comparing Full Stack and Headless CMS Platforms
Comparing Full Stack and Headless CMS Platforms
Vultr
7 Easy Ways to Make a Magento 2 Website Faster
7 Easy Ways to Make a Magento 2 Website Faster
Konstantin Gerasimov
Powerful React Form Builders to Consider in 2024
Powerful React Form Builders to Consider in 2024
Femi Akinyemi
Quick Tip: How to Animate Text Gradients and Patterns in CSS
Quick Tip: How to Animate Text Gradients and Patterns in CSS
Ralph Mason
Sending Email Using Node.js
Sending Email Using Node.js
Craig Buckler
Creating a Navbar in React
Creating a Navbar in React
Vidura Senevirathne
A Complete Guide to CSS Logical Properties, with Cheat Sheet
A Complete Guide to CSS Logical Properties, with Cheat Sheet
Ralph Mason
Using JSON Web Tokens with Node.js
Using JSON Web Tokens with Node.js
Lakindu Hewawasam
How to Build a Simple Web Server with Node.js
How to Build a Simple Web Server with Node.js
Chameera Dulanga
Building a Digital Fortress: How to Strengthen DNS Against DDoS Attacks?
Building a Digital Fortress: How to Strengthen DNS Against DDoS Attacks?
Beloslava Petrova
Crafting Interactive Scatter Plots with Plotly
Crafting Interactive Scatter Plots with Plotly
Binara Prabhanga
GenAI: How to Reduce Cost with Prompt Compression Techniques
GenAI: How to Reduce Cost with Prompt Compression Techniques
Suvoraj Biswas
How to Use jQuery’s ajax() Function for Asynchronous HTTP Requests
How to Use jQuery’s ajax() Function for Asynchronous HTTP Requests
Aurelio De RosaMaria Antonietta Perna
Quick Tip: How to Align Column Rows with CSS Subgrid
Quick Tip: How to Align Column Rows with CSS Subgrid
Ralph Mason
15 Top Web Design Tools & Resources To Try in 2024
15 Top Web Design Tools & Resources To Try in 2024
SitePoint Sponsors
7 Simple Rules for Better Data Visualization
7 Simple Rules for Better Data Visualization
Mariia Merkulova
Cloudways Autonomous: Fully-Managed Scalable WordPress Hosting
Cloudways Autonomous: Fully-Managed Scalable WordPress Hosting
SitePoint Team
Best Programming Language for AI
Best Programming Language for AI
Lucero del Alba
Quick Tip: How to Add Gradient Effects and Patterns to Text
Quick Tip: How to Add Gradient Effects and Patterns to Text
Ralph Mason
Logging Made Easy: A Beginner’s Guide to Winston in Node.js
Logging Made Easy: A Beginner’s Guide to Winston in Node.js
Vultr
How to Optimize Website Content for Featured Snippets
How to Optimize Website Content for Featured Snippets
Dipen Visavadiya
Psychology and UX: Decoding the Science Behind User Clicks
Psychology and UX: Decoding the Science Behind User Clicks
Tanya Kumari
Build a Full-stack App with Node.js and htmx
Build a Full-stack App with Node.js and htmx
James Hibbard
Digital Transformation with AI: The Benefits and Challenges
Digital Transformation with AI: The Benefits and Challenges
Priyanka Prajapat
Quick Tip: Creating a Date Picker in React
Quick Tip: Creating a Date Picker in React
Dianne Pena
How to Create Interactive Animations Using React Spring
How to Create Interactive Animations Using React Spring
Yemi Ojedapo
10 Reasons to Love Google Docs
10 Reasons to Love Google Docs
Joshua KrausZain Zaidi
How to Use Magento 2 for International Ecommerce Success
How to Use Magento 2 for International Ecommerce Success
Mitul Patel
5 Exciting New JavaScript Features in 2024
5 Exciting New JavaScript Features in 2024
Olivia GibsonDarren Jones
Tools and Strategies for Efficient Web Project Management
Tools and Strategies for Efficient Web Project Management
Juliet Ofoegbu
Choosing the Best WordPress CRM Plugin for Your Business
Choosing the Best WordPress CRM Plugin for Your Business
Neve Wilkinson
ChatGPT Plugins for Marketing Success
ChatGPT Plugins for Marketing Success
Neil Jordan
Managing Static Files in Django: A Comprehensive Guide
Managing Static Files in Django: A Comprehensive Guide
Kabaki Antony
The Ultimate Guide to Choosing the Best React Website Builder
The Ultimate Guide to Choosing the Best React Website Builder
Dianne Pena
Exploring the Creative Power of CSS Filters and Blending
Exploring the Creative Power of CSS Filters and Blending
Joan Ayebola
How to Use WebSockets in Node.js to Create Real-time Apps
How to Use WebSockets in Node.js to Create Real-time Apps
Craig Buckler
Best Node.js Framework Choices for Modern App Development
Best Node.js Framework Choices for Modern App Development
Dianne Pena
SaaS Boilerplates: What They Are, And 10 of the Best
SaaS Boilerplates: What They Are, And 10 of the Best
Zain Zaidi
Understanding Cookies and Sessions in React
Understanding Cookies and Sessions in React
Blessing Ene Anyebe
Enhanced Internationalization (i18n) in Next.js 14
Enhanced Internationalization (i18n) in Next.js 14
Emmanuel Onyeyaforo
Essential React Native Performance Tips and Tricks
Essential React Native Performance Tips and Tricks
Shaik Mukthahar
How to Use Server-sent Events in Node.js
How to Use Server-sent Events in Node.js
Craig Buckler
Five Simple Ways to Boost a WooCommerce Site’s Performance
Five Simple Ways to Boost a WooCommerce Site’s Performance
Palash Ghosh
Elevate Your Online Store with Top WooCommerce Plugins
Elevate Your Online Store with Top WooCommerce Plugins
Dianne Pena
Unleash Your Website’s Potential: Top 5 SEO Tools of 2024
Unleash Your Website’s Potential: Top 5 SEO Tools of 2024
Dianne Pena
How to Build a Chat Interface using Gradio & Vultr Cloud GPU
How to Build a Chat Interface using Gradio & Vultr Cloud GPU
Vultr
Enhance Your React Apps with ShadCn Utilities and Components
Enhance Your React Apps with ShadCn Utilities and Components
David Jaja
10 Best Create React App Alternatives for Different Use Cases
10 Best Create React App Alternatives for Different Use Cases
Zain Zaidi
Control Lazy Load, Infinite Scroll and Animations in React
Control Lazy Load, Infinite Scroll and Animations in React
Blessing Ene Anyebe
Building a Research Assistant Tool with AI and JavaScript
Building a Research Assistant Tool with AI and JavaScript
Mahmud Adeleye
Understanding React useEffect
Understanding React useEffect
Dianne Pena
Web Design Trends to Watch in 2024
Web Design Trends to Watch in 2024
Juliet Ofoegbu
Building a 3D Card Flip Animation with CSS Houdini
Building a 3D Card Flip Animation with CSS Houdini
Fred Zugs
How to Use ChatGPT in an Unavailable Country
How to Use ChatGPT in an Unavailable Country
Dianne Pena
An Introduction to Node.js Multithreading
An Introduction to Node.js Multithreading
Craig Buckler
How to Boost WordPress Security and Protect Your SEO Ranking
How to Boost WordPress Security and Protect Your SEO Ranking
Jaya Iyer
Understanding How ChatGPT Maintains Context
Understanding How ChatGPT Maintains Context
Dianne Pena
Building Interactive Data Visualizations with D3.js and React
Building Interactive Data Visualizations with D3.js and React
Oluwabusayo Jacobs
JavaScript vs Python: Which One Should You Learn First?
JavaScript vs Python: Which One Should You Learn First?
Olivia GibsonDarren Jones
13 Best Books, Courses and Communities for Learning React
13 Best Books, Courses and Communities for Learning React
Zain Zaidi
5 jQuery.each() Function Examples
5 jQuery.each() Function Examples
Florian RapplJames Hibbard
Implementing User Authentication in React Apps with Appwrite
Implementing User Authentication in React Apps with Appwrite
Yemi Ojedapo
AI-Powered Search Engine With Milvus Vector Database on Vultr
AI-Powered Search Engine With Milvus Vector Database on Vultr
Vultr
Understanding Signals in Django
Understanding Signals in Django
Kabaki Antony
Why React Icons May Be the Only Icon Library You Need
Why React Icons May Be the Only Icon Library You Need
Zain Zaidi
View Transitions in Astro
View Transitions in Astro
Tamas Piros
Getting Started with Content Collections in Astro
Getting Started with Content Collections in Astro
Tamas Piros
What Does the Java Virtual Machine Do All Day?
What Does the Java Virtual Machine Do All Day?
Peter Kessler
Become a Freelance Web Developer on Fiverr: Ultimate Guide
Become a Freelance Web Developer on Fiverr: Ultimate Guide
Mayank Singh
Layouts in Astro
Layouts in Astro
Tamas Piros
.NET 8: Blazor Render Modes Explained
.NET 8: Blazor Render Modes Explained
Peter De Tender
Mastering Node CSV
Mastering Node CSV
Dianne Pena
A Beginner’s Guide to SvelteKit
A Beginner’s Guide to SvelteKit
Erik KückelheimSimon Holthausen
Brighten Up Your Astro Site with KwesForms and Rive
Brighten Up Your Astro Site with KwesForms and Rive
Paul Scanlon
Which Programming Language Should I Learn First in 2024?
Which Programming Language Should I Learn First in 2024?
Joel Falconer
Managing PHP Versions with Laravel Herd
Managing PHP Versions with Laravel Herd
Dianne Pena
Accelerating the Cloud: The Final Steps
Accelerating the Cloud: The Final Steps
Dave Neary
An Alphebetized List of MIME Types
An Alphebetized List of MIME Types
Dianne Pena
The Best PHP Frameworks for 2024
The Best PHP Frameworks for 2024
Claudio Ribeiro
11 Best WordPress Themes for Developers & Designers in 2024
11 Best WordPress Themes for Developers & Designers in 2024
SitePoint Sponsors
Top 10 Best WordPress AI Plugins of 2024
Top 10 Best WordPress AI Plugins of 2024
Dianne Pena
20+ Tools for Node.js Development in 2024
20+ Tools for Node.js Development in 2024
Dianne Pena
The Best Figma Plugins to Enhance Your Design Workflow in 2024
The Best Figma Plugins to Enhance Your Design Workflow in 2024
Dianne Pena
Harnessing the Power of Zenserp for Advanced Search Engine Parsing
Harnessing the Power of Zenserp for Advanced Search Engine Parsing
Christopher Collins
Build Your Own AI Tools in Python Using the OpenAI API
Build Your Own AI Tools in Python Using the OpenAI API
Zain Zaidi
The Best React Chart Libraries for Data Visualization in 2024
The Best React Chart Libraries for Data Visualization in 2024
Dianne Pena
7 Free AI Logo Generators to Get Started
7 Free AI Logo Generators to Get Started
Zain Zaidi
Turn Your Vue App into an Offline-ready Progressive Web App
Turn Your Vue App into an Offline-ready Progressive Web App
Imran Alam
Clean Architecture: Theming with Tailwind and CSS Variables
Clean Architecture: Theming with Tailwind and CSS Variables
Emmanuel Onyeyaforo
How to Analyze Large Text Datasets with LangChain and Python
How to Analyze Large Text Datasets with LangChain and Python
Matt Nikonorov
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form