3

I've created an Angular2 application integrated with ASP.NET and served by IIS. This application is to be used by multiple clients and they would like the ability to customize the look and feel of the application to fit their branding. I'm using some aspects of ASP.NET MVC5's framework to make this possible.

The application is working as expected for most things, but I have two big stumbling blocks utilizing the cli that I can't seem to figure out.

Here is the basic folder structure of the application(there are other files and directories, but I'm excluding them as I don't believe they are relevant to the question):

Solution.sln
Web
  Controllers
  dist
  Models
  src
  Views
    Shared
      _Layout.cshtml
  .angular-cli.json
  package.json
  Web.config
  Global.asax

The only relevant update that I've made to my .angular-cli.json file is that I changed the { apps: { index: '' } } value to be "../Views/Shared/_Layout.cshtml" instead of the default "index.html".

When I run ng build from within the Web directory, the application compiles and the correct script tags are placed in the _Layout.cshtml file right before the closing body tag as expected.


I'm not sure if these problems stem from the same root cause. I assume they do which is why I've put both here, but if they seem to be separate questions, I can break them apart.


Issue 1 If I run ng build multiple times, it doesn't remove and replace the script tags it creates in the _Layout.cshtml file as expected, but instead continues to append new script tags before the closing body tag. So if I run ng build twice in development mode, I'll get the same set of script tags in the file twice.

Any ideas as to what I would need to change to get the expected functionality back from ng build?


Issue 2 Now when I run ng build --watch, the build seems to end up in an endless loop. It continues packaging the assets over and over.

Any ideas as to what might be causing the infinite loop and how I can get ng build --watch to work properly with this setup?


I didn't have either of these issues with the index.html file, but unfortunately, I need to use the _Layout.cshtml file as my index file. I can create a custom prebuild npm task to clean up the script tags, but it doesn't seem like I should have to. I have not figured out a work around for the watch task issue.


UPDATE As I suspected, this has nothing to do with the fact that it is an *.cshtml file, but rather due to the fact that the apps/index location is outside of the src folder.

Steps to reproduce:

  1. npm install @angular/cli
  2. node_modules/.bin/ng new test-custom-index
  3. Movetest-custom-index/src/index.html to test-custom-index/index.html
  4. Update the .angular-cli.json so index property is "index": "../index.html" instead of "index": "index.html"
  5. From the test-custom-index directory run node_modules/.bin/ng build two times consecutively and you will see the duplicate script entries in the index.html file. OR run node_modules/.bin/ng build --watch and watch it get caught in an infinite loop.

Any help would be greatly appreciated!!!

3 Answers 3

2

I solved this by making the angular index page a partial and loading into the razor layout.

In the cli config:

"index": "index.cshtml",

but that file is actually blank. Then, in a razor file that uses the layout you want:

@section scripts
{
    <base href="~/">
    @Html.Partial("~/js/dist/index.cshtml")
}

You could also just put that partial directly into the layout. The only gotcha with this is that if the index.cshtml lies outside your View folder you need to make it inherit from MvcWebPage, and the easiest way to do this is to copy the web.config file from your View folder into a folder one up from the index.cshtml file.

Sign up to request clarification or add additional context in comments.

5 Comments

This is a great idea. However I found that it always wanted to resolve the linked in js an css files from the root instead of from the dist folder. Do you have a solution for that?
Not 100% sure what you mean, but in the js/dist/index.cshtml, the <script> imports will use the config from the angular-cli.json, eg "deployUrl": "js/dist/"
Nice solution. Although I don't know how do you approach the typical scenario where generated index holds both a <link ... /> node in </head> (for style) and a set of <script ...></script> nodes in <body> (for code), and you want to insert those two in separate places in your Razor _Layout.cshtml.
When starting from an empty index, generated one has them all packed together: <head><link ... /></head><script ...></script><script ...></script><script ...></script>.
Correct, with the --prod switch the cli extracts the .css into a <head></head> tag. Using my solution the final index.cshtml will have two <head></head> tags, but still magically works (tested even on old IE versions). You don't need to put it in different parts of the _Layout.cshtml for it to work. Not sure what the implications of that are, but so far so good.
1

Currently I have a work around in place that I would like to share if others are experiencing the same issue.

I would still like to know if there is a better way to approach this as it seems a bit hacky to me.

At the same level as the src directory, I created a build directory. In it, I have two files:

prebuild.js

var fs = require('fs');
var path = require('path');
var inputFilePath = path.join(__dirname, '../Views/Shared/_Layout.cshtml');
var outputFilePath = path.join(__dirname, '../src/_Layout.cshtml');

fs.readFile(inputFilePath, 'utf8', function (err,data) {
  if (err) {
    return console.log(err);
  }
  var result = data.replace(/<\/app-faculty-profile>[\s\S]*<\/body>/g, '</app-faculty-profile>\n\r</body>');

  fs.writeFile(outputFilePath, result, 'utf8', function (err) {
     if (err) return console.log(err);
  });
});

postbuild.js

var fs = require('fs');
var path = require('path');
var inputFilePath = path.join(__dirname, '../dist/_Layout.cshtml');
var outputFilePath = path.join(__dirname, '../Views/Shared/_Layout.cshtml');

fs.readFile(inputFilePath, 'utf8', function (err,data) {
  if (err) {
    return console.log(err);
  }

  fs.writeFile(outputFilePath, data, 'utf8', function (err) {
     if (err) return console.log(err);
  });
});

Then in my package.json file I added a few scripts:

"ng": "ng",
"prebuild": "node build/prebuild.js",
"build": "ng build",
"postbuild": "node build/postbuild.js",
"watch": "npm run build && ng build --watch"

Now when I run npm run watch from the command line, it will execute the build script. By convention npm scripts will run the prebuild before it executes the build script and the postbuild script after. The prebuild script removes the script tags from and then copies the _Layout.cshtml file into the src directory. The postbuild script then copies the _Layout.cshtml file back into the location that ASP.NET is expecting it to be in. After all of this the npm script kicks off the ng build command with the --watch flag. This forces the angular build process to execute twice before the watch actually takes effect, but this is the only way I've been able to get this to work so far.

1 Comment

In this GH issue comment there's another approach that adds custom build steps to extract pieces from generated index.html and write them into Razor file
0

@user917170

The only gotcha with this is that if the index.cshtml lies outside your View folder you need to make it inherit from MvcWebPage, and the easiest way to do this is to copy the web.config file from your View folder into a folder one up from the index.cshtml file.

you can just add the line to the index.cshtml:

@inherits System.Web.Mvc.WebViewPage

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.