ConboJS 4

ConboJS

ConboJS is the best JavaScript MVx framework you've never heard of.

It is a lightweight MVx application framework for JavaScript for use with modern browsers which enables developers a take a structured, decoupled, class based approach to application development, in a way that should be familiar to anyone with experience of languages like ActionScript/Flex, C#/XAML or Java.

Features include extendible classes, event bus, dependency injection, data binding, command pattern, pseudo-interfaces and an easy to use event model with scoped event handling, plus simple view state management and support for ES2015 syntax.

ConboJS requires no special IDEs, compilers or transpilers, it just makes regular JavaScript nicer. Extensive TypeScript definitions are also included to enable code completion from the moment it's installed.

While ConboJS provides everything you need to create single page applications (SPA) and self-contained modules, like widgets and media players, it can also offer a great base for server-side Node.js applications or a fantastic way to add models, controller and services to third party view frameworks.

ConboJS can be added to your project as a global, an AMD or CommonJS module, or using ES2015/TypeScript import syntax.

Browser support

ConboJS targets the two most recent major releases of Firefox, Chrome (desktop and Android), Safari (desktop and iOS) and Edge, plus Internet Explorer 11 (for now).

Modular namespace declarations

ConboJS brings the familiar concepts of packages and imports to JavaScript in the form of modular namespaces, optimised to work as an alternative to the commonly used minification pattern, for example:

// Utils.js
conbo('com.example.utils', console, function(console)
{
    var utils = this;

    utils.doSomething = function(value)
    {
        console.log(value);
    };
});

// Constants.js
conbo('com.example.app', function()
{
    var app = this;

    app.BEST_FRAMEWORK = 'ConboJS';
    app.SMILE = ':-)';
});

// Main.js
conbo('com.example.app', window, document, navigator, function(window, document, navigator, undefined)
{
    // Import data from other namespaces
    var app = this;
    var utils = conbo('com.example.utils');

    // Your app code goes here

    utils.doSomething(app.BEST_FRAMEWORK+' makes me '+app.SMILE);
});

Working with ES2015, TypeScript, AMD and CommonJS modules

If you're using ES2015, TypeScript, AMD or CommonJS modules, it's easy to import all of your Application and View classes into your namespace to take advantage of ConboJS features like auto instantiation and data binding:

// ES2015 & TypeScript Decorator

import {Application, Viewable} from 'conbo';

@Viewable('com.example.app')
export class FooApp extends Application { ... }
// ES2015 & TypeScript

import * as conbo from 'conbo';
import FooApp from './FooApp';
import BarView from './BarView';

conbo('com.example.app').import({ FooApp, BarView });
// AMD

define(['conbo', 'FooApp', 'BarView'], function(conbo, FooApp, BarView) 
{
    conbo('com.example.app').import({ FooApp, BarView });
};
// CommonJS

var conbo = require('conbo');
var FooApp = require('./FooApp');
var BarView = require('./BarView');

conbo('com.example.app').import({ FooApp, BarView });

Extendible classes

There's no messing about with prototypes in ConboJS, all of your classes simply extend from another, for example:

ES5

var MyClass = conbo.Class.extend
({
    initialize: function()
    {
        console.log('Welcome to my class!');
    }
});

ES2015 / TypeScript

class MyClass extends conbo.Class
{
    initialize()
    {
        console.log('Welcome to my class!');
    }
}

Interfaces

In ConboJS, an interface is a code snippet, in the form of a JavaScript Object, that you can implement and and test against. They come in 2 forms, strict and partial.

A strict interface is intened for use in a similar way to languages such as Java or ActionScript, enabling you to specify the class of each property (or use undefined for any) and then perform a strict comparison against an object or class instance:

var IPerson = { name: String, age: Number };
var person = { name: "Foo", age: 69 };

conbo.is(person, IPerson); // true

Alternatively, to enable developers to add and test for functionality that is not included in the prototype chain, interfaces in ConboJS can contain default functionality, which will be used if the class has not implemented the interface in full, for example:

var ILogger = { logSomething: function() { conbo.log('Something!'); } };
var Logger = conbo.Class.extend().implement(ILogger);
var logger = new Logger();

conbo.is(logger, ILogger, false); // true

logger.logSomething(); // Outputs: "Something!"

In this example, a shallow comparison is used, verifying that the expected properties are present, but ignoring their values. Pre-populating a method with conbo.notImplemented will ensure that it throws an error when called but not implemented in a class instance.

Decoupling & data binding

One of ConboJS's core aims is to enable developers to create highly decoupled, testable code.

To this end, the framework's ever expanding data binding features enable you to separate your HTML from your JavaScript, removing the need for direct references between the them using cb-* and custom, developer defined, attributes to automatically bind properties and events in the DOM to your View classes.

In addition, any existing HTML attribute can be bound to a property or function simply by prefixing it with cb-, for example cb-title="myTitle" or cb-onclick="myClickHandler".

For example:

In your View class

class MyView extends conbo.View
{
    declarations()
    {
        this.myButtonLabel = 'Click me!';
    }

    myClickHandler(event)
    {
        alert('You clicked a button!');
    }
}

In your HTML

Almost all bindings can be made using cb-* attributes:

<div cb-view="MyView">
    <button cb-onclick="myClickHandler" cb-html="myButtonLabel"></button>
</div>

But if you prefer to use custom tag names and/or curly brackets, simply use a hyphenated, lower case version of your Application, View or Glimpse class name as the tag and put your text inside {{ and }}:

<my-view>
    <button cb-onclick="myClickHandler">{{myButtonLabel}}</button>
</my-view>

Consistent, scoped events

With ConboJS you don't have to remember how many arguments each event handler should have or the order they're supposed to be in, because there's a single, consistent DOM-like event model that offers predictable results, even enabling you to set the value to use as this when the callback is executed.

All events fired by the framework are conbo.ConboEvent event objects, and you can easily create events of your own by using or extending the conbo.Event class, for example:

foo.addEventListener("myEvent", this.myFunction, this);
foo.dispatchEvent(new conbo.Event("myEvent"));

Decorators

ConboJS provides a number of class (ES2015 and TypeScript) and property (TypeScript only) decorators to resolve transpilation issues, simplify, enhance or simply provide syntactical sugar while developing applications:

import { Application, Bindable, Inject, Viewable } from 'conbo';

// Add a class to specified ConboJS namespace to enable auto-instantiation (second parameter only required if minifying)
@Viewable('com.example.app', 'MyApp')
class MyApp extends Application
{
    // Mark a property as injectable so you don't have to set it to undefined in declarations (TypeScript only)
    @Inject
    public myService:MyService;

    // Mark a property as bindable so you don't have to set it in declarations (TypeScript only)
    @Bindable
    public myValue:string = 'Hello, World!';
}

Naming conventions

The naming conventions used by ConboJS should be familiar to anyone who uses JavaScript or ActionScript on a regular basis:

  • ClassNames are camel case with an initial capital letter
  • IInterfaceNames are camel case with an initial capital letter, prefixed with a capital I
  • publicPropertyAndMethodNames are camel case, starting with a lower case letter
  • _privatePropertyAndMethodNames are user defined properties and methods used within the current class only, prefixed with an underscore
  • __internalPropertyAndMethodNames are prefixed with a double underscore to indicate that they are for internal use by the framework only
  • CONSTANT_VALUES are all upper case, with words separated using an underscore
  • @Decorators are camel case with an initial capital letter, following the naming convention used for similar metadata and annotations in other languages

Wherever possible, file names should match their contents, for example ClassName.js, methodName.js or IInterfaceName.ts.

Builds

conbo.js (17KB minified+gzipped): Includes everything you need to build dynamic web application, including HttpService, RemoteHash and RemoteList classes for working with web services, and History and Router classes for browser integration.

Builds are created using Grunt, which requires Node.js; all required modules can be installed by running npm install from the command line in the project folder.

You can create a new build from the CLI using grunt. Use grunt watch, or run watch.cmd (Windows) or ./watch.sh (Mac, Linux) to auto-build as you edit.

License

ConboJS is released under MIT license.

GitHub https://github.com/mesmotronic/conbo

Docs https://conbo.mesmotronic.com/