A MVVM Project
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
demos
dist
grunt
js
.gitignore
LICENSE
README.md
RELEASE.md
gruntFile.js
package.json

README.md

uxJS

A simpler MVVM.

uxJS is a simple and eloquent JavaScript framework that was created to make it easy to deliver great user experiences. We wanted uxJS to be simple, so we made it simple. To accomplish this, we started with the MVVM design pattern and implemented it in the simplest way we could imagine. uxJS consists of controllers and plugins...that it! Controllers handle model updates. Plugins handle applying model data to the view.

Installation

npm install uxjs

Fast Start

The fastest possible way to get started building stuff with uxJS.

<input type="text" data-ux-plugin="bind" data-model="form.myInputField">
<div data-ux-plugin="bind" data-model="form.myInputField"></div>
<script type="javascript/text">
    //initialize uxJS
    ux();
</script>

Quick Start

Define a controller:

ux.controller('myController', function (controller) {
    //my controller logic
});

Bind it to the view:

<div class="myView" data-ux="myController">
    <!-- My View Stuff -->
</div>

Use built in plugins:

<div class="myView" data-ux="myController">
    <input type="text" data-ux-plugin="bind"
        data-model="form.myInputField">
    <div data-ux-plugin="bind" data-model="form.myInputField"></div>
</div>

Why MVVM

MVVM is a modern interpretation of your traditional MVC, but with a twist. In a traditional MVC framework, there is typically a middle layer that sits between the Controller and the View called the View Model. In an MVC, the View Model is an object representation of the view that contains dynamic data that will be consumed by the View. This allows you to decouple view logic and business logic. In a traditional MVC, this is known as a one-way binding. MVVM builds on top of the MVC pattern and provides a two-way binding. Which not only allows the Controller to communicate with the View, but the View to communicate to the Controller as well through the View Model, and it is all done automatically! So whenever, say you make a change to the value in the View Model from the Controller, the View will automatically be refreshed to reflect the change. If you make a change to a value in a field in the View, the value in the Controller will automatically be updated. All of this coordination happens through the View Model.

uxJS vs. Angular

uxJS and Angular are very similar in the implementation of MVVM. When we originally started planning the development of uxJS, we tried very hard not to be Angular. Angular is Awesome! We did not want to replicate it, there is no need. Over the past several years Angular has set the standard in the implementation of the MVVM design pattern in JavaScript. So we decided to make our framework for a different need than what Angular solves. Angular is very powerful and provides a good foundation for enterprise level application development. uxJS, although just as powerful, provides a bare bones foundation and an easier implementation for your projects.

uxJS differs from Angular in many distinct ways:

  • No Dependency Injection Although this is one of the best Design Patterns for developing very large enterprise applications, we decided not to include it within uxJS because we are not targeting large enterprise application development.
  • Clearer Separation of View and Controller The Controller in Angular is merely a definition and not so an object. So you cannot add methods to it. Instead, you are forced to add methods to your View Model. Your View Model is then responsible for both State and Business Logic. uxJS has solved this problem by providing a Controller object directly in the controller definition.
  • Easier Library Extensions Angular has made a way to extend the library through Service Providers and Dependency Injection. Extending Angular can become very difficult to understand because of the number of options they provide as you extend the library. You can extend Angular with a Service, Provider, Factory, Filter, etc. There are a number of blog posts out there to try and help you define which to use and why. uxJS provides you direct access to extend the three primary components with one configuration interface.
  • The Plugin Angular provides you with the ability to "extend the functionality of HTML" through the use of Directives. Directives, however, again are very difficult to implement. There are so many possible configurations, it becomes difficult to understand which configuration to use and when. uxJS Plugins are very similar to Angular Directives except there is only one possible configuration.

uxJS Overview

uxJS is an MVVM library built around two easy to understand concepts. The Controller and the Plugin. The Controller is where your data exists. Plugins are designed to combine the data in your Controller with the HTML element your plugin is targeting.

The Controller

The Controller is an object which is designed to provide you with all of the functionality you need to manage your model. To start using uxJS, you must first register a controller by binding it to an HTML element.

<div class="myCoolAppContainer" data-ux="myController"></div>

In the example above, the property data-ux is how a controller that gets bound to an HTML element. The value myController given to the property data-ux is how we identify the controller. We call this value the Controller ID. Controller IDs must be unique and cannot be reused within the same HTML document.

After you bind the controller to the view, you may now retrieve the controller object.

var myController = ux.controller('myController');

Initializing A Controller

When defining a Controller's functionality, you can do it in two ways. You can either obtain the controller object like in the example above. Or you can initialize it by passing in an initHandler function.

ux.controller('myController', function (controller) {
    //define functionality here
});

Controller Inheritance

Each Controller created inherits its root properties from a base abstract controller object. For the following example, we will take a look at a single controller instance:

<div data-ux="controller1"></div>

In the example above the controller we have defined inherits from the abstractController:

controller1 inherits from abstractController

Controllers can also be nested. Below is an example of how nested controllers inherit from one another:

<div data-ux="parentController">
	<div data-ux="childController"></div>
</div>

In an instance where you have controllers that are nested, child controllers will inherit the properties of a parent controller:

childController inherits from parentController
parentController inherits from abstractController

Plugins

uxJS plugins are designed to translate data from controllers to views and bind view events back to the controllers. Plugins are registered to the HTML elements using the attribute data-ux-plugin. Very similar to the class attribute, the data-ux-plugin attribute can register multiple plugins to the same HTML element. In the example below, the data-ux-plugin attribute contains bind and mustache. Both of these plugins will be applied to the HTML element.

<div class="myContainer" data-ux-plugin="bind mustache"></div>

Included Plugins

  • Bind The purpose of the Bind plugin is to bind information between controllers and views.

    • Supported attributes:
      • data-model="path.to.data" - Takes the value "path.to.data" and maps it to data in the controller. If data-model is bound to a form input element of any time, the Bind plugin will monitor the value for the field and update the controller data it is mapped to and then call controller.apply() to apply the updates to the rest of the view.

          <input type="text" data-ux-plugin="bind" data-model="path.to.data">
        
      • data-{eventname}="path.to.function" - Registers an event listener to monitor for "{eventname}" events and maps the value for "path.to.function" as the event listener callback.

          <div data-ux-plugin="bind" data-{eventname}="path.to.function"></div>
        
          /**
           * The eventNameCallback function
           * @param  {Object} event The event object provided by addEventListener callback
           * @param  {HTMLElement} element The element the plugin is bound to.
           * @param  {Object} controller The controller object the element belongs to.
           * @return {void}
           */
          function eventNameCallback(event, element, controller) {
              //callback
          }
          controller.path.to.function = eventNameCallback;
        
  • Mustache The purpose of the Mustache plugin is to bind data in the controller to a mustache template. Note: https://github.com/janl/mustache.js is required to use this plugin.

    • Supported attributes:
      • data-model="path.to.data" - Takes the value "path.to.data" and maps it to the data in the controller for the mustache template to use.
      • data-mustache="path.to.mustacheTemplate" - Takes the value for "path.to.mustacheTemplate" and uses the mapped controller value as the mustache template. Note: The mustacheTemplate defined in the constroller is expected to be the string mustache you intend to use for the mustacheTemplate.

Creating uxJS Plugins

Plugins are simple to create. When you register a plugin, you have two callbacks you may define. A callback for when the plugin is first initialized and a callback for when a controller's apply function is called ie: controller.apply();

/**
 * The function that is called when the plugin is first initialized.
 * @param  {HTMLElement} element The element the plugin is bound to.
 * @param  {Object} controller The controller object the element belongs to.
 * @return {void}
 */
function initFn(element, controller) {
    //init plugin
}

/**
 * The function that is called when the controller.apply() method is called.
 * @param  {HTMLElement} element The element the plugin is bound to.
 * @param  {Object} controller The controller object the element belongs to.
 * @return {void}
 */
function applyFn(element, controller) {
    //apply controller changes
}

ux.plugin('pluginName', initFn, applyFn);

Now just bind the plugin to your DOM:

<div data-ux-plugin="pluginName"></div>

Applying Data Changes

When changes are made to the model stored on a controller, it would be nice to have the view update itself to reflect those changes. controller.apply() is meant to do just that. Whenever a change is made to the controller, the change should be applied by calling the apply method on the controller. As a note, when you call the apply method on a controller, the changes will only be applied to the part of the HTMLElement that is bound to that controller. If you have a sibling element you also need to update, then you must call ux.apply(). This will find all controllers and call their apply() methods.

ux(fn)

In some cases, you may find that you want to use uxJS Plugins without having to define a controller. In the "Fast Start" section, we created an example where we did not bind a controller to the view, but were still able to use uxJS plugins. In that example, we executed ux() as a function to initialize the view. This is a special method, we created, that will try to find the outer most controller and use that to process plugins. If it cannot find a controller defined in the view, it will bind a controller called "ux" to the body element of your HTML DOM, and then process plugins using the "ux" controller it just bound to the view.