Skip to content

Scalejs Project Structure

Erica Gucciardo edited this page Feb 10, 2014 · 6 revisions

The first thing you will be exposed to after creating a project is the file structure of the project.

Files generated by project template

In this structure there is a bunch of boilerplate code, but it is actually a runable project. Projects can be run by pressing ctrl + f5.

The project template consists of:

  • index.html, the bootstrapper of the app, and config.js a file which specifies the paths to base libraries and scalejs extensions.
  • app folder, a collection of modules needed in the app, and app.js, the file which registers said modules.
  • main folder, a collection of files needed in the main module, and mainModule.js, a file which defines states and ties together the functional and visual aspects of the module.
  • views folder, a collection of views needed for the main module, and main.html, a file containing a view used by the main module.
  • bindings folder, a collection of bindings needed for the main module, and mainBindings.js, the file which contains bindings used by the main module.
  • viewmodels folder, a collection of viewmodels needed in the main module, and mainViewModel.js, the file which contains a viewmodel used by the main module.
  • styles folder, a collection of styles needed in the main module, and main.css or main.less, the file which contains the styles used by the main module.
  • Scripts folder, a collection of base libraries and extensions
  • test folder, a folder containing unit tests for the application

This section will introduce you to the code and purpose of these files. How you can expand to these files to do more complex things will be covered in another section.

index.html and config.js

index.html is the starting point for your app.

index.html

<!DOCTYPE html>
<html>
    <head>
        <script src="config.js" type="text/javascript"></script>
        <script src="Scripts/require.js" data-main="app/app" type="text/javascript"></script>
        <title>helloworld application (development version).</title>
    </head>
    <body>
    </body>
</html>

Only two scripts are referenced in this file: config.js and require.js. The use of requirejs allows us to write code in seperate files, while also maintaining dependencies and ensuring files get loaded in order. It also allows us to write code without adding variables to the global namespace, as it provides function scope.

require.js

The most important aspect of this file is that it tells requirejs that our starting point is the app folder.

You also will notice we have other index files, e.g. index.debug.html, index.release.html, and index.test.html. index.release and index.debug serve a similar purpose as index but they load concatenated versions of your application where as index.html will load files in their respective directories. The difference between the two is that index.release will load a minified version of your app. These will be used once you build the application. index.test is different; it will allow you to run unit tests for your code.

config.js

config.js tells requirejs what base libraries and extensions need to be loaded. It is automatically editted whenever you install, update, or uninstall an extension via NuGet, so you will not need to edit this file unless you are have an intermidiate understanding of scalejs and are ready to build your own extension.

app folder and app.js

The app folder contains a single file app.js as well as folders for the modules in your application.

app.js

/*global require*/
require([
    'scalejs!application/main'
], function (
    application
) {
    'use strict';

    application.run();
});

This is the bootstrapper of your app; it loads the modules and runs the application. Modules can be registered and loaded by appending a comma folloed by the module's name to scalejs!application/main. For example, if you add a new module and name it new, your would need to change scalejs!application/main to scalejs!application/main,new

When adding a new module, you would do so by right clicking on the app folder, selecting Add > New Item, and selecting the appropriate template for a Scalejs Module. If you are using a project with LESS, select a Scalejs LESS Module, and if you are using a project with CSS, select Scalejs CSS Module.

main folder and mainModule.js

The main folder contains the files needed for the main module. Typically, the main module is the shell of your application and will be used to define the layout for your app by creating regions to contain content from other modules. It also can define styles specific to your application.

A folder for a module contains of a file which will be run when the module is loaded. In the case of main, this file is called mainModule.js. Similarly, additional modules will follow the naming convention Module.js

mainModule.js

/*global define */
define([
    'sandbox!main',
    'app/main/viewmodels/mainViewModel',
    'views!main',
    'bindings!main',
    'styles!main'
], function (
    sandbox,
    mainViewModel
) {
    'use strict';

    return function main() {
        var // imports
            root = sandbox.mvvm.root,
            template = sandbox.mvvm.template,
            registerStates = sandbox.state.registerStates,
            state = sandbox.state.builder.state,
            onEntry = sandbox.state.builder.onEntry,
            // vars
            viewModel = mainViewModel();

        // Register application state for the module.
        registerStates('root',
            state('app',
                state('main',
                    onEntry(function () {
                        // Render viewModel using 'main_template' template 
                        // (defined in main.html) and show it in the `root` region.
                        root(template('main_template', viewModel));
                    }))));
    };
});

This file consists of a requirejs module which is used to load the views, viewmodels, bindings, and styles for the module. Requirejs modules differ from scalejs modules as a requirejs module is created using define and is used also to define viewmodels and bindings. A scalejs module, on the other hand, is a combination of html and javscript files needed to implement a specific feature for your app which is independent of other features of the app. The purpose of the mainModule.js file is to setup the module; it does not solve functional tasks, its primary job is to tie the other components of the modules together by including them at the top of define as seen above and define the statechart.

view folder and main.html

The view folder is a container of views and includes one view file for the main module:

main.html

<div id="main_template">
    <span class="main text" data-class="main-text"></span> from helloworld.
</div>

It consists of a single template, main_template. This template text which is bound to a data-class named main-text. The purpose of the view is to contain the templates needed for the module. Usually, it is neccesary to bind components of templates to data which is signified by data-class. data-class connects your view to your bindings, which will be discussed next.

bindings folder and mainBindings.js

Bindings define the data-classes which are used in the view. Bindings rarely exist on their own; they are always referenced by a view. Since main.html defines a data-class property, it must be created in the bindings. This data-class is defined in mainBindings.js

mainBindings.js

/*global define */
/*jslint sloppy: true*/
define({
    'main-text': function () {
        return {
            text: this.text
        };
    }
});

data-class provides similar functionality to what is found in data-bind which is provided by knockoutjs. More specifically, data-class is extends the capabilities of knockout by allowing you to define your data-binds in a seperate file. This avoids ugly inline html for more complex code.

Bindings are what connects your view to your viewmodel. The viewmodel is covered in the next section.

viewmodels folder and mainViewModel.js

Viewmodels are what perform the logical and functional tasks of your module. Viewmodels also define the model for your view; e.g. whatever needs to be in your view which is data-centric or requires dynamic modification must be defined and exposed by a viewmodel. The viewmodel used in the main module is mainViewModel.js

mainViewModel.js

/*global define */
define([
    'sandbox!main'
], function (
    sandbox
) {
    'use strict';

    return function () {
        var // imports
            observable = sandbox.mvvm.observable,
            // properties
            text = observable('Hello World');

        return {
            text: text
        };
    };
});

In this file, we have exposed the text observable which was referenced in the bindings in the previous section.

styles folder and main.css or main.less

Styles are what are used modify the visual aspect of your modules. There is nothing special about the style which is provided by the template. However, we do recommend giving thought to the order and structure of styles when building a large application. These recommendations can be found in the styles section.

Scripts

The Scripts folder contains a combination of core scalejs files, scalejs extensions, and base libraries. The difference between extensions and base libraries are one of the key advantages of scalejs.

test

The test folder contains unit tests which are provided by the jasmine testing framework.

all.tests.js

/*global define,jasmine*/
define([
    'jasmine-html',
    'scalejs!application',
    './mainViewModel.test'
], function () {
    'use strict';

    var jasmineEnv = jasmine.getEnv(),
        htmlReporter = new jasmine.HtmlReporter();

    jasmineEnv.updateInterval = 1000;
    jasmineEnv.addReporter(htmlReporter);

    jasmineEnv.specFilter = function (spec) {
        return htmlReporter.specFilter(spec);
    };

    jasmineEnv.execute();
});

This file starts the tests. Add new tests by referencing them in the first argument of define like how ./mainViewModel.test is defined.

mainViewModel.test.js

/*global define,describe,expect,it*/
/*jslint sloppy: true*/
/// <reference path="../Scripts/jasmine.js"/>
define([
    'scalejs!core',
    'app/main/viewmodels/mainViewModel',
    'scalejs!application'
], function (core, mainViewModel) {
    describe('`mainViewModel`', function () {
        var vm = mainViewModel(core.buildSandbox('main.test'));

        it('text is set to "Hello World" after initialization.', function () {
            expect(vm).toBeDefined();
            expect(vm.text()).toBe('Hello World');
        });
    });
});

This test checks if the mainViewModel was loaded and text observable from the mainViewModel is 'Hello World!'