Angular 4, SharePoint On-Premises, localhost development and SP-REST-Proxy

We've been running Angular 4 (via ng-cli) on our SharePoint On-Premises environment for a while, I wanted to just take a short time and jog down our battle notes. 

Especially, as a thank you to @AndrewKoltyakov who built https://github.com/koltyakov/sp-rest-proxy

If there are areas that are unclear, let me know in the comments and I'll try to clarify.

package.json

  "scripts": {
    "ng": "ng",
    "hmr": "ng serve --hmr --environment=hmr --verbose --proxy=proxy.conf.json",
    "debug": "ng serve --environment=hmr --verbose --proxy=proxy.conf.json",
    "prod": "ng serve --env=prod --verbose --proxy=proxy.conf.json",
    "serve": "ng serve -e=source --verbose --proxy=proxy.conf.json",
    "build": "ng build --env=prod --aot ",
    "bundle-report": "webpack-bundle-analyzer dist/stats.json"
  },

We added additional scripts to our package.json.  This just means we can easily switch to different modes without forgetting which arguments we messed up.

"serve" was the basic one that says run localhost.  We don't use this one now as much, as we love hot module reloading (hmr).

We use "--proxy" to set up Angular/Webpack-Dev-Server's proxy settings via a separate proxy.conf.json file.

https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md

"debug" was the same as "prod" except it doesn't have Angular's production flag.  This makes things faster somehow.  In reality, if you want Angular to be blinding fast, use --aot

"hmr" is nearly the same as "serve, and runs out of localhost".  Use the Angular --proxy 

"build" compiles everything with --prod and --aot.  Does not run locally.

We use different Angular environment settings to set up mock proxies, and apply a slightly different header CSS so we know at a glance which environment we are in.

I'm going to hear the question: Why so many different variations?!  That's so confusing.

Well, they are all different.  And nobody can decide which one is the best for which scenario.  So we keep writing new ones!  

Deal with it :-)

environment.ts

A quick note on our Angular environment, before we get into the proxy configurations.

// environment.hmr.ts
export const environment = {
  production: false,
  source: true,
  hmr: true,
  mock: require("../app/core/testData.json"),
  jquery: require("jquery")  
};

Depending on which --env=hmr is used, the corresponding environments/environment.hmr.ts is loaded.  

We put variables that affect different code execution in this environment file.  We also find this to be a good place to load big mock json files.

Sometimes you want to run the application locally and you aren't in office, so even the proxy won't work - we will then fall back to local mock json data sources.

hot module reloading (HMR)

HMR needs a separate blog post to describe it.  We followed this:

https://medium.com/@beeman/tutorial-enable-hrm-in-angular-cli-apps-1b0d13b80130#.2p0n6oo34

// main.ts
const bootstrap = () => {
  return platformBrowserDynamic().bootstrapModule(AppModule);
};

if (environment.hmr) {
  if (module['hot']) {
    hmrBootstrap(module, bootstrap);
  } 
  else {
    console.error('HMR is not enabled for webpack-dev-server!');
    console.log('Are you using the --hmr flag for ng serve?');
  }
} 
else {
  bootstrap();
}

When HMR is enabled via environment variable, we have a slightly different bootstrap mechanism.

proxy.conf.json

{
    "*/_api/**": {
        "target": "http://localhost:8080",
        "secure": false,
        "changeOrigin": true
    },
    "/style%20library/**": {
        "target": "http://localhost:8080",
        "secure": false,
        "changeOrigin": true
    },
    "/Style%20Library/**": {
        "target": "http://localhost:8080",
        "secure": false,
        "changeOrigin": true
    },
    "*/sp2016/**": {
        "target": "http://localhost:8080",
        "secure": false,
        "changeOrigin": true
    }
}

We re-route several localhost calls in Webpack-Dev-Server to the SP-REST-Proxy  
We also send relative URL asset requests through SP-REST-Proxy.

SP-REST-Proxy

// serve.js
const RestProxy = require('sp-rest-proxy');
const path = require('path');
const settings = {
    configPathpath.join(__dirname'/_private.conf.json'), // Location for SharePoint instance mapping and credentials 
    port8080,                                              // Local server port 
    staticRootpath.join(__dirname'/static')                 // Root folder for static content 
};
 
const restProxy = new RestProxy(settings);
restProxy.serve();

This is the main starting server.js 

{
    "siteUrl": "http://sp2016",
    "domain": "SPG",
    "username": "jliu",
    "password": "********" 
}

This is _private.conf.json, everything is routed to SharePoint On-Premises as me.

Images and CSS we place in a static subfolder which mirrors the SharePoint root web style library.

\static
    \style library
        \cloud.jpg
        \main.css
\server.js
\_private.conf.json

Start SP-REST-Proxy and it will bind all localhost:8080 calls over to SharePoint, or to its static file system.

localhost development

And that's pretty much how we set up Angular 4, WebPack-Dev-Server (with --proxy), SP-REST-Proxy, various different environment variables and wire everything to different npm run scripts.

Our main favourites:

npm run hmr
This option runs localhost, with SP-REST-PROXY to real on-premises server.

npm serve
This option runs localhost with mock data.  Also uses SP-REST-PROXY for static resources.  But Angular data services does not make real calls - just mock ones.

npm build
This option builds with -production and --aot
We chain this with SP-SAVE to upload this into our on-premises development environment.

npm run bundle-report
This runs a bundle report check and is fun eye candy to help us understand what the hell got webpacked.