Tag Archives: WebComponents

Part 7: Sharing data in Knockout Components (1/2) – Building a Tree Component

Continuing my account of using KO Components and the building of the Silkthread SPA, today I am going to build another component – a Tree view component.

Note: All my articles on KO Components are now categorized and you can bookmark the this link for easy reference. All future articles will automatically appear in that list.

No one really uses a TreeView in a public facing Website today, but the underlying hierarchical data representation is still very valid and the TreeView has transformed into a series of Folding panels, cascading dropdowns, CSS Menus or in case of mobile devices cascading set of lists.

I came across the need for the TreeView because I wanted to build a Documentation Index for the SilkThread site. So this is like dog-fooding of SilkThread SPA itself. Today we’ll explore the following things:

  1. How to layout the TreeView, creating the Tree and TreeNode components (1/2).
  2. How to pass data into nested Components (we have done this in the previous article also) (1/2)
  3. Styling the TreeView (1/2)
  4. How to raise and handle events using Amplify.js (2/2)

Creating the Tree and the TreeNode components

I am starting with v6 label of my BuildingSpaWithKO project in Github.

Adding a new Page

1. First up, we’ll add a new ‘page component’ called docs.

Adding a new Page Component

a. Add new folder under App/pages called docs
b. Add a docs.js and docs.html file in the docs folder. Remove all the boilerplate code/markup that Visual Studio adds and setup the component as follows

define(['knockout', 'text!./docs.html'], function (ko, docsTemplate) {
    function docsViewModel(params) {
        var self = this;
        self.title = ko.observable('SilkThread - Yet another SPA Framework');
        self.data ={
            title: 'Documentation Home',
            nodes: []
        };
        return self;
    }
    return { viewModel: docsViewModel, template: docsTemplate };
});

The component is pretty self explanatory, it has two properties, title and data. The title property has a default text but is an observable, so if we bind it to an html element if will be updated in the UI if we decide to change the Title at runtime.

The ‘nodes’ property has an empty JavaScript object at the moment and we will pump mock data into it once our component is ready.

Registering the Docs page

To register our new page we update the app.js by adding the new page definition to it.

app = {
    components: {
        greeter: {
            name: 'greeter',
            template: 'App/components/greeter/greeting'
        },
        tabitem: {
            name: 'tabitem',
            template: 'App/components/tabitem/tabitem'
        },
        tabbedNavigation: {
            name: 'tabbed-navigation',
            template: 'App/components/tabbed-navigation/tabbed-navigation'
        }
    },
    pages: {
        home: {
            name: 'home',
            template: 'App/pages/home/home'
        },
        docs: {
            name: 'docs',
            template: 'App/pages/docs/docs'
        },
        settings: {
            name: 'settings',
            template: 'App/pages/settings/settings'
        }
    }
}

We register the component in startup.js as follows:

ko.components.register(app.pages.docs.name, { require: app.pages.docs.template });

To navigate to the ‘Docs’ page we add a link in _Layout.cshtml page. as seen below the href is pointing to the page-name we registered above.

                <ul class="nav navbar-nav">
                    <li>
                        <a href="/">Home</a>
                    </li>
                    <li>
                        <a href="settings">Settings</a>
                    </li>
                    <li>
                        <a href="docs">Docs</a>
                    </li>
                </ul>

image

Finally we add the docs route to our router in router.js as follows

    return new Router({
        routes: [
            { url: '/', params: { page: 'home' } },
            { url: 'home', params: { page: 'home' } },
            { url: 'docs', params: { page: 'docs' } },
            { url: 'samples', params: { page: 'samples' } },
            { url: 'settings', params: { page: 'settings' } }
        ]
    });

With the Docs page in place we can get down to the business of creating the actual Tree component.

Building a Tree View using KO Components

A tree component can logically have two sub-parts. First the container and second a Node object that can itself contain a list of Nodes. So we create two components for this purpose:

1. The Tree-Node component
2. The Tree component

The Tree Node Component

We look at the node view model before we look at the container. As standard with all KO Components we create a Tree folder under the Scripts/app/components folder and add two files tree-node.html and tree-node.js

The Node view model (tree-node.js)

define(["knockout", "text!./tree-node.html"], function (ko, treeNodeTemplate) {
    function treeNodeViewModel(params) {
        var self = this;
        self.title = ko.observable('Default');
        self.url = ko.observable('/');
        self.nodes = ko.observableArray();
        self.expanded = ko.observable(true);
        if (params.node) {
            self.title(params.node.title);
            if (params.node.expanded != null) {
                self.expanded(params.node.expanded);
            }
            self.nodes().push.apply(self.nodes(), params.node.nodes);
        }

        self.changeState = function () {
            self.expanded(!self.expanded());
        }
        return self;
    }
    return { viewModel: treeNodeViewModel, template: treeNodeTemplate };
});

The treeNodeViewModel has four fields and a function() to store the information and react to actions on it.

title – This is the text that’s displayed on the Node
url – A relative or absolute URL to navigate to when user clicks on the node.
nodes – A collection of more treeNodeViewModels that are child nodes of the current node. Note that, the nodes collection essentially makes the view model recursive.
expanded – A property storing the state of the current node, as to whether its child nodes are shown or hidden. This is applicable only when the nodes collection has more than one element.
changeState() – This function is attached to the click event of the span element in the view. The span has the text + or – depending on the value of ‘expanded’ property.

The Node View (tree-node.html)

The corresponding markup for the Node view is as follows:

<ul class="nav nav-stacked" style="padding-left:10px">
    <li class="nav list-group-item-heading selected">
        <div class="row">
            <div class="col-sm-1 hidden-xs">
                <!-- ko if: nodes().length > 0 -->
                <!-- ko if: expanded() -->
                <a data-bind="click: changeState" role="button" href="">
                    <i class="glyphicon-minus"></i>
                </a>
                <!-- /ko -->
                <!-- ko ifnot: expanded() -->
                <a data-bind="click: changeState" role="button" href="">
                    <i class="glyphicon-plus"></i>
                </a>
                <!-- /ko -->
                <!-- /ko -->
            </div>
            <div class="col-sm-5">
                <span data-bind="text: title"></span>
            </div>
        </div>
    </li>
    <!-- ko if: nodes().length > 0 && expanded() === true -->
    <!-- ko foreach: nodes -->
    <li>
        <tree-node params="node: $data"></tree-node>
    </li>
    <!-- /ko -->
    <!-- /ko -->
</ul>

 

The tree-node is wrapped in a <ul> just to make use of Bootstrap’s default indentation. If you want you can use any markup. The <div class=”row”> is where things get interesting. We have two columns in the row.

The first column is rendered conditionally. We check if there are elements in the nodes collection. If there are one or more nodes we check the expanded() property to see if we should render a [-] or a [+] using an anchor tag. We also attached the click event of the anchor to the changeState function.

The second column is a span that’s bound to the title property of the tree-node viewmodel.

Finally we need to render the rest of the nodes so again we check if the nodes collection is empty or not. If there are one or more nodes we loop though each node and render a tree-node itself with the current instance of the node object as provided by KO in the $data variable.

The Tree Component

Now that we have seen what the node looks like lets setup the container

The Tree view model (tree.js)

define([
    "knockout",
    "text!./tree.html"], function (ko, treeTemplate) {
    function treeViewModel(params) {
        var self = this;
        self.title = ko.observable('');
        self.nodes = ko.observableArray([]);

        if (params.data) {
            self.title(params.data.title);
            self.nodes().push.apply(self.nodes(), params.data.nodes);
        }
        return self;
    }
    return { viewModel: treeViewModel, template: treeTemplate };
});

As we can see, the viewModel is pretty simple, it has two properties:
title – A string that can be shown on top of the TreeView
nodes – A collection of node objects.

The Tree view (tree.html)

The markup for the Tree component simply binds the nodes collection from the viewModel into a unordered list.

<ul class="nav nav-stacked">
    <li class="nav list-group-item-heading" data-bind="text: title">
    	<!-- ko if: nodes().length > 0 -->
    		<li>
        	<!-- ko foreach: nodes -->
        		<tree-node params="node: $data"></tree-node>
        	<!-- /ko -->
    	<!-- /ko -->
   	</li>
</ul>

Registering the new components

We go back to the app.js file and add our two new components to the ko.components’ list.

app = {
    components: {
        greeter: {
            name: 'greeter',
            template: 'App/components/greeter/greeting'
        },
        tabitem: {
            name: 'tabitem',
            template: 'App/components/tabitem/tabitem'
        },
        tabbedNavigation: {
            name: 'tabbed-navigation',
            template: 'App/components/tabbed-navigation/tabbed-navigation'
        },
        treeNode: {
            name: 'tree-node',
            template: 'App/components/tree-node/tree-node'
        },
        tree: {
            name: 'tree',
            template: 'App/components/tree/tree'
        }
    },
    pages: {
        home: {
            name: 'home',
            template: 'App/pages/home/home'
        },
        docs: {
            name: 'docs',
            template: 'App/pages/docs/docs'
        },
        settings: {
            name: 'settings',
            template: 'App/pages/settings/settings'
        }
    }
}

 

Adding some dummy data

Now that we’ve got the components in place, let’s add some dummy data to bring up the tree. We go back to the docs.js file and update the data source.

self.data =
	{
            title: "Documentation Home",
            nodes: [
                {
                    title: "Node 0",
                    nodes: [
                    {
                        title: "Node 0-1",
                        expanded: false,
                        nodes: [
                            {
                                title: "Node 0-1-0"
                            },
                            {
                                title: "Node 0-1-1"
                            }
                        ]
                    },
                    {
                        title: "Node 0-2"
                    }]
                },
                {
                    title: "Node 1",
                    expanded: false,
                    nodes: [
                    {
                        title: "Node 1-1"
                    },
                    {
                        title: "Node 1-2"
                    }]
                },
                {
                    title: "Node 2"
                }]
        }

Demo Time

Now that we are setup with the new components lets run the app and see how it works.

Navigating to the docs page shows us the following:

image

Notice ‘Node 0-1’ and ‘Node 1’ are not expanded by default but ‘Node 0’ is. This is because these nodes have the expanded property explicitly set to false. By default expanded is assumed true.

So we have a functional Tree layout. However clicking on the nodes don’t do anything because their HREFs are empty and there is no ‘click’ handler to do something with.

Initially I wanted to introduce AmplifyJS in this article itself, but this one is already 1500+ chars and is straining on everyone’s patience. So I am going to split up this article and introduce AmplifyJS in the next article where we’ll see how we can use it to handle events and share data across components.

Tagged , , ,

Knockout Components – Separating Templates from View Model

Part 1: Dipping your feet into Knockout JS Components

This is the second part in my Knockout JS Components series. So far we have made a simple ‘greeter’ component and used it multiple times. But our HTML template was rather simple and hard coded in the ViewModel as a string. That was fine for a small snippet but for involved templates you don’t want to stick an HTML as string. Rather you would like it to be an independent HTML that’s loaded as required.

Today we’ll see how we can separate the HTML and load it dynamically using another library called RequireJS and a plugin for it (RequireJS-Text).

If you want to follow along, you can get the source code branch for the previous article.

Quick intro to RequireJS

RequireJS is a library that is used for ‘Automatic Module Detection’ and loading of JavaScript modules and references. It was developed by James Burke from Mozilla. It is an open source project hosted on Github with a vibrant community that helps maintain it. It also has good documentation that you can refer to for getting started.

This article is not a RequireJS tutorial, instead we’ll jump right in and start using it, I’ll explain the syntax as we go along. If you have not used RequireJS before, well don’t panic, neither have I Smile.

Installing RequireJS and RequireJS-Text plugins

RequireJS has an official Nuget channel so getting the base library is easy to install via Nuget Package Manager Console, simply type in

install-package requirejs

Next we have to get the Text plugin from Github directly. You can either clone the repo from here https://github.com/requirejs/text or just download the JS file.

Once you have the file, add it to your scripts folder. I have started creating sub-folders for each library because it will come in handy later.

image

  • App/boot : This folder will contain scripts that initialize our libraries
  • App/components : This folder will contain all the components we create. Each component in turn has it’s own folder that may contain the ViewModel, HTML template and more.
  • Scripts/*: As mentioned earlier I’ve moved each library into it’s respective sub-folder under the Scripts folder. So Scripts is essentially for all libraries and frameworks that we will use (and not modify), and everything that we build will go under App.

NOTE: This folder structure is completely arbitrary. I ‘feel’ this works, you can use it, you can totally use your own structure. Just remember where your ‘root’ (folder) is Smile.

Now that we are done with the structure of the libs and sources let’s move on and configure RequireJS.

Configuring RequireJS

Under App/boot folder add a new JS file called require.configure.js. The name is, again, not a part of any convention, use what works for you, just refer it in Index.html correctly).

I have configured RequireJS initially as follows

var require = {
baseUrl: “/”,
paths: {
“bootstrap”: “Scripts/bootstrap/bootstrap”,
“jquery”: “Scripts/jquery/jquery-1.9.0”,
“knockout”: “Scripts/knockout/knockout-3.2.0beta.debug”,
“text”: “Scripts/require/text”
},
shim: {
“bootstrap”: {
deps: [“jquery”]
}
}
}

  • I’ve created a require global variable that has three properties, baseUrl, paths and shim.
  • The baseUrl property sets the root location with respect to which all other paths will be considered.
  • The paths property is assigned an object with name value pairs corresponding to the library names and their relative locations. Not the .js is stripped away from the paths.
  • The shim property provides additional dependency information. Here it shows bootstrap is dependent of jquery. The value ‘jquery’ matches the name used in ‘paths’ above.

Updating references

Now that we have configured RequireJS, technically all we need to do is load RequireJS using the configuration and all should be fine!

Well, let’s update the Index.html file to load RequireJS and remove all other Script references. We update the script references as follows:

<!–<script src=”Scripts/jquery-1.9.0.js”></script>
<script src=”Scripts/bootstrap.js”></script>
<script src=”Scripts/knockout-3.2.0beta.debug.js”></script>–>

<script src=”App/boot/require.config.js”></script>
<script src=”Scripts/require/require.js”></script>

Now you are wondering where is the greeting.js gone and how is it going to be loaded? Well there is no magic, we have a couple of more steps to go.

Add App/boot/Startup.js

In the App/boot folder add a new JavaScript file called startup.js. The name is to help us understand the process, it’s not a convention.

Add the following ‘module-definition’. You can read up about RequireJS Modules here.

define([‘jquery’, ‘knockout’, ‘bootstrap’], function ($, ko)
{
ko.components.register(‘greeter’,
{
require: ‘App/components/greeter/greeting’
});
ko.applyBindings();
});

The Startup module says that it is dependent on jQuery, KnockoutJS and BootStrap. Note, it uses the same names that were used in RequireJS configuration above. The function parameters are instances of the dependencies requested in the array, so if you put another input parameter like boots it would have instance of the bootstrap library. We’ll just keep this in mind for now.

Next it declares a function that has jQuery and KO lib references as input parameters.

In the function we ‘register’ our ‘greeter’ component. Note, that we have moved the registration from the greeting.js to startup. Also note instead of specifying the hard-coded template and view model, we are simply configuring a ‘require’ property, that points to the folder where the greeting.js is (without the js).

Well, that’s the startup configuration. Needless to say, as we add more components they will need to be registered here.

Updating our ‘greeter’ component

The first thing we do is add a greeting.html (name same as JavaScript is again not a convention, just easier to map in our heads).

It contains the same markup that we had hardcoded in the template:

image

Update greeting.js

Finally we update the greeting.js component. We comment out all the old code and replace it with the the following:

define([“knockout”, “text!./greeting.html”], function (ko, greeterTemplate) {
function greeterViewModel(params) {
var self = this;
self.greeting = ko.observable(params.name);
self.date = ko.observable(new Date());
return self;
}
return { viewModel: greeterViewModel, template: greeterTemplate };
});

So essentially we have morphed our component to a RequireJS module. Key thing to note here is use of the text plugin to load load the greeting.html. Require does all the work to load the template and stuff it into the greeterTemplate parameter.

Finally we return an object that KO accepts as definition for a module.

One more thing!

We are almost there. Those paying close attention would have noticed that we didn’t put in reference to the startup.js anywhere. How does RequireJS know how to initialize our app and it’s dependencies?

Back to the Index.html, we update the script tag that refers to RequireJS as follows:

<script data-main=”App/boot/startup” src=”Scripts/require/require.js”></script>

The data-main tag, tells Require that the main module to initialize the app is in that JS file. Thus RequireJS knows what to invoke once fully initialized.

Done!

Conclusion

If you run the application now, you’ll see the same old screen we saw in the first part. So what have we done new? Well plenty:

1. Let RequireJS load scripts dynamically.
2. Separated our KO components’ view from it’s model
3. Defined a central place to register all KO components

What we have not done is do more ‘webby’ stuff like putting in links to other possible pages of the app and creating different types of modules for each page and then showing how our app can navigate to those pages as well as load those dependencies on-demand. That’s what we’ll cover in the next part – Routing!

Source Code

The source is on Github as usual (note, after each article I am branching the code out and keeping the master for the latest updates)!

https://github.com/sumitkm/BuildingSpaUsingKO/tree/Part2

Tagged , , , ,

Dipping your feet into KnockoutJS Components

Last week I saw Steve Sanderson’s NDC 2014 talk on how to build large Single Page Applications using KnockoutJS and other tooling. It struck a chord because SPA is something that I am dealing with right now and really wanted to get neck deep into how to use KO and other tools to build one ‘correctly’.

I have tried with AngularJS in the past and you may have read my multipart series on Devcurry. While I have nothing against AngularJS I still find myself more inclined towards using KO more than anything else. Don’t read too much into it, it’s just me, I like KO!

Anyway, Steve’s talk is very deep and he rightly says, you feel like you are hanging on to a race car when sitting through his talk. So I’ve decided to really slow things down and take it one small bit at a time to see how we can use the latest and greatest version of KO (3.2.0 Beta) to build a front-end framework.

Today we’ll look at something new that’s not available in the release version of KO 3.1 as of date – Components.

Components allow you to build HTML+JS snippets that can be reused just like old server side controls of yester-years or like Directives in AngularJS. They actually mimic an upcoming web standard called Web Components.

Steve also used a set of tooling that I am unfamiliar with, so I’ll try to map stuff he did to how I would do if I were using Visual Studio (as far as possible). Lets get started.

Getting Knockout 3.2Beta

As soon as 3.2 goes live it will be available for use via Nuget and the Nuget Package Manager from inside Visual Studio. Today you can download the build from the Releases page on KO’s Github repo – https://github.com/knockout/knockout/releases

If you are a JS ninja you can get the entire library source and build it using NPM and Grunt.

Starting with a clean slate

I’ll start with an empty Web Project in Visual Studio 2013.

Screenshot 2014-07-18 20.55.33

Screenshot 2014-07-18 20.55.57

Screenshot 2014-07-18 20.56.28

As you can see, we get a really really empty template. If you run this in VS, you will get an error saying you don’t have directory browsing permissions.

The Home Page

Since our project is all clean, let’s first install Bootstrap that we’ll use for styling. In the PM console type:

install-package bootstrap

This gives us the following folder structure, where Content has the StyleSheets and Scripts has the JavaScripts required.

image

Now add a new HTML page to the root of the project, call it Index.html (or home.html)

image

Thanks to Visual Studio I have forgotten how to setup a basic startup page using Bootstrap. Serves me right, that, I have to scratch my head and wonder ‘now what’:

image

After ‘considerable’ struggle Winking smile, I update the HTML to include Bootstrap styling and jQuery references.

<!DOCTYPE html>
<html xmlns=”
http://www.w3.org/1999/xhtml”>
<head>
<title>Dipping your feet into KnockoutJS Components</title>
<link href=”Content/bootstrap.css” rel=”stylesheet” />
<link href=”Content/bootstrap-theme.css” rel=”stylesheet” />
</head>
<body>
<div class=”navbar navbar-inverse navbar-fixed-top”>
<div class=”container”>
<div class=”navbar-header”>
<button type=”button” class=”navbar-toggle” data-toggle=”collapse” data-target=”.navbar-collapse”>
<span class=”icon-bar”></span>
<span class=”icon-bar”></span>
<span class=”icon-bar”></span>
</button>
<a class=”navbar-brand” href=”/”>KO Components</a>
</div>
</div>
</div>
<div class=”container body-content” style=”padding-top:50px”>
<h2>Dipping your feet into KnockoutJS Components</h2>
<hr />
</div>
<footer class=”navbar navbar-fixed-bottom”>
<p>&copy; 2014 – Still Learning </p>
</footer>
<script src=”Scripts/jquery-1.9.0.js”></script>
<script src=”Scripts/bootstrap.js”></script>
</body>
</html>

Now if I run this in Visual Studio we’ll get the following page:

image

Let’s see some KO!

I am assuming you have downloaded the KO library as instructed above, so add it to the scripts folder and add a reference to it in our HTML file.

image

Now our App is going to be all HTML and JavaScript and I don’t see the need for CSHTML files at the moment. So let’s create an App folder in the application to consolidate ‘our stuff’.

We add a folder called App and add a js file called greeting.js to it.

In this file we add a simple view model with two properties greeting and date. For now we’ll hardcode the greeting to a standard “Hello World”.

var viewModel = {
greeting: ko.observable(“Hello world!”),
date: ko.observable(new Date())
};

$(function () {
ko.applyBindings(viewModel);
});

Finally we add reference to this script in our Index.html and add a couple of spans to show our greeting.


<div class=”container body-content” style=”padding-top:50px”>
<h2>Dipping your feet into KnockoutJS Components</h2>
<hr />
<div class=”container-fluid”>
<div> Hello <span data-bind=”text: greeting”></span></div>
<div> It is <span data-bind=”text: date”></span></div>
</div>
</div>

<script src=”Scripts/jquery-1.9.0.js”></script>
<script src=”Scripts/bootstrap.js”></script>
<script src=”Scripts/knockout-3.2.0beta.debug.js”></script>
<script src=”App/greeting.js”></script>

Refresh the Index.html on your browser and you should see something similar:

image

Now if you are wondering what’s so great about this, it’s exactly how KO works, you are right!

Where are my komponentz?!?

Well, let’s say we want to convert our ‘Greeting’ HTML + View Model into a reusable component that can be applied anywhere we want? Hello KO components!!!

Change the greeting.js to the following:

ko.components.register(‘greeter’, {
    // The register method needs a config object with
// 2 properties

template: // template is a string with the HTML template to apply
// Here we have directly hardcoded the string we originally
// had in index.html
        “<div class=’container-fluid’>” +
“<div> Hello <span data-bind=’text: greeting’></span></div>” +
“<div> It is <span data-bind=’text: date’></span></div>” +
“</div>”,
viewModel: function(){ // viewModel that can be an object or function
        greeting = ko.observable(“Hello world!”);
date = ko.observable(new Date());
}
});

$(function () {
ko.applyBindings();
//We have removed the explicit reference to the viewModel
});

As we can see above, we have used the new ko.components API to declare a new component called ‘greeter’ (first parameter).

The component needs two parts to be initialized properly, one is the HTML template (in the template) property, and other is the View Model in the viewModel property of the initialization object.

As of now, we have hardcoded the HTML that we had added to our Index.html as the template. Later on, we’ll see how to get it from a separate file etc.

We have used the constructor method technique to define the view Model with the same two properties we had earlier. Why? We’ll see in a minute.

Now that our ‘component’ is ready how do we ‘use’ it? Simple, the name of the component is also the name of the tag, so switch back to Index.html and update the body as follows:

<div class=”container body-content” style=”padding-top:50px”>
<h2>Dipping your feet into KnockoutJS Components</h2>
<hr />
<greeter></greeter>
</div>

Refresh your Index.html in browser and you’ll see things still work as they were! Congratulations, you’ve just built your first component.

Passing Parameters to components

Well, the hard coded Greeting is not quite flexible, what if we wanted to pass in a message to the component?

It is very simple to pass parameters into a component. Add a ‘params’ attribute to the tag and pass in name: value pairs. You will get it as an object in the constructor of your viewModel and you can use it accordingly.

So change the component setup in greeting.js as follows

ko.components.register(‘greeter’, {
// The register method needs a config object with
// 2 properties
template: // template is a string with the HTML template to apply
// Here we have directly hardcoded the string we originally
// had in index.html
“<div class=’container-fluid’>” +
“<div> Hello <span data-bind=’text: greeting’></span></div>” +
“<div> It is <span data-bind=’text: date’></span></div>” +
“</div>”,
viewModel: function(
params){ // viewModel that can be an object or function
greeting = ko.observable(params.name);
date = ko.observable(new Date());
}
});

Next update the Index.html to pass parameters to our component.

<greeter params = ‘name: ” Sumit!”‘></greeter>

Refresh the page and you see the following:

image

Copy paste multiple <greeter … /> instances and pass different names to them

<greeter params=’name: ” Sumit!”‘></greeter>
<greeter params=’name: ” Optimus!”‘></greeter>
<greeter params=’name: ” Bumblebee!”‘></greeter>

Refresh Index again and things work as expected!

image

Congratulations, you have built your first KO component!

This concludes the first 15 (approx.) minutes of Steve’s talk. Lots of more stuff is in store. As I explore these things, I’ll continue to share what I learn, first of which will be using RequireJS and Automatic Module Detection. So watch out for the next part in the series.

Source code on my Github repo here – https://github.com/sumitkm/BuildingSpaUsingKO/tree/Part1

Tagged , , , ,
%d bloggers like this: