Developer
Something you use all the time, but don’t always realize, is how a web browser will request resources in html files with relative url’s.
for example:
<script src="libs/jquery.min.js" type="text/javascript"></script>
will be served from example.com as http://example.com/libs/jquery.min.js
- provided the website is sitting in the web server root e.g wwwroot
or /var/www/html
.
If, instead, the app is sitting in a sub-folder / virtual directory, the above code will still work, even thought it may now come from http://example.com/salesapp/libs/jquery.min.js/
. This is great, as it lets us decouple our application structure from where it is deployed.
What if we changed the URL a bit? say to:
<script src="/libs/jquery.min.js" type="text/javascript"></script>
With this change, the leading /
indicates the root of the website. It is still a relative path, but it is “relative” to the root of the website. http://example.com/libs/jquery.min.js
will still work, but http://example.com/salesapp/libs/jquery.min.js
will not. This can be a subtle error, as most applications run locally are sitting at http://localhost:12345
or whatever, but many are not deployed to the web root.
Using the leading slash removes a lot of future flexibility. So why would you want it? Well, consider this scenario. You have a script file that has a jquery dependency, which lives at http://example.com/salesapp/scripts/accounts.js
. This reference will no longer work:
<script src="libs/jquery.min.js" type="text/javascript"></script>
It will return as http://example.com/salesapp/scripts/libs/jquery.min.js
oops.
The reason this doesn’t work is that the relative path is build based on your current window location, which will change with different url’s:
http://example.com
http://example.com/salesapp
http://example.com/salesapp/accounts
It will have to be one of these:
<script src="/libs/jquery.min.js" type="text/javascript"></script>
<script src="../libs/jquery.min.js" type="text/javascript"></script>
<script src="http://example.com/libs/jquery.min.js" type="text/javascript"></script>
Out of the three, the 2nd is probably the most flexible, but is cumbersome to read and write (especially if your source trees are deep).
What you really want to do is set your url relative to the path of the application. This can be using the <base>
tag. According to mdn
“The HTML
For example, if you set up the <base>
tag as follows:
<head>
<base href="/">
</head>
This effecively sets all relative paths relative to the root of the website
libs/jquery.min.js
will be read as /libs/jquery.min.js
, and thus http://example.com/libs/jquery.min.js/
To set an application specific path, you could use:
<head>
<base href="/salesapp/">
</head>
libs/jquery.min.js
will be read as /salesapp/libs/jquery.min.js
, and thus http://example.com/salesapp/libs/jquery.min.js/
this works well when deploying to different endpoints as only the <base>
reference needs to change, which can be easily handled via a build system.
Be careful though, specifing relative paths with the leading slash will ignore the specified <base>
.
Lastly, you can also specify an empty/current directory base path
<head>
<base href="">
</head>
or
<head>
<base href="./">
</head>
Which is effectively the same as specifying no <base>
. You might use this for local development.
Additionally, you can dynamically write the <base>
with a script, but doesn’t appear to be a recommended approach. It seems better to leave this up to your build / depolyment system.
The good news is that broken paths are generally easy to detect when your browser is in development mode - just look for the red requests in the network tab, and check the path. You’ll know something is off when the path is partially correct, but contains additional levels or missing levels altogether.
Had an interesting error the other day. While reviewing an interns test project, I noticed that on a fresh clone of his repo, his website errored out on startup. For our projects we typically have a project for the website, and another for the business classes. For some reason, Visual Studio was trying to run the business project as the website.
This is easily fixed right-clicking the web project and selecting set as startup project
, but this setting is stored in the .suo
file which is not checked in. So why does this work in our other projects?
Doing some googling, came across this stackoverflow answer. It turns out that Visual Studio looks at the order of the projects in the solution file. Somehow, my intern added the business project first - I don’t think anyone has done that before.
All you need to do is manually edit the .sln
file in a text editor, and move the “startup” project to the top of t he Project
/ EndProject
blocks
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySiteApp", "MySite.App\MySiteApp.csproj", "{F5C8B11C-FF25-45B6-8F25-B1C779D4A053}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySiteBusiness", "MySite.Business\MySiteBusiness.csproj", "{E9561B89-36A0-43C2-B9BA-970843316D23}"
EndProject
On a related note, it seems like Microsoft can’t pick a file format and stay with it. the .suo
file is binary, .config
files are xml, and the .sln
file is basically just simple keys and values.
In a post last year, I found a simple csv to JSON technique that worked well - but today I noticed a significant limitation - all numbers were converted to strings, which isn’t very useful for setting up mock data for TypeScript
.
I created the simple node.js
file below to convert an existing JSON file (using the technique above), by specifying one or more values to convert in the file. It’s not especially robust, but it works for my current needs. A few comments:
utf-8
"null"
are converted to null
for int or float types.Looking at unit testing in Angular2, following the offical docs. Unit Testing is a large topic, so this is just an initial look and summary of what I’ve learned so far.
According to the documentation, Unit Testing as three main purposes:
Jasmine is a unit testing framework for JavaScript that the Angular team recommends using.
.spec.ts
file. Each component/service/pipe that has tests should have their own spec file, e.g. App.component.spec.ts
.it()
describe()
expect()
global function, inside an it()
function.expect()
describe('Math Tests', () => {
it('Math.pow(2,32) should be 4294967296', () => {
expect(Math.pow(2,32)).toBe(4294967296);
});
it ('Math.sqrt(64) should be 8', () => {
expect(Math.sqrt(64)).toBe(8);
});
});
Jasmine tests need to be run to see if they succeed or fail. Karma is a unit test runner the Angular team recommends. Karma runs via the node command: npm test
. It launches a browser instance that hosts the tests, and can be configured to run all tests automatically after application code is updated.
After each test run, the results will output in a command window (not the browser). While a bit verbose, you will be able to clearly see if any tests have failed.
I’m not using karma for the demo code below, but it is a part of the angular-cli environment
By itself, Jasmine knows nothing about an Angular application, so we need to use an Angular test infrastructure to make useful unit tests.
beforeEach()
to initialize components. beforeEach()
runs before every individual it()
execution.TestBed
class. This class is responsible for:
TestBed.createComponent()
. this will return a special ComponentFixture
class that contains the component instance as well as a few other testing propertiesTestBed.configureTestingModule()
debugElement
and nativeElement
properties of the ComponentFixture
. Note that these elements are hierarchical i.e. debugElement
can contain one or more child debugElement
sfixture.componentInstance
. Note that the properties will not be defined until fixture.detectChanges()
is run.import { TestBed, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import {SampleComponent } from './sample.component';
let comp: SampleComponent;
let fixture: ComponentFixture<SampleComponnent>;
describe('SampleComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ SampleComponent ],
});
fixture = TestBed.createComponent(SampleComponent);
comp = fixture.componentInstance;
});
it ('Should create the component', () => {
let app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it ('H1 title should be: Sample Component', () => {
let de = fixture.debugElement.query(By.css('h1'));
expect(de.nativeElement.textContent).toEqual("Sample Component");
});
it ('component property should be 25', () => {
fixture.detectChanges(); // the component does not initialize until detect changes happens
expect(comp.componentProperty).toEqual(25);
});
it ('component component property should be undefined before initialization', () => {
expect(comp.componentProperty).toBeFalsy();
});
});
A major change in Angular2 from Angular1 is a transition from using promises
to observables
in the implementation of the new Http
angular module. You can listen to angular team member Rob Wormald discuss this change in an April 2016 talk.
Observables are part of ReactiveX. This is a major cross-platform set of libraries designed to work with several languages. The version Angular uses is called RxJS
, also known as Reactive Extensions for JavasScript.
$http({
method: 'GET',
url: 'http://example.com/api/getStuff'
}).then(function successCallback(response) {
//do stuff
});
Observable<string[]> observable = this.http.get('http://example.com/api/getStuff')
.map(res: Response => res.json() as string[]);
var observer = observable.subscribe(function(data){
//do stuff
});
Both Promises and Observables are mechanisms for responding to asynchronous events. Promises are designed to retrieve data exactly once. Once the .then()
fires, we’re done. Observables are different. They have the ability to send data multiple times. Notice in the above example we “subscribe” to the observable. This is the equivalent of the .then()
handler in a promise. However, the observable can call this handler multiple times!
Here’s an example of how this works:
First, we create an Observable
var source = Rx.Observable.create(function(observer){
var itemsToEmit = 5;
var itemsEmitted = 0;
while (itemsEmitted < itemsToEmit){
observer.onNext(Math.floor(Math.random() * (101-1) + 1));
itemsEmitted += 1;
}
observer.onCompleted();
})
The observable is where the async operations happen. At some point, the Observable needs to pass along some information to the observer
. This is done with the .onNext()
function. Notice that this is called multiple times. When this happens we say that we are emitting data to the observer. It’s the observer’s job to decide what to do with this data, which is handled in the subscription:
source.subscribe(
function(emittedValue){
document.getElementById('subscriptionOneOutput').innerHTML += emittedValue + ', ';
});
Each time .onNext()
is called, the function(emittedValue)
handler is called. The observer defines three event handlers for .onNext()
, onError()
, and onCompleted()
. We can pass these inline to a .subscribe()
, or create the observer directly:
Rx.Observer.create (function(emittedValue){
document.getElementById('subscriptionTwoOutput').innerHTML += emittedValue + ', ';
}, // onNext()
{}, // onError()
function(){
document.getElementById('subscriptionTwoOutput').innerHTML += '... done!';
}); // onCompleted()
source.subscribe(observer);
The .onError()
and .onCompleted()
both pass information to the observer, but once they fire the Observable does not emit any more data. The observer does not need to define handlers for these events.
You can try this out in this fiddle:
Observables can be created automatically from many sources:
Rx.Observable.from()
- from an array or collectionRx.Observable.fromEvent(<target control>, <event name>)
- from a DOM eventRx.Observable.fromPromise()
- even from promisesAdditionally, Observables define methods for manipulating the data returned to the observer:
.map( x=> 10 * x)
- transform data. this is very useful for parsing HTTP response to get the data payload.filter(x => x > 2)
- limit data returnedA good place to start reviewing Observables is in this online book, but be warned, this is a very deep topic. You don’t need to understand all of this to use Observables in Angular, but understanding Observables outside the context of Angular will enable you to use them more effectively.