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 letterIInterfaceNames
are camel case with an initial capital letter, prefixed with a capital IpublicPropertyAndMethodNames
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 onlyCONSTANT_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/