AngularJS and .NET MVC: Convert templates from html to javascript and bundle app and templates together

I am working on a project where I have an AngularJS app on my own .NET MVC website, and I will display this app on several remote websites.

TL;DR: To get the AngularJS templates to load correctly on the remote website, I needed to convert the html template files to javascript and bundle them together with the rest of the app. This article will try to explain why and how to achieve this.

Table of contents

What happens when you run your AngularJS app on a remote website?

My AngularJS app is not very complex, it has this file structure in Visual Studio:

project-structure

The project is using the .NET MVC 4 framework. That does not affect how I make the AngularJS app per se, but I will be bundling the app using the .NET framework later in the article.

To use the app, I simply create a html page with this content:

Now the app will be loaded into the div at the bottom of the page.

As you see, the template files (Creditcard.html and Home.html) are not referenced in the html page. They are referenced inside CreditcardApp.js, which looks like this:

I left out the other routes for readability, the Creditcard.html template is referenced in the same way as Home.html.

Then I create a new html page on another website:

Note that I moved the script tags to the body of the page. On the remote server, I (or rather the editor) does not have access to change the head of the page, just the content in the body.

Obviously, now I have to use full urls in the references to the js files, since they are located on another server. But I don’t have a way of referencing the templates. When I open the page in a browser, the app does not load, I just get an error message saying “Error: [$compile:tpload] Failed to load template: /CreditcardApp/Templates/Home.html”.

My first thought was to simply change the paths in CreditcardApp.js to full urls, like this:

Unfortunately, this does not work. I get the same error message saying the template failed to load. Apparently AngularJS expects its templates to be on the same server as its code, and ignores the domain.

How to convert your templates to javascript and bundle them

Before I tackle the problem of how to load the templates on the remote server, I just want to quickly go through how I bundled my app with .NET MVC. This is standard procedure for all script files, and not just relevant for AngularJS apps.

Bundle and minify an AngularJS app in .NET MVC

In a standard .NET MVC project, you use a BundleConfig class to set up bundling and minification of scripts and stylesheets. For my project it looked like this:

If I return to my simple html page on the remote server, the whole app is now fetched by inserting a single script tag:

Note that I also removed the div tag at the bottom, because I insert that programmatically into the page in the script init.js. Remember, my goal is for an editor to include my app on her own website as easily as possible. If I had full control of the site where the app would be displayed, I would have included the script tag in the head of the page and not in the body. Now the app will be displayed wherever the script tag is inserted, which gives the editor freedom to easily put other content before and after it.

But my problem still remains, the app does not load on the remote site because it cannot find the template files.

I cannot include my html templates directly into the bundle. Bundles are meant for scripts and stylesheets (with specific bundle types for each), not html. But in AngularJS you can also create templates with javascript and insert them directly into the template cache. This is done like this:

This is OK for very small templates, but it gets very confusing very quickly when you are trying to write the html for a large webpage all on one line and without any help from Visual Studio’s intellisense.

Create a custom bundler to transform html templates to javascript

My solution is to combine the two techniques I have just discussed. I use the bundling framework to make my own custom bundler that transforms my html files to javascript and then use the above AngularJS method to insert the template into the template cache.

The solution was inspired by Andy Lee’s blog post ASP.NET Bundling of Angular Templates.

The first step is to create a custom BundleTransform:

The transformer creates a string with the necessary javascript to open AngularJS’ template cache for writing. Then it inserts a put method for each template file and inserts the content of the template file into the method parameters.

Note that it also removes all linebreaks in the the html content and replaces apostrophes (‘) with escaped apostrophes (//’). The path to the template is converted from the format “~/CreditcardApp/Templates/Home.html” to “/CreditcardApp/Templates/Home.html”.

When the content of all the template files are inserted, the string is returned to the bundler.

To use this custom transform, we need to create a custom bundle that uses it:

And finally in the BundleConfig class, set up bundling and transformation of all our template files. This is the complete BundleConfig class:

Now, to display the AngularJS on our remote web page, we insert the following html:

I can now ask the editors of the remote websites to add these two lines of code to their page and the app will load.

Comments or questions?

I am sure there many ways I can improve this code, and I would love to get feedback on it. If anything is unclear, please ask questions in the comment section, and I will try to answer.

2 thoughts on “AngularJS and .NET MVC: Convert templates from html to javascript and bundle app and templates together”

  1. Thanks for the post. I came across a few issues with transforming my HTML pages so I recommend a few minor changes to your code:

    Instead of replacing the “\r\n” character, Individually replace \r and \n

    content = content
    .Replace(“‘”, “\\'”)
    .Replace(“\r”, “”)
    .Replace(“\n”, “”);

    I also added an a replace on the path variable so that I could recursively include a directory.

    path = path.Replace(“\\”, “/”);

    And then in the bundle config:

    bundles.Add(new AngularTemplatesBundle(“app”, “~/bundles/templates”)
    .IncludeDirectory(“~/app”, “*.html”, true));

  2. Thanks a lot for your reply. I changed the \r\n replacement like you suggested.

    I didn’t understand the part where you replace “\\” with “/”.

    Should it not be enough to replace
    .IncludeDirectory(“~/CreditcardApp/Templates/”, “*.html”);

    with
    .IncludeDirectory(“~/CreditcardApp/Templates/”, “*.html”, true);

    (the final “true” meaning the IncludeDirectory method should also search through subdirectories)

Leave a Reply

Your email address will not be published. Required fields are marked *