winjsrocks
v0.6.15
Published
WinJS-based MVVM architecture for cross-platform apps
Downloads
40
Maintainers
Readme
An ES6 MVVM framework optimized for WinJS
Built to support amazing app developers targetting:
- Windows 10
- Windows 8/8.1
- XboxOne
- Windows Phone 10
- Android
- iOS
- FireTV
- Playstation 3/4/tv
- Web
Featuring the Commandments of great code:
- View, ViewModel, Model coordination
- Message based Communication
- Navigation Framework
- Command Pattern
- Event/Dispatch Pattern
- Providers
- Services
- Plugins
- Domain model indexing with Lokijs
- Why WinJS?
- Sample Apps
- Project Setup
- The Application Object
- Building Views
- Working with Plugins
- Building Providers
See Technical Documentation for APIs
Why WinJS?
WinJS is a visual framwork that really considers a few important factors that other frameworks fall short on:
- Input Modalities - Given a world of accelerating device fragmentation, people interact with UI's from a variety of input patterns. WinJS is built with the exectation that elements should automatically support Mouse, Keyboard, Gesture, Remote and Controller interaction patterns seamlessly. This support is not a weak accessibility-focused implementation, but one that actually attempts to provide core persona support for each input type.
- Command/Property Binding - WinJS supports a variety of markup-driven binding styles that allows developers to avoid common pitfalls with Javascript memory management. This allows clear seperation of concerns when diagnosing memory performant creative View development. Many frameworks don't do this and seem to encourage Views, Models and Controllers to all be directly bound to the DOM, leaving the advanced topic of Javascript Memory Profiling
- Dynamic View Resolve - Views can be either packaged in the app, fetched from a CDN or remotely generated via Web API. This allows application developers to use the tools that work for creative View development while still offering the advantages of dynamic server-side generation of artifacts.
- Localization - Views can use localized strings to bind declaratively and developers can support a wide range of localization sets
WinJS is a future-facing UI Framework that really is designed to allow your Information Architecture to adapt to Desktop, Console, Mobile and Web without having to frankenstein your development architecture.
Sample Apps
- (In Development) WinJSRocks-Kaneda - ES6 cross-platform
- (In Development) WinJSrocks-Skywarp - ES6 Shallow template
Project Setup
Install Packages
Install the framework and the current WinJS bits:
npm install winjs --save
npm install winjsrocks --save
Note: Though WinJSRocks is tested against a specific version (latest verified) of the WinJS library, the distribution does not package these bits. So, "Bring your own WinJS" and "Millage may vary" if not the same version tested with WinJSRocks.
Optionally, install the maintained pluglin library WinJSRocks-Extras. This project includes cool plugins like IndexDB Storage, JQuery Adapters and shows really good examples of how to write your own application plugins.
npm install winsrocks-extras --save
Load WinJS
Application developers are required to pre-load WinJS
before attempting run of the library.
var WinJS = require('winjs');
var WinJSRocks = require('winjsrocks');
Create Application Instance
Create a version of the Application object:
var WinJSRocks = require('winjsrocks');
var app = new WinJSRocks.Application();
Once the application is instatiated, access is available as a singleton:
var app = WinJSRocks.Application.Instance;
The Application Object
The Application object has three important phases that need to be implemented for an application:
- configure - Allows plugin registration and generally is the best place to build up the container with Services and Providers.
var app = new WinJSRocks.Application();
var CustomProvider = require('providers/customProvider');
var CustomService = require('services/customService');
var CustomPlugin = require('plugins/customPlugin');
app.configure({
plugins: [
CustomPlugin
]
}, function(err){
if(err)
console.error(err);
// Load up the container with goodies
app.container.registerProvider("customProvider", CustomProvider);
app.container.registerService("customService", CustomService);
});
app.load({},
function(err){
if(err)
console.error(err);
// Application has loaded and ready for use
// Suggestion: Inject the Message Service and navigate to a view
// see [Building Views](#building-views)
var MessageService = app.container.getService('message');
MessageService.send("navigateToMessage", {
viewKey: "landing"
});
});
- unload - Stops Services and unloads all known components.
app.unload({},
function(err){
if(err)
console.error(err);
// All components have been notified of the shutdown
});
Building Views
The WinJSRocks MVVM framework provides the View/ViewModel sequencing to enable the fastest possible asynchronous load behavior for application developers.
This is achieved by using a NavigationService
to instantiate ViewModels at the same time as their respective View, kicking off the ViewModel's load life-cycle before WinJS starts up the native page life-cycle.
Then, the View is allowed to render while the native WinJS Page ready
method is suspended until the ViewModel has fully loaded initially.
In result, application developers can rely on the native WinJS Page ready
method to fire after the View Template is fully rendered, the ViewModel has loaded up principle Models and the initial *.processAll()
phases have completed on the visual tree.
ViewKey Registration
The framework provides the WinJSRocks.Application.Instance.builder
API to aid in view/item registrations around a single ViewKey
.
Example of registering views:
var app = new WinJSRocks.Application();
var viewKeys = ['splash', 'landing', 'items', 'article', 'contributor'];
viewKeys.forEach(function(viewKey) {
app.builder.registerView(
viewKey, // framework ViewKey
require('./views/' + viewKey + '/view'),
require('./views/' + viewKey + '/view-model'),
'views/' + viewKey + '/view.html');
});
Note: View registration should occur before calling
load
on the Application object
View
Building a view is a combination of a Template, Component and ViewKey. The Template/Component will be associated with a ViewKey and will be joined together at runtime upon navigation to a view.
Template
Templates are expected to be compliant HTML mark-up files and their defintion is based on the WinJS navigation framework's formal definition of an WinJS.UI.Pages.PageControl
Generally, anything goes here and this is where most teams will match the UI creative pattern to each target User-Experience.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Landing Screen</title>
</head>
<body>
<h1>Landing Screen</h1>
<!-- Bind directly to the ViewModel using ES6 property compatible Binding Helpers -->
<div data-win-bind="innerHTML: sampleData WinJSRocks.Binding.Mode.Property"></div>
<!-- Execute ViewModel Commands directly from markup -->
<button data-win-bind="onclick: navigateToListPageCommand WinJSRocks.Binding.Mode.Command">Navigate To List Page</button>
<div id="landingPivot" data-win-control="WinJS.UI.Pivot">
<div data-win-control="WinJS.UI.PivotItem" data-win-options="{'header': 'PivotItem1'}">
PivotItem1 Content
</div>
<div data-win-control="WinJS.UI.PivotItem" data-win-options="{'header': 'PivotItem2'}">
PivotItem2 Content
</div>
</div>
</body>
</html>
Component
import WinJSRocks from "winjsrocks";
export default class extends WinJSRocks.View.Page {
// Polymorphic constructor that already integrates into the WinJS.Page base structure
constructor(element, viewModel) {
super(element, viewModel);
}
// Ready is supsensed until both:
// - View element (this.element) has been loaded into the DOM
// - ViewModel loading state has fired
ready(element, options) {
// Managed events automatically get disconnected when the view unloads
// The binding context is managed too!
this.addManagedEventListener(landingPivot.winControl, "selectionchanged",
this._onPivotSelectionChanged);
return super.ready(element, options);
}
_onPivotSelectionChanged() {
// ViewModel is already bound to the View and can be used to monitor events
// and send commands
this.viewModel.onPivotSelectedCommand.execute();
}
}
ViewModel
import WinJSRocks from "winjsrocks";
export default class extends WinJSRocks.ViewModel.Base {
constructor(application) {
super(application);
}
// All ViewModel classes have a Data property that is
// posted during navigation events
onDataSet(callback) {
var that = this;
super.onDataSet(function() {
return callback();
});
}
// Encapsulate your data with Getters/Setters for view binding
get sampleData(){
return this._sampleData;
}
// Dispatch events when properties are set
set sampleData(val){
this._sampleData = val;
this.notify("sampleData");
}
// Expose commands for execution
get navigateToListPageCommand() {
return new WinJSRocks.Command.Base(function() {
console.log("navigateToListPageCommand command called!");
});
}
}
#Working with Plugins Plugins are the recommended way of bolting on features into the WinJSRocks application life-cycle.
These types of components are loaded after the core framework has loaded and enables access to all of the goodies (Services/Providers) without any load order mishaps. They are loaded in series, so make sure you provide them in the order of dependence.
To activate, register the class plugin class definition in the WinJSRocks.Application.Instance.configure
options as a plugins
array:
var WinJSRocks = require('winjsrocks');
var app = new WinJSRocks.Application();
app.configure({
plugins:[
MyAwesomePlugin // Provide the Class Definition of the Plugin
]
},
function(err){
});
Building Providers
Providers are an elegant design pattern for decoupling application behavior into implementation strategies that might vary in different situations.
As an example, a common use-case is selecting a provider type to fullfill a need within a service Service at runtime based on measured conditions within application state.
In the WinJSRocks framework, providers have the following expecations:
- remain Stateless (don't expect the same instance to be used in all places)
- Use a self reference to
application
to manage their stateful needs - Does all build-up in the Constructor (no load/unload cycle)
To build a provider, inherit from the WinJSRocks.Provider.Base
class:
import WinJSRocks from "winjsrocks";
export default class KeyboardCatProvider extends WinJSRocks.Provider.Base {
constructor(application) {
super(application);
}
methodA: function(args){
return this.methodB(args);
}
methodB: function(args){
return "Keyboard cat";
}
};
To activate a provider, register using the WinJSRocks.Application.Instance.builder
before calling configure
:
var WinJSRocks = require('winjsrocks');
var app = new WinJSRocks.Application();
app.builder.registerProvider("localStorage", KeyboardCatProvider);
Providers are registered in the
WinJSRocks.Application.Instance.container
before theload
method is called on the application instance to assure custom Services and Providers are first-class citizens during application start-up.