In this tutorial, we take a look at Cascade, what it can do, and the fundamental parts of any Cascade project. You should already have a TypeScript environment running, and have included Cascade in your project dependencies.

Project Organization

Any Cascade project includes Application State and Components. This is similar to many common patterns such as MVC or MVVM, where you have a distinct separation between your data, and how you display it. Ultimately, the specifics of your pattern are up to you, but the overal concept is the same. We will introduce some common patterns in later tutorials.

Before we get started, your TypeScript project should have a tsconfig.json file. Open this file, and ensure the following options are set:

"jsx": "react",
"reactNamespace": "Cascade",
"experimentalDecorators": true,
"emitDecoratorMetadata": true

The emitDecoratorMetadata value is optional, as we will see later.

Also, ensure that you have src and public directories in your project.

Building your Project

It will be easier to develop our project if we can run it. Let's start by setting up a simple set of files and getting the to build.

Inside the public directory, create a file index.html. This will be the main HTML file that runs the project.

<!DOCTYPE>
<html>

<head>
    <title>Tutorial 0</title>
</head>

<body>
    <div id="root"></div>
    <script src="bundle/main.js" type="text/javascript"></script>
</body>

</html>

This file creates a <div> to mount our component, and then loads the script. Note that we're loading our script from bundle/main.js, which is a file that doesn't currently exist. We are going to build it later.

Inside the src/scripts directory, create a file main.tsx.

window.onload = function () {
    console.log('started');
}

We now must set up Webpack to build our main.tsx file. Inside the webpack configs (there are two if you're using the ts-boilerplate), ensure that the entry and output objects have the correct values. it should read:

    entry: {
        'main': './src/scripts/main.tsx'
    },
    output: {
        filename: './public/bundle/[name].js',
        libraryTarget: 'var',
        library: '[name]'
    },

This is likely very similar to what is already there. Simply change main.ts to main.tsx, the filename from dist to public, and remove the .min portion of the filename.

Now to test it, run

npm run dev

If you want to run automatically as you're developing, call

npm run watch

If you want to run a minified version of the file, call

npm run min

If it is successful, open the public/index.html file in your browser. Open the browser's console, and you should see started printed to the screen.

Application State

Cascade stores its data in Application State objects. Depending on what you want to store, they may contain values, other objects, arrays, or even methods to manipulate the data.

Let's say we want to create a Model of a User. It should store common information for our users, like first and last name. We also will want to edit it.

So, we start by declaring a class, and adding several properties. Inside your project, create a folder src/scripts/models, and inside it a file User.ts.

export default class User {
    firstName: string;
    lastName: string;
}

Fantastic! We now have a User class which will correctly store our data! We have also exported it as default from this file.

Components

Cascade displays its data in Components, which are simply classes that render to the DOM. They may render Nodes, strings, numbers, nothing, or even other Components.

So, since we have our User model, let's display it! Inside your project, create a folder src/scripts/views/user, and inside it a file UserView.tsx.

First, we must import Cascade into the file.

import Cascade, { Component } from 'cascade';

We have imported Cascade, and the Component class. While we will not necessarily use the Cascade import directly in our code, the JSX interpreter will transpile our JSX statements into Cascade calls. This process turns what appear to be XML elements into:

Cascade.createElement<T extends Object>(type: string | (new (props: T, ...children: Array<any>) => Component<T>), props: T, ...children: Array<any>): IVirtualNode<T>;`.

Now we must import our User model.

import User from '../../models/User';

We now must define what properties our component takes. Our component will take in a user. These appear as XML Attributes in our JSX code.

export interface IUserViewProps {
    user: User;
}

Next we need to define our component itself.

export default class UserView extends Component<IUserViewProps> {
    render() {
        let {user} = this.props;
        return (
            <div>
                <p>First name: {user.firstName}</p>
                <p>Last name: {user.lastName}</p>
            </div>
        );
    }
}

This component takes in a user, and displays the firstName and lastName inside to <p> elements, wrapped inside one <div> element.

There are a couple things to note:

  1. We used the Props interface inside the Component definition. This provides intellisense both when writing the component, and later when we use it.
  2. We used object destructuring to get user from this.props. This is a shorthand that is much simpler than let user = this.props.user;. It especially comes in handy if you're doing that for multiple properties.
  3. Every component must define a render method, even if it returns nothing.
  4. A component may return exactly one value. Here we wrapped our multiple <p> tags in a single <div> tag.
  5. We user the {} notation to insert the user values into the elements.

Rendering

We now must render our Application State into Component and display it to the DOM.

Inside the main.tsx we must import all of our files, and then render them.

import Cascade from 'cascade'

import User from './models/User';
import UserView from './views/UserView';

window.onload = function () {
    var user = new User();
    user.firstName = 'First';
    user.lastName = 'Last';

    Cascade.render(
        document.getElementById('root'),
        <UserView user={User} />
    );
};

There are a couple things to note:

  1. We imported Cascade, our User, and our UserView.
  2. We write our code inside the onload function to ensure it runs after everything is ready.
  3. We create a new user and set the first and last name properties.
  4. We get the root element from the DOM
  5. We create a new UserView with JSX, pass in the user.
  6. We pass the root element and the UserView to Cascade.render().

Now build the project and run the HTML file in your browser. It should display your user in the HTML you specified.