This is probably obvious for Angular aficionados and aficionadistas but I found how to copy static files into the output directory of an angular application non-obvious.
To do it go to angular-cli.json in your project root and add the static file to the assets collection thus
And it places it nicely into the output folder which is dist by default. I found this useful when using a web.config file to set up some URL rewriting rules in an IIS hosting scenario i.e..
Just though I would break it out of that post and flag up how to do it when I had a spare 10 mins between cleaning the bathroom and taking the daughter to swimming lessons (too much information – sorry).
I recently had cause to put an Angular application a IIS sub application under an existing .Net website so effectively they share the same host url. It took me longer than it should have done – I made heavy weather of it. Some issues were just fiddly and took some working out. Some issues I had were the result of my own sub-optimal performance in this technical area (stupidity). It was Saturday afternoon so I’m allowed sub -optimal performance in my own time.
Tutorial
There is already a good tutorial here. So I’m not proposing to go through it step by step, Instead I’ll quickly outline the basic steps then point out where I went wrong. I suffer so you don’t have to.
Motivation
Just briefly – my motivation for doing this was to have Angular running under the same address as the API it was calling.
So
http://demo-api:4200/
Is called by an angular app under a virtual directory at
http://demo-api:4200/Angular
This is to avoid Cross Origin Scripting Issues in my own environment. I know there are other ways to do this – but they either
Never worked
Were too invasive to implement on the API side (for a spare time demo project)
Did work then frustrating stopped working
So I bit the bullet and moved Angular under the API project where it is always going to work for me i.e. they use the same host url so are no longer cross origin.
Create the Sub Application
So very briefly – ceate a sub application under main application to get this configuration
Point virtual directory to Angular app and browse. Boom – It doesn’t work!
Error 1: Using a .Net App pool
First issue is this yellow screen of death
The exact error will vary but essentially the angular application complains that it is missing assemblies, handlers, modules etc… It wants to be a .Net application. It doesn’t need to be.
When setting up the new application the default will be a .Net integrated application pool
To stop it behaving like a .Net app – create a new app pool with .Net CLR version of ‘No Managed Code’
Assign this to the new angular application in IIS and the site is no longer a .Net wannbe. The YSOD is gone.
Error 2: Pointing to the wrong folder
No yellow screen of death, instead the application is entirely blank. This is where I was being particularly sub-optimal. So I pointed it to my angular application at
{MyAngularApplication}\src
This is never going to work. Angular builds into this directory by default
{MyAngularApplication}\dist
Point the sub application here. Why I thought this would magically work I’ve no idea. Although IIS had stopped behaving like a .Net site, I clearly carried on thinking it was one and pointed to the root folder as I would a .Net site. 15 minutes of my life working this out that I am never going to get back. Oh well.
Error 3: Correcting Angular script references
So run the app again and sadly it is still blank. This time if you look at chrome developer toolbar or similar you will see a lot of 404 errors
What’s going on here is that Angular is still behaving as if it is loading all scripts from the root website. To correct this
As a side note – I always forget these kind of command lines. To avoid the need for additional brain friction use the npm start command to run the ng build command with the extra parameters. So go to packages.json in the root directory of your angular app and change the npm start node under scripts to the build command i.e.
If you omit this step then the web.config won’t be copied over to the dist folder on build. If you add it to the dist folder manually then it will be deleted on build so you won’t be any further forward.
Once all these steps are done, refreshing on a route this will reissue the request up to the base url which will resolve correctly.
Finishing up
Now the angular app runs nicely under a sub application in IIS and my Cors problem is no longer an issue. There are a couple of disadvantages to running it this way
Hot reloading of the application on rebuild doesn’t work
Related to this – the application sometimes caches and a hard refresh of the browser (ctrl, F5) is sometimes needed to kick it back to life.
When refreshing a page it will redirect back to the root so you lose whatever route you are on so
http://demo-app/angular/page1
Refreshed becomes
http://demo-app/angular
Which may or may not be what you want
These are probably resolvable but when I got my development environment to this point, I declared loudly to all with ears to hear – Job done!
And it’s a good one. Pitched at the right level, good quick start chapter so you can see how it all fits together early on and a nice primer on TypeScript in chapter 2. So, how much for hundreds of pages of Angular goodness delivered to a Kindle device? Well to you Madam/Sir, it is zero pounds, shillings and pence or nada dollars if you prefer. Oddly, if you are trying to get this on Australian Amazon then you are out of luck – clearly the bits and bytes can’t travel that far (though a VPN connection might help in that circumstance).
But Amazon Australia not withstanding, this book is worth the money. I’d even go further – this book would be worth the money if Mr Hussain ever decides to charge for it. Thank you Asim.
I want to truncate text in Angular 5 using a pipe. I don’t just want to chop the text up inbetween words. I want to truncate to the nearest whole word. And I want a ellipses on. I want it all. So the following text as an example
Mind precedes all mental states. Mind is their chief; they are all mind-wrought. If with an impure mind a person speaks or acts suffering follows him like the wheel that follows the foot of the ox.
Truncated to a length of not more than 150 would be
Mind precedes all mental states. Mind is their chief; they are all mind-wrought. If with an impure mind a person speaks or acts suffering follows…
I want that – and as luck would have it is actually a nice quote. Purely coincidental of course.
Starting off
First let’s use angular cli scaffolding to get us the base pipe. So open the command line and run
ng g pipe TruncateText
Or if you prefer
ng generate pipe TruncateText
And the following js file pops out
Import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncateText'
})
export class TruncateTextPipe implements PipeTransform {
transform(value: any, args?: any): any {
return null;
}
}
Nice start. The pipe metadata tells us that to use it in the html page we will be using the truncatetext tag. It’s pretty obvious where our implementation is going to be.
The Implementation
So filling out the implementation gives us
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncatetext'
})
export class TruncateTextPipe implements PipeTransform {
transform(value: string, length: number): string {
const biggestWord = 50;
const elipses = "...";
if(typeof value === "undefined") return value;
if(value.length <= length) return value;
//.. truncate to about correct lenght
let truncatedText = value.slice(0, length + biggestWord);
//.. now nibble ends till correct length
while (truncatedText.length > length - elipses.length) {
let lastSpace = truncatedText.lastIndexOf(" ");
if(lastSpace === -1) break;
truncatedText = truncatedText.slice(0, lastSpace).replace(/[!,.?;:]$/, '');
};
return truncatedText + elipses;
}
}
Value is the string that is to be truncated. Length is our target (maximum) length. It returns a string which is the truncated text. There is a bit of parameter checking then
let truncatedText = value.slice(0, length + biggestWord);
I’ve taken the view that the biggest word I’m likely to deal with is
Pneumonoultramicroscopicsilicovolcanoconiosis
So it’s 45 letters plus 5 for any spelling mistakes for a 50 character margin. (I’m ignoring the 189,819 letter protein name that someone unearthed – I guess biochemists need to use this Angular filter with caution).
So I truncate at my desired length plus 50 then nibble backwards at each space
Taking into account the ellipses, the logic would be that the entire string evaporates to just the ellipse or perhaps nothing. I don’t really want that so this filter will return the first word with an ellipse as the minimum return value i.e.
Mind…
If you want the behaviour where the output strictly adheres to the length or less then it would be something like ..
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncatetext'
})
export class TruncateTextPipe implements PipeTransform {
transform(value: string, length: number): string {
const biggestWord = 50;
const elipses = "...";
if(typeof value === "undefined") return value;
if(value.length <= length) return value;
if(length < elipses.length) return '';
//.. truncate to about correct lenght
let truncatedText = value.slice(0, length + biggestWord);
//.. now nibble ends till correct length
while (truncatedText.length > length - elipses.length) {
let lastSpace = truncatedText.lastIndexOf(" ");
if(lastSpace === -1) {
truncatedText = ""
break;
}
truncatedText = truncatedText.slice(0, lastSpace).replace(/[!,.?]$/,'');
};
return truncatedText + elipses;
}
}
So it will return the length or less in all cases – if you really want that.
Demo App
As ever the code is on my github site here. It’s part of a bigger app and associated API that generates Buddhist quotes from the Pail Canon. All work in progress but I quite like it.
Mind precedes all mental states. Mind is their chief; they are all mind-wrought. If with an impure mind a person speaks or acts suffering follows him like the wheel that follows the foot of the ox.
Mind precedes all mental states. Mind is their chief; they are all mind-wrought. If with a pure mind a person speaks or acts happiness follows him like his never-departing shadow.
It’s said that when learning a new language or framework it is best to approach it fresh, with no preconceived ideas from other languages. So completely ignoring that advice here is my implementation of an ASP.NET web.config file in AngularJS.
I’m used to squirrelling away my config in one external file and I want to do that again in AngularJS. Really it’s the appSettings part of the web.config file that I’m implementing but the point is that all my config for the application is in one file that I can use throughout my application. I like it in .NET and I want to do it again here.
The implementation
App.Config.js
The first step is to implement a config object in an external file like so…
We have just 2 config items at the moment, the root of the API and if it is in debug mode or not. In the normal run of things I wouldn’t want to pollute the global scope with JSON objects. I would wrap them in an IIFE. However this is a special case as it is genuinely global so I’m explicitly placing it in the global scope i.e. using the window object.
As it is an external file I need to remember to reference in the main application page.
<script src="app/config/app.config.js"></script>
App.Module.js
The next task is to reference the config in the main application file – app.module.js in this case. This is how I’m going to make it available throughout the entire application. Strictly speaking I don’t need to do this. As it is on the global scope I could leave it like that and directly reference it via the window object. I’d rather not do that. It’s going to make unit testing problematic in the future and anyway I don’t like it. Instead, I’m going to use the app.constant property in the main app.module file thus
(function () {
app.config(function ($routeProvider, $locationProvider) {
//.. set up the application – routing etc..
});
app.constant("appConfig", window.appConfig);
})();
My naughty accessing of a global object is here and nowhere else. The config object is placed into app.constant which is then available throughout the application via dependency injection. I could have used app.value and it would work in much the same way. However I’m never going to change these values during the lifetime of the application so the constant property is more appropriate.
Consuming the config
The config is then available via dependency injection in controllers, services, directives e.g.
angular.module('app')
.controller('MyController', ['appConfig', function (appConfig) {
//.. app.config is now available
});
In a service
The consumption in a service is the same. The use case here is a service can use the config to set the API url which could, should and will change in different environments.
angular.module('app')
.service('MyService', ['$http', 'appConfig', function ($http, appConfig) {
var serviceRoot = appConfig.apiRoot;
this.get = function(id) {
var url = serviceRoot + "myAPIMethod/" + id;
return $http.get(url);
};
//.. more methods
}]);
In a directive
I’ve previous blogged about a debug panel that can be toggled on and off. In that post I used a query string parameter to turn the display of the debug information on and off. In truth I don’t really like that. This is the debug panel directive implemented with the web.config style config. As before it is injected in and it does a bit of DOM manipulation to remove the panel in the link method if debug is on. I prefer this way rather than my crazy query string implementation from before.
One of the reasons that I like external config files is that I can have different settings in different versions of the files. I can then use some sort of task runner or deployment script to swap them out as it moves through different environments. So I could have the different app.configs in my project i.e.
app.config (standard development one)
app.devtest.config
app.qa.config
app.preprod.config
app.prod.config
I can then delete the app.config file and rename the environment specific file so it is picked up in different environments. So when deploying to QA I would ….
delete app.config
rename app.qa.config to app.config
And as if by deployment magic I get different settings in QA. I currently use a bunch of PowerShell scripts to do this so it’s all automated but it can be done with whatever script or tool is most familiar or fashionable or nearest to hand.
Alternative implementations
As always, I’m not claiming that this is the best implementation for consuming application config. Global scope and config is going to be something that can be done in any number of ways. In fact I can think of several just while sat here typing in my pyjamas.
Global objects
I could just access the config directly wherever I like by using the global windows object like so..
angular.module('app')
.service(‘MyService’, ['$http', function ($http) {
var serviceRoot = window.appConfig.apiRoot;
this.get = function(id) {
var url = serviceRoot + "myAPIMethod/" + id;
return $http.get(url);
};
//.. more CRUD methods
}]);
I personally don’t like having the windows object scattered through my code but it could be done. It’s not going to work well with any kind of unit testing though so unless you’re a no test kind of developer it’s probably best avoided.
Global scope
We could use $rootScope instead of app.constant to stash the external config file there. So accessing it would be a matter of grabbing the config from $rootScope thus
angular.module('app')
.service(‘MyService’, [‘$rootScope’, '$http', function ($rootScope, $http)
{
var serviceRoot = $rootScope.appConfig.apiRoot;
//.. CRUD methods
}]);
I still like the idea of only the config being available in the method. I just get what I need, not the entire rootScope – that said this would work perfectly well.
Log Provider
This isn’t a complete replacement but it’s worth noting that debug config could be implemented through the $logProvider component if we wanted. To toggle on and off we would do this
$logProvider.debugEnabled(false);
It’s not a complete replacement but in the interests of full disclosure there are other (better??) ways to get debug information in and out of angular that have their own config.
Very bootstrappy as usual. Reference the directive on the view thus
<error-display display="vm.Error" />
Happy days – a lovely reusable error panel.
The Problem
Sadly it isn’t to be. The previously functional page becomes entirely blank after the directive.
Unhelpfully there is no errors in the console and viewing the HTML source does nothing for us since it is a single page application.
The Solution
The problem is the use of self closing tags when referencing the directive. Referencing the directive with an explicit closed tag resolves the issue. Example – use this markup
Relief all round. Fortunately I found the solution pretty quickly (though I still continued to ignore the daughter). It’s the sort of error that can wasted hours if it hits on a Friday afternoon when you are feeling generally depleted.
Actually the use of self closing tags isn’t that helpful in HTML5. I’ve got into the habit of using them but looking into it more deeply it’s actually a bad habit. They are valid but optional in HTML5 for void elements but invalid for non-void elements. Since they are either optional or invalid its best to avoid them and write out the tag – particularly as it causes obscure issues like this.
I am aware that there are many many other ways to get debug information from an AngularJS application but that’s not stopped me implementing another one. I do actually find a customised output of JSON objects useful particularly when working with deep object graphs. The implementation also is useful as a worked example of a couple of AngularJS principles.
The Demo Application
The demo application is a bookshelf application that searches the google book API and enables the user to add books to their own library. This is my ‘go to’ demo application that I’ve implemented a couple of times in different frameworks. This one is using the MEAN stack and the source code can be found here.
The Problem
When I search the google API I get a very large complex JSON object as the output. I want to know what is in there as I’m saving bits of it in my own database and might be interested in other bits. I want it displayed but easy to toggle on and off. I’m ultimately going to read something from the query string to do this as it is easy to toggle with minimal fuss.
When it’s finished it’s going to look like this…
Google Bool API results displayed in debug panel
I’m going to use bootstrap for styling throughout as I’m not hugely concerned about how it looks – it’s just got to be reasonably neat. So its bootstrap classes throughout I’m afraid.
The Solution
The solution is going to demonstrate the following
The use of directives in AngularJS
Using global values and config
Setting globally accessible values from the query string.
Step 1: Implement a debug panel
The initial implementation is directly on the search page. When I do a search I want to see the following debug output
Debug panel displayed on book search page
Before I do a search I don’t want to see anything.
The implementation is
<!— book details page -->
<div class="row" ng-show=" searchResults ">
<div class="col-md-8">
<hr />
<pre>{{searchResults | json}}</pre>
</div>
</div>
<!— rest of book details page -->
The scope has a property SearchResults with a giant json object direct from the GoogleAPI. I want to show this. I use the json filter to prettify the result and it’s wrapped in pre tags so it displays like code. The ng-show attribute hides it if the searchResults is null or generally falsy which is what I want. That’s pretty good and works for me.
Step 2: Make it a directive so it can be reused
Now I want this on every page and I want it to display anything I want not just the searchResults. I want it to be generally useful. To achieve this I’m going to use directives which allow me to include fragments of HTML across different parts of my site. I now want it on the book details page as well.
The first job is to extract out the debug panel into a separate html file and to remove the dependency on searchResults. The debug panel html is extracted and saved into its own file at
This is the contents of the entire file now. I’ve removed the binding to searchResults and I have now got a property called ‘display’. This is any JSON object I want to display in my results panel.
Next job is to tell my angular application about my new directive. So I create a new JavaScript file and put this in
This does a few things. It tells angular where to find the html fragment for display via the templateUrl property. More interesting it also uses scope isolation to feed in the JSON object I want to display.
scope:
{
display : "=display"
},
The scope property is the link between the page and the directive. I can insert my parent page scope into an attribute called display and this becomes available to the directive. It breaks the hardcoded dependency on the parent scope and makes it all more flexible. The ‘=’ in ‘=display’ tells angular it is an attribute.
That’s probably not hugely clear but finishing the example should help us. The last step is to reference the directive on the book details page. I need to remember to include the script
Then the directive is referenced as markup which looks quite neat
<debug-display display="vm.book"></debug-display>
So the display attribute is bound to the book property which is then fed through to the debug panel and displayed. I can put anything into it not just searchResults as before.
Debug panel displayed on book details page
I can now go back to the search page and implement the directive in the same way.
And that will display my search results in prettified json which I quite like. It’s even better now and I am mentally slapping myself on the back. However I’m not finished. I don’t always want to see the debug panel. Sometimes I just want to see the page as it will be when it goes live with no debug information at all. I want to be able to toggle it on and off with ease.
Step 3: Using a global setting to toggle the debug panel
I have ambitions for my BookShelf demo site to be truly huge; a site which will make eBay look small. To that end I don’t want to have to turn my debug panel on and off manually everywhere it is used. I want one point of control.
I’m going to set up a DebugOn flag when the application initialises. For this I could use a global constant like so
app.constant('DebugOn', 1);
However (spoiler alert) I’m going to want to amend them later on so a global value is more the way to go. I’m setting this when the app initialises so it will be
(function () {
'use strict';
var app = angular.module('app', ['ngRoute', 'infinite-scroll']);
// routing and other set up
app.value("DebugOn", 1);
})();
I can use the built in dependency injection to access the global variable in any service, filter etc.. so I’m going to use that to grab it in the directive script …
The directive has become a bit more complex but it’s not too terrifying. I’m taking the DebugOn value as a DI injected parameter – very standard Angular stuff. I want to hide the debug panel if it is set to 0. In essence I want to change the DOM if the DebugOn variable is 1 and the link property enables us to do that.
The elem parameter is a collection of all the directive element outputs in the app at that time. For the debug panel it will be one but I will be good and iterate through it anyway – we just obliterate the innerText and thus hide them from the user.
Now we have done this turning the debugging panel off becomes a simple matter of amending the value of DebugOn variable in one place and restarting the application.
app.value("DebugOn", 1);
Just what I wanted.
Step 4: Using the query string to toggle the debug panel
But it’s not enough. It never is. I’m not satisfied with amending the application – I want to just pop something on the query string, have my application pop into debug mode and have my debug panels spring into life like desert flowers in the rain. I want it and I can have it.
http://myapplication#angularstuff?DebugOn=1
Should turn on the debugger but as I’m navigating around my single page application my querystring is going to disappear. I could maintain it but I don’t want to – it seems like hard work. So I’m just going fall back to some good old fashioned JavaScript. The full url is available in
window.location.href;
So I’m going to use a couple of functions to grab the value and put it in the global variable
function getDebugParam(){
var debugParam = getParameterByName("Debug");
if(!debugParam) debugParam = 0;
return debugParam;
}
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
And the setting of the debug variable becomes
app.value("DebugOn", getDebugParam());
And done. I done even need to change my app to get the debug panels on. A quick fiddle with my querystring and there they are. Lovely.
Further Development
As much as I like the query string idea I wouldn’t want to see it in production. As an immediate project I would want to disable this in prod environments. It’s not hard but needs to be done. I’ve got other ideas around multiple panels, debug levels and improved display but I’ll leave those for another day.
That’s it – end of post other than notes. Happy New Year everyone for 2017.
Full Script
There is a bunch of code here so to aid the discerning coder – this is the all code needed for the debug panels
function getDebugParam(){
var debugParam = getParameterByName("Debug");
if(!debugParam) debugParam = 0;
return debugParam;
}
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
app.module.js
(function () {
'use strict';
var app = angular.module('app', ['ngRoute']);
//.. other setup such as routing
app.value("DebugOn", getDebugParam());
})();
Note about Immediately Invoked Function Expressions
I haven’t wrapped all my examples in an IIFE. This is just to save space and reduce nesting in the examples. Just mentally put them back in if they are absent i.e.
I’m currently tinkering around with Angular JS. I’ve always liked JavaScript and since there is currently one or two (thousand) JavaScript frameworks around I thought I would have a go at the Angular. It wasn’t immediately obvious how to cleanly concatenate strings for display so I wrote a filter and it works quite nicely. As ever I’m not making any claims for this to be technically brilliant but it does work, I wrote it with my own coding fingers and I like it.
The Demo
The demo system for this is a simple search for books using the Google Books API. The results display on an infinite scroll. That’s totally irrelevant to this but again I like it. The demo project code is on github at https://github.com/timbrownls20/BookShelf.MEAN.Stack
The Problem
Books have multiple authors. The Google API returns them in an array i.e.
"volumeInfo": {
"title": "Baptizing Harry Potter",
"subtitle": "A Christian Reading of J.K. Rowling",
"authors": [
"Luke Bell", “Another Author”
]}
We display them on the view with a simple binding
<h4>{{item.volumeInfo.authors}}</h4>
But unfortunately the display looks rubbish
The Solution
We could display them correctly by putting a bit of looping logic in the view but I see enough abuse of views in my day job (and I’m guilty of some of it myself) so I would rather not continue view abuse in my spare time. So let’s whip up a filter. It will be cleaner and reusable.
To implement create a file called filters.js and pop in this
(function () {
'use strict';
angular
.module('app')
.filter('stringConcat', function () {
return function (input, delimiter) {
if (input) {
return input.join(delimiter)
}
else {
return '';
}
};
});
It’s embarrassing straightforward. An array and a delimiter in, join and return. We added it directly into our angular application which is imaginatively called ‘app’. It’s then available to be called in the view with a PowerShell style pipeline i.e.