Skip to content

ServiceStackApps/typescript-react-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 

Repository files navigation

TypeScript React App

ServiceStack's new TypeScript React App VS.NET template offers the current Gold Standard for developing modern JavaScript Apps in VS.NET with the just released:

  • TypeScript 1.8 - Superset of JavaScript with optional typing, advanced language features and down-level ES5 support
  • JSPM - JavaScript Package Manager supporting SystemJS modules and multiple npm and GitHub repositories
  • typings - Package manager to search and install TypeScript definition files
  • React - Simple, high-performance JavaScript UI Framework utilizing a Virtual DOM and Reactive Data flows
  • Gulp - Task runner for server and client packaging, bundling, minifying and deployments

Providing a great base for the development of large-scale, JavaScript Apps that's further enhanced by a great development experience within Visual Studio.

Pre-Requisites

Download and install the latest TypeScript 1.8+ for your IDE:

The TypeScript Single Page App templates are optimized for VS.NET 2015's support for tsconfig.json

If you haven't already, you also need to install the ServiceStackVS VS.NET Extension containing the VS.NET template.

Getting Started

As with most modern JavaScript Apps we're leveraging an npm-based build system which are increasingly being split into hundreds of micro modules (often only containing a single function). The immediate impact of this is that creating a template can take minutes, so it's a good time to sit back, relax and continue ahead read the rest of this guide :)

For the purposes of this guide we'll assume you've created a project called NetworkedShapeCreator which we'll walk through creating the final Networked Time Travelling Shape Creator example seen below:

To help familiarize with these modern technologies we've published an in-depth step-by-step guide for beginners that starts off building the simplest HelloWorld TypeScript React App from scratch then slowly growing with each example explaining how TypeScript, React and Redux can be used to easily create more complex Apps.

Live Demo: http://redux.servicestack.net

If you need to refer to it, the source code for this guide is available from the /src folder of this GitHub Repository.

Build and Run the default template

Even after the template is loaded in Visual Studio you'll need to wait until npm completes:

After it successfully completes you can Rebuild + Run the solution to see a working client/server Hello World App:

Troubleshooting

If one of the many npm packages happens to fail to install during the creation of the template, the easiest way to resolve the issue is to delete npm's node_modules folder and in a command prompt from your NetworkedShapeCreator Web App project folder, run:

npm install

If there's still an issue we recommend deleting the solution folder and re-creating the VS.NET Template again.

Solution Structure

The resulting project structure is similar to other ServiceStackVS Single Page App VS.NET Templates which follows our Recommended Physical Project Structure:

NetworkedShapeCreator.sln

  • NetworkedShapeCreator - Complete Web App, inc. all .ts/.tsx, .js, .css, imgs, etc.
    • \src - Should contain all TypeScript source files used in your Client App
    • \wwwroot - Where the packaged app is staged to ready for deployment
    • \wwwroot_build - Scripts and production config used for packaging and deployment
  • NetworkedShapeCreator.ServiceInterface - ServiceStack Service Implementations
  • NetworkedShapeCreator.ServiceModel - Request and Response DTO's
  • NetworkedShapeCreator.Tests - NUnit Tests

NetworkedShapeCreator Project

The primary NetworkedShapeCreator project contains the complete React Web App hosted in an ASP.NET Project. It includes gulpfile.js which provides the necessary Gulp tasks to bundle and optimize the Wep Application ready for deployment.

The primary Gulp Tasks you'll use to package and deploy your App are prefixed with a number in the logical order they're run in and sorted at the top of VS .NET's Task Runner Explorer which is built into VS 2015:

  • 00-update-deps-js - Used to regenerate deps.lib.js from deps.tsx for reducing number of HTTP requests sent on each refresh
  • 01-package-server - Used for building and staging the server components of your application
  • 02-package-client - Used to compile and stage your client side resources ready for deployment
  • 03-deploy-app - Deploys your application using msdeploy and config.json found in wwwroot_build/publish
  • default - The default task that builds and stages your application
  • package-and-deploy - Perform all tasks to build, stage and deploy your application

You can double-click the task name to run that specified task.

All other tasks below package-and-deploy starting with a www- prefix contain the individual granular tasks the high-level tasks above call upon to accomplish their respective tasks.

Preloading Dependencies

Despite having a working App we also have our first problem where you'll witness a noticeable delay in rendering Hello World? Checking the network requests tab shows the root cause:

Over 170 requests to render the simplest React App! The same reason why we had to wait so long for npm to finish downloading all React's dependencies after the template was created, is what's causing noticeable lag here - micro modules!

Since we don't have the luxury of HTTP 2's multiplexed requests in VS.NET's WebDev server, we need another way to get the network requests countdown which we've built into the template with the 00-update-deps-js Gulp task which will create an interim bundle with all the 3rd Party dependencies in our deps.tsx:

/// <reference path='typings/browser.d.ts'/>
import * as React from 'react';
import { render } from 'react-dom';

class Deps extends React.Component<any, any> {
    render() {
        return <div>Hello, World!</div>;
    }
}

const ignore = () => render(<Deps/>, document.body);

When you run 00-update-deps-js it will populate all the dependencies used above into a single deps.lib.js library which the template already references in default.html.

After deps.lib.js is populated with all referenced 3rd party modules, it drops requests for all external deps down to a single HTTP Request as by the time our App requests use of a 3rd party module, it's already pre-loaded.

As this keeps a cache of external dependencies, if you update any 3rd Party dependency you'll need to re-run 00-update-deps-js so the cache is updated. You can also change deps.tsx with references to new dependencies that you want to pre-load in deps.lib.js.

The necessary infrastructure for optimizing, packaging the React Web Application is contained in the /wwwroot_build folder:

/wwwroot_build
  /deploy                   # copies all files in folder to /wwwroot
    appsettings.txt         # production appsettings to override dev defaults
  /publish                    
    config.json             # deployment config for WebDeploy IIS deployments
  00-install-dependencies   # runs npm and jspm install, for downloading missing deps after cloning repo
  package-and-deploy.bat    # runs the gulp **package-and-deploy** task

React App Startup

The <script /> in your default.html shows how your app is structured:

<div class="container">
    <div id="content"></div>
</div>

<!-- build:js system.js -->
<script src="jspm_packages/system.js"></script>
<!-- endbuild -->

<script src="config.js"></script>

<!-- build:remove -->
<script src="deps.lib.js"></script>
<!-- endbuild -->

<!-- htmlbuild:appbundle -->
<!-- endbuild -->
<script>
    System.import("./src/app");
</script>
  • The empty <div id="content"></div> is where we'll instruct React to render our App in
  • system.js - is the Universal SystemJS Module loader
  • config.js - holds all our apps 3rd party JSPM dependencies
  • deb.lib.js - holds an interim bundle of our 3rd party JSPM dependencies
  • The empty <!-- htmlbuild:appbundle --> tells gulp where to insert our bundled app

Whilst System.import("./src/app") runs the main entry point of our App in src/app.tsx:

/// <reference path='../typings/browser.d.ts'/>

import * as ReactDOM from 'react-dom';
import * as React from 'react';
import HelloWorld from './hello';

ReactDOM.render(<HelloWorld/>, document.getElementById("content"));

The first line references all the Type Definitions we've installed with typings.

The react and react-dom import statements references 3rd party JavaScript libraries installed with JSPM.

Whilst ./hello references our local <HelloWorld /> React Component below that the last line renders in the <div id="content"></div> element:

/// <reference path='../typings/browser.d.ts'/>

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import 'jquery';
export default class HelloWorld extends React.Component<any, any> {
    constructor(props, context) {
        super(props, context);
        this.state = { yourName: '' };
    }

    update(event: any) {
        var yourName = event.target.value;
        $.getJSON(`hello/${yourName}`, r => {
           this.setState({ yourName: r.Result });
       });
    }

    render() {
        return (
          <div className="form-group">
            <input type="text" placeholder="Your name" onChange={e=>this.update(e)} className="form-control"/>
            <h3>{this.state.yourName}</h3>
          </div>);
    }
}

The implementation for the HelloWorld component just calls our back-end Hello ServiceStack Service with the value of the <input /> field on every key stroke, the result is then rendered in the <h3/> title tag.

NetworkedShapeCreator.ServiceModel

The Service Contracts (DTO's) for all our Services is contained in the implementation-free ServiceModel project which contains the Request and Response DTO for our Hello Service:

[Route("/hello")]
[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
    public string Name { get; set; }
}

public class HelloResponse
{
    public string Result { get; set; }
}

NetworkedShapeCreator.ServiceInterface

Implementation for our Services is contained in the ServiceInterface implementation project that just returns a simple Greeting message:

public class MyServices : Service
{
    public object Any(Hello request)
    {
        return new HelloResponse { Result = "Hello, {0}!".Fmt(request.Name) };
    }
}

Development Workflow

Every Time you save a .ts or .tsx file TypeScript automatically builds and regenerates the corresponding .js file using the TypeScript compiler options configured in tsconfig.json.

The generated .js outputs are what gets loaded by JSPM when viewing the website in a browser. Since TypeScript has sourceMap:true enabled in tsconfig.json it also generates the source maps that you can use to debug original TypeScript source files directly from within Chrome!

When you create a new .ts or .tsx TypeScript source files, you'll want to include a reference to browser.d.ts in order to reference all the Type Definitions installed with typings:

/// <reference path='../typings/browser.d.ts'/>

Publishing to Source Control

After you're comfortable with your working project it's a good time to publish it to source control. Before you publish you'll want to ensure that both node_modules/ and jspm_packages/ folders are added to your repo's .gitignore (or equivalent for your source control):

node_modules/
jspm_packages/

Otherwise it will unnecessarily check-in thousands of your app's javascript dependencies.

Other developers after cloning your project can download the missing npm and jspm dependencies with:

C:\proj> npm install

You'll also want to ignore your publish/ folder as it will contain sensitive info if you're also using Gulp to deploy your App. This would already have been added if you've used the VisualStudio template when creating your GitHub repository.

Default JavaScript Libraries and Type Definitions

This template comes pre-installed with the major dependencies we expect most ServiceStack TypeScript React Apps to have, namely:

  • react - The core React Framework
  • react-dom - The DOM bindings for React
  • es6-shim - A shim to add missing ES6 APIs in ES5 browsers
  • jQuery - The One and Only and required dependency for ss-utils
  • ss-utils - Client utils library and integration with ServiceStack features

The template also includes TypeScript definitions for all the above dependencies, installed using typings.

Creating Networked Shape Creator

To learn about Redux and how to build a Networked Shape Creator from scratch please walk through all the examples in typescript-redux

Here we'll just include the steps to create the Networked Shape Creator from the default template.

First thing we need to is install the required JavaScript libraries which we rely on JSPM to install for us. Whilst the Install Package VS.NET Extension would provides a nice GUI to install JSPM packages we recommend that you use a command-line instead which offers better visibility of how external packages integrates with your project and all fits together.

The easiest way to bring up a command prompt is to hold down Shift whilst right-clicking on your NetworkedShapeCreator Web App project folder and clicking Open command window here on the context menu:

And in the Command Prompt install redux and react-redux:

by running both commands below:

jspm install redux
jspm install react-redux

We also need to install Type Definitions for the above libraries by running:

typings install redux --ambient --save
typings install react-redux --ambient --save

That will install all the missing dependencies and TypeScript Definitions NetworkedShapeCreator needs. Now we just need to create the application by adding new TypeScript files to your project under the TypeScript /src folder using the Add > TypeScript File context menu item:

After you've added your first TypeScript file a way to quickly create multiple copies is to copy + paste the file

Now replace each file with the filename and contents below:

Since we have a completely different application we need to also replace default.html with the HTML below:

<html>
<head>
    <title>TypeScript + JSPM + React</title>
    <style>
        body { font-family: sans-serif; }
        h1 { padding: 20px 0; }
        .noselect {
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -khtml-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }
    </style>
</head>
<body>
    <h1>Networked Shape Creator</h1>
    <div id="content"></div>

    <!-- build:js system.js -->
    <script src="jspm_packages/system.js"></script>
    <!-- endbuild -->
    <script src="config.js"></script>
    <!-- build:remove -->
    <script src="deps.lib.js"></script>
    <!-- endbuild -->
    <!-- htmlbuild:appbundle -->
    <!-- endbuild -->

    <script>
        System.import("./src/app");
    </script>
</body>
</html>

Update redux.d.ts

There's currently an outstanding issue with redux TypeScript definition that makes the method signature of applyMiddleware incompatible with what createStore accepts. Until a new TypeScript definition is released you'll need to manually edit typings/browser/ambient/redux/redux.d.ts and replace the applyMiddleware definition from:

function applyMiddleware(...middlewares: Middleware[]): Function;

to:

function applyMiddleware(...middlewares: Middleware[]): () => any;

Configuring Server Events

With the client created all that's left is to configure the Server which in order to facilitate real-time communications between users we need to enable Server Events by registering the ServerEventsFeature in our AppHost.Configure():

public override void Configure(Container container)
{
    ...
    
    Plugins.Add(new ServerEventsFeature());
}

Then in the NetworkedShapeCreator.ServiceModel project add the Service Contracts for the 2 new Services:

using System.IO;
using ServiceStack;
using ServiceStack.Web;

namespace NetworkedShapeCreator.ServiceModel
{
    [Route("/publish-channel/{Channel}")]
    public class PublishToChannel : IReturnVoid, IRequiresRequestStream
    {
        public string Channel { get; set; }
        public string Selector { get; set; }
        public Stream RequestStream { get; set; }
    }

    [Route("/send-user/{To}")]
    public class SendUser : IReturnVoid, IRequiresRequestStream
    {
        public string To { get; set; }
        public string Selector { get; set; }
        public Stream RequestStream { get; set; }
    }
}

Then we just need to add the Services implementation in NetworkedShapeCreator.ServiceInterface

using NetworkedShapeCreator.ServiceModel;
using ServiceStack;

namespace NetworkedShapeCreator.ServiceInterface
{
    public class ReduxServices : Service
    {
        public IServerEvents ServerEvents { get; set; }

        public void Any(PublishToChannel request)
        {
            var msg = request.RequestStream.ReadFully().FromUtf8Bytes();
            ServerEvents.NotifyChannel(request.Channel, request.Selector, msg);
        }

        public void Any(SendUser request)
        {
            var msg = request.RequestStream.ReadFully().FromUtf8Bytes();
            ServerEvents.NotifyUserId(request.To, request.Selector, msg);
        }
    }
}

Package and Deployments

Now that the app is in a state that's ready for deployment we can make use of the Gulp tasks to package and optimize the App and deploy it via Web Deploy.

The /wwwroot_build folder contains the necessary files required for deployments including:

/wwwroot_build
  /deploy                 # copies all files in folder to /wwwroot
    appsettings.txt       # production appsettings to override dev defaults
  /publish
    config.json           # deployment config for WebDeploy deployments
  package-and-deploy.bat  # runs package-and-deploy Gulp Task

The minimum steps to deploy an app is to fill in publish/config.json with the remote IIS WebSite settings as well as a UserName and Password of a User that has permission to remote deploy an app:

{
    "iisApp": "vstest",
    "serverAddress": "remote.server.org",
    "userName": "{WebDeployUserName}",
    "password" : "{WebDeployPassword}"
}

Then just run the package-and-deploy Gulp task (or .bat script) which copies over the the server and client assets into the /wwwroot folder which contain the physical files of what gets deployed.

The package-server task will copy over the required .NET .dll and ASP.NET files as well as any files in /wwwroot_build/deploy which can be used to customize the production website.

The package-client task also optimizes any .jsx, .js and .css so only the bundled and minified versions get deployed.

The resulting wwwroot folder is then packaged into a webdeploy.zip file and deployed to the remote server, after it's finished running you will be able to run your app on the remote server which for NetworkedShapeCreator is currently: http://vstest.servicestack.net/

Feedback

We hope you've found this guide useful and it helps spur some ideas of what you can create with these simple and powerful technologies in your next TypeScript React App. Please feel free to suggest any improvements on ServiceStack's UserVoice or you can report any Issues on GitHub.

About

Walk through of using the new TypeScript 1.8 + React + JSPM VS.NET Template

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published