Getting Started

Note

This documentation assumes a reasonable understanding of javascript, html, and css. If you are brand new to frontend development you might want to learn the language and basics of html to begin with...it's worth it, and will help immensely regardless of what framework you end up using.

By far, the fastest route to get Footwork running is including it via the CDN with a simple script tag:

<script src="https://unpkg.com/footwork/dist/footwork.js"></script>

Thats all you need, toss that script tag into an HTML file and get started right away. For a super quick demo/example, take a look at the Hello World jsFiddle.

Installing

For a more defined project you may want to install/configure it as a dependency. Footwork is served up via the two most common frontend packagers, bower and npm:

The Basics

Footwork is largely declaratively-based, logic is modularized within view models bound against those declarations. Using two-way bindings and simple constructor functions you can build rich user interfaces.

Views and View Models

An example view:

<viewModel module="Person" params="name: 'Jill'">
  My name is <span data-bind="text: name"></span>

  <button data-bind="click: sayHello">Say Hello</button>
  <span data-bind="text: response"></span>
</viewModel>

In the example above we declare a single viewModel Person with some markup inside of it. When the application is started Footwork will iterate over the view, find the viewModel declaration, match that up with the registered viewModel, and then instantiate and bind it.

An example view model which we might bind against the view above:

fw.viewModel.register('Person', function (params) {
  var self = fw.viewModel.boot(this, {
    namespace: 'Person'
  });

  self.name = params.name;
  self.response = fw.observable();
  self.sayHello = function () {
    self.$namespace.publish('hello', self);
  };

  self.$namespace.subscribe('hello', function (from) {
    if (from.name !== self.name) {
        self.response('Hello ' + from.name);
        } else {
      self.response(undefined);
    }
  });
});

// Start the application
fw.start();

Tip

There are several different types of view models and ways to create them. Some have additional features or advantages depending on your needed use, see Creating View Models

Starting The Application

There are two ways of kickstarting your application:

  • fw.start

    Using this method you can simply tell Footwork to start processing the DOM. Instantiation of your application then depends on the declarative viewModels, dataModels, routers, components, etc that you define in the view.

    fw.start();
    

    You can optionally pass it a root-node you want it to begin processing on (by default it assumes document.body):

    fw.start(document.getElementById('my-app'));
    

    Once fw.start has been called all descendant declarations to the root node will be processed. This means any nested viewModels/components/etc will be instantiated and bound per their registrations/configurations/etc.

  • fw.applyBindings

    This method allows you to instantiate and bind a specific view model when binding to the view.

    fw.applyBindings(new MyViewModel());
    

    You can optionally pass it a root-node you want it to begin processing on (by default it assumes document.body):

    fw.applyBindings(new MyViewModel(), document.getElementById('my-app'));
    

    Note

    Once fw.applyBindings has been called all descendant declarations to the root node will be processed as well. This means any nested viewModels/components/etc will be instantiated and bound per their registrations/configurations/etc.

Browser Support and Polyfills

Footwork is an ES5-based library and utilizes a few of the newer ES6/HTML5 features such as:

This means that it needs at least IE9 or later, and might require that you load some polyfills (if any of the aforementioned are not natively supported by a browser you need to support).

If you require polyfills, then the following ones are recommended (they are the ones run with the tests):

Depending on your preferred package manager, they are available via bower and npm:

  • Install via bower

    bower install es6-promise fetch html5-history-api --save
    
  • Install via npm

    npm install es6-promise whatwg-fetch html5-history-api --save
    

    Note

    whatwg-fetch on npm is the same thing as fetch from bower, it is just released under a different name.

These polyfills need to be loaded prior to Footwork. Here is an example using normal script tags (adjust accordingly for your preferred script loader):

<html>
  <head>
    <title>My Awesome Application</title>

    <!-- (optional) Need this for animation support -->
    <link rel="stylesheet" href="bower_components/footwork/dist/footwork.css">
  </head>

  <body>
    <!-- ... application markup goes here ... -->

    <!-- polyfills -->
    <script src="bower_components/es6-promise/es6-promise.auto.js"></script>
    <script src="bower_components/fetch/fetch.js"></script>
    <script src="bower_components/html5-history-api/history.js"></script>

    <!-- Footwork framework -->
    <script src="bower_components/footwork/dist/footwork.js"></script>

    <!-- Your code -->
    <script src="scripts/my-script.js"></script>
  </body>
</html>

Dynamic Loading of Assets

One of Footworks nice features is the ability to load your components/viewModels/dataModels/routers at runtime when needed.

In order To use those features you will need to ensure RequireJS is installed and linked. Footwork also requires the supporting text plugin to load your template/html files dynamically as well.

You can install these dependencies via bower and npm:

  • Install via bower

    bower install requirejs requirejs-text --save
    
  • Install via npm

    npm install requirejs requirejs-text --save
    

And to load these you would do something like so:

<html>
  <head>
    <title>My Awesome Application</title>

    <!-- (optional) Need this for animation support -->
    <link rel="stylesheet" href="bower_components/footwork/dist/footwork.css">
  </head>

  <body>
    <!-- ... application markup goes here ... -->

    <!-- polyfills -->
    <script src="bower_components/es6-promise/es6-promise.auto.js"></script>
    <script src="bower_components/fetch/fetch.js"></script>
    <script src="bower_components/html5-history-api/history.js"></script>

    <!-- RequireJS -->
    <script src="bower_components/requirejs/require.js"></script>
    <script src="bower_components/text/text.js"></script>

    <!-- Footwork framework -->
    <script src="bower_components/footwork/dist/footwork.js"></script>

    <!-- Your code -->
    <script src="scripts/my-script.js"></script>
  </body>
</html>

The above example shows a lot of ugly script tags...and whats worse is that they may cause significant lag when loading these dependencies sequentially.

It is important to remember that if you are using RequireJS you will typically load everything via RequireJS itself. This allows you to get rid of most of the script tags and allows you to optimize your application into a single file when you are ready to deploy into a production environment.

A brief example of this is shown here:

  • The main HTML file:

    <html>
      <head>
        <title>My Awesome Application</title>
    
        <!-- (optional) Need this for animation support -->
        <link rel="stylesheet" href="bower_components/footwork/dist/footwork.css">
      </head>
    
      <body>
        <!-- ... application markup goes here ... -->
    
        <script src="bower_components/requirejs/require.js" data-main="/path/to/main/script"></script>
      </body>
    </html>
    
  • The main script.js file:

    require.config({
      paths: {
        "text": "/bower_components/text/text",
        "footwork": "/bower_components/footwork/dist/footwork",
        "es6-promise": "/bower_components/es6-promise/es6-promise",
        "fetch": "/bower_components/fetch/fetch",
        "history": "/bower_components/html5-history-api/history"
      }
    });
    
    require(['footwork', 'es6-promise', 'fetch', 'history'],
      function (fw, promise) {
        // Promise polyfill requires an explicit call (fetch and history do not)
        promise.polyfill();
    
        // Your code goes here
        fw.start();
      }
    );
    

Much better, RequireJS will take care of the heavy lifting for us now. And whats even better, with a bit more work we can utilize the RequireJS optimizer and compile our code down into a single file (including templates).

Note

For more detailed information see: How to get started with RequireJS.