Brian Vander Plaats

Developer

Blog

16 Jan 2017

Understanding Relative Urls

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 element specifies the base URL to use for all relative URLs contained within a document.”

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.

07 Dec 2016

Default Startup Project in Visual Studio

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.

17 Oct 2016

Converting JSON Types

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:

  • input file must be utf-8
  • null values converted to string type get set to blank values.
  • strings of "null" are converted to null for int or float types.
  • you may specify any number of inputs fields.
  • Only supports simple file structures, no nested arrays inside the JSON objects.

gist

11 Oct 2016

Angular2 Testing Introduction

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.

Why Unit Testing

According to the documentation, Unit Testing as three main purposes:

  1. Guard against regressions in code. Changing code in an established application is frequently a source of bugs, and having a battery of tests available can alert the developer to issues quickly (assuming there is a test that covers that particular issue)
  2. Describe how services, pipes, and components should operate. Even if you are good at reading code, it is not always easy to visualize what inputs and outputs should look like.
  3. Expose design flaws in component design. If it is hard to write a test for a component, it is a strong sign that the component has design flaws, such as doing too many things, containing unnecessary dependencies, etc.

Jasmine

Jasmine is a unit testing framework for JavaScript that the Angular team recommends using.

  • In Jasmine, a test is known as a “spec”.
  • Specs are stored in a .spec.ts file. Each component/service/pipe that has tests should have their own spec file, e.g. App.component.spec.ts.
  • specs are defined with the global Jasmine function it()
  • Multiple specs for the same component are known as a “suite”. A suite is defined with the global jasmine function describe()
  • The purpose of a spec is to define an assertion about what a component should do, or be. An assertion is either true or false. for example, an assertion may be: When we pass 2 to the function square(), the result will be 4
  • To create an assertion in Jasmine, use the expect() global function, inside an it() function.
  • Jasmine defines a large number of “Matchers” to be used with expect()
    • toBe()
    • toEqual()
    • toBeUndefined()
    • toBeTruthy()
    • toContain()
    • etc.

Example Test

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);
    });
});

Karma

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

Jasmine + Angular

By itself, Jasmine knows nothing about an Angular application, so we need to use an Angular test infrastructure to make useful unit tests.

  • Need to import all components/services that the test/spec(s) will be using
  • use the global Jasmine function beforeEach() to initialize components. beforeEach() runs before every individual it() execution.
  • some items such as pipes and services do not depend on the angular environment running, and can be tested with little setup in Jasmine, besides instantiating the class.
  • the primary class for interacting with the Angular environment is the TestBed class. This class is responsible for:
    • Instantiating the component that will be tested via TestBed.createComponent(). this will return a special ComponentFixture class that contains the component instance as well as a few other testing properties
    • configuring the angular environment via TestBed.configureTestingModule()
    • querying the components template + rendered DOM via debugElement and nativeElement properties of the ComponentFixture. Note that these elements are hierarchical i.e. debugElement can contain one or more child debugElements
    • provide access to the component’s properties & methods via fixture.componentInstance. Note that the properties will not be defined until fixture.detectChanges() is run.

Example Component Test

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();
  });
});

Demo

04 Oct 2016

An Initial Look at RxJS Observables

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.

Angular 1 $http.get() using Promises

$http({
  method: 'GET',
  url: 'http://example.com/api/getStuff'
}).then(function successCallback(response) {
   //do stuff
  });

Angular 2 http.get() using Observables

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:

jsfiddle.net/brnvndr/eakrb004/

Observables can be created automatically from many sources:

  • Rx.Observable.from() - from an array or collection
  • Rx.Observable.fromEvent(<target control>, <event name>) - from a DOM event
  • Rx.Observable.fromPromise() - even from promises

Additionally, 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 returned

A 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.