Adding Route Resolve to UI-Router Hello World

Last update 17 Nov 2017.

Previous Step, Hello World

This is my notes when I tried to follow the UI-Router Hello Solar System Tutorial using angular CLI.

This time I tried to add resolve to a route.

First let’s create a component called People:

ng generate component People

Add route definition for PeopleComponent:

{
  name: "people",
  url: "/person",
  component: PeopleComponent
}

Add a link to navigate to people in app.component.html:

<a uiSref="people" uiSrefActive="active">People</a>

We want to display a list of people in this PeopleComponent, and the list of people should come from a HTTP server, and we are not going to get the data during component initialization, but from resolve step when user tries to navigate to people component.

Now we need a mock HTTP server that dispenses JSON. Let’s create a file called people.json in src/assets. This file will be served as static file from http://localhost:4200/assets/people.json.

Fill the file with some random content:

[
    {
        "ID": "293",
        "IsActive": false,
        "EyeColor": "brown",
        "Name": "Ingrid Townsend",
        "Company": "JASPER",
        "Email": "ingridtownsend@jasper.com",
        "Address": "690 Charles Place, Santel, Northern Mariana Islands, 3791"
    },
    {
        "ID": "581",
        "IsActive": true,
        "EyeColor": "blue",
        "Name": "Estrada Nolan",
        "Company": "FIBRODYNE",
        "Email": "estradanolan@fibrodyne.com",
        "Address": "317 Seeley Street, Cade, Maryland, 3976"
    }
]

Now let’s create a service to get data from our brand new API:

ng generate service service/People

Note that the service will be put in folder src/app/service.

Add dependency to HttpClient in the service’s constructor. HttpClient is available from @angular/common/http.

Then add a function to get the data:

GetPeople() {
  return this.http.get("/assets/people.json");
}

Actually we can do better. We should take advantage of Typescript’s static typing by binding the response to a defined class.

Let’s create a class to contain a person data:

ng generate class service/Person

We should define the class to match the API’s response:

export class Person {
    ID: number;
    IsActive: boolean;
    EyeColor: string;
    Name: string;
    Company: string;
    Email: string;
    Address: string;
}

Then we modify the service to automatically bind the response to Person[]:

GetPeople() {
  return this.http.get<Person[]>("/assets/people.json");
}

Don’t forget to add HttpClientModule in imports of app.module.ts. It should be after BrowserModule. We should also add PeopleService in providers.

Now we are ready to add resolve to people route definition:

{
  name: "people",
  url: "/person",
  component: PeopleComponent,
  resolve: [
    {
      token: "ResolveDataPeople",
      deps: [PeopleService],
      resolveFn: (peopleSvc: PeopleService) => peopleSvc.GetPeople().toPromise()
    }
  ]
}

Note the token name. It should match the component property name that will receive it.

The resolveFn should have parameters matching the list of dependencies in deps. Match by position.

Also, resolveFn should return promise, not Observable, while HttpClient returns Observable, so don’t forget to convert to promise.

All that is left is to configure the people component. To capture data from resolve, add a property with name matching the resolve token:

@Input() ResolveDataPeople: Person[];

The property should have the @Input() attribute on it.

Then display it in the template:

<ul>
  <li *ngFor="let person of ResolveDataPeople">
    {{person.Name}}
  </li>
</ul>

Resolve successfully added!

Try to navigate to http://localhost:4200/#/person, it should display the list of people.

Later if you have trouble building the app:

ng build --prod --build-optimizer

If it produces error like: Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, that’s because angular 5’s AOT compiler doesn’t like arrow function in the router definition.

You can replace the resolve:

resolveFn: (peopleSvc: PeopleService) => peopleSvc.GetPeople().toPromise()

with:

resolveFn: ResolvePeople
...
export function ResolvePeople(peopleSvc: PeopleService) {
  return peopleSvc.GetPeople().toPromise();
}

Or you can tell angular not to use AOT compiler:

ng build --prod --aot false

Comments


UI-Router Hello World Tutorial using Angular CLI

Last update 16 Nov 2017.

This is my notes when I tried to follow the UI-Router Hello World Tutorial but using angular CLI.

As of 16 November 2017 the above tutorial uses SystemJS. Since I am not familiar with SystemJS when I tried the tutorial I had some trial and errors before I made it work with angular CLI.

I used Angular CLI version 1.5, Angular 5.0.1, UI-Router 1.0.0-rc.0, Typescript 2.4.2, Node.js 8.9.0, NPM 5.5.1 in Windows 7 Pro using Visual Studio Code 1.18.0.

If you haven’t installed Angular CLI, install it:

npm install -g @angular/cli

First step is to generate a new project:

ng new HelloWorld --routing

The --routing parameter will create a separate module for routing, which I think is nice.

If you are using Windows and encounters error when npm is installing modules, and the error message says something about fsevents, it is a known issue that will be fixed in the future. In the mean time you can separate the creation of the angular project with the installation of npm module like this:

ng new HelloWorld --routing --skip-install
cd HelloWorld
npm install

Sometimes it helps, but sometimes you will encounter the same issue during npm install. You can repeat npm install until you don’t encounter the fsevents error.

Then add UI-Router:

npm install @uirouter/angular

Next step is to edit app-routing.module.ts in /src/app. From:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

into:

import { NgModule } from '@angular/core';
import { RootModule, UIRouterModule } from '@uirouter/angular';

const rootModule: RootModule = {
  states: [],
  useHash: true
};

@NgModule({
  imports: [UIRouterModule.forRoot(rootModule)],
  exports: [UIRouterModule]
})
export class AppRoutingModule { }

Later you will add UI-Router state definitions in the states: [] array.

Next step is to modify the app.component.html in /src/app, change:

<router-outlet></router-outlet>

into:

<ui-view></ui-view>

Now you are ready to create your first route definition. First create a component:

ng generate component Hello

Then add a route definition in app-routing.module.ts:

...
const rootModule: RootModule = {
  states: [
    {
      name: "hello",
      url: "/hello",
      component: HelloComponent
    }
  ],
  ...

Don’t forget to add reference at the top:

import { HelloComponent } from './hello/hello.component';

Now if you run the dev server:

ng serve

And navigate to http://localhost:4200/#/hello, you will see hello works! at the bottom. That’s the content of the hello.component.html if you haven’t noticed.

Add another component called about:

ng generate component About

Add route definition:

...
import { AboutComponent } from './about/about.component';
...
    {
      name: "about",
      url: "/about",
      component: AboutComponent
    }
...

And when you go to http://localhost:4200/#/about, you will see about works! at the bottom.

Now modify app.component.html, add:

<a uiSref="hello" uiSrefActive="active">Hello</a>
<a uiSref="about" uiSrefActive="active">About</a>

uiSref is a directive to generate link to a certain state.

uiSrefActive is a directive to add specified class to the tag if it detects the specified state is active.

Now you can switch back and forth between the two states by clicking the Hello and About links.

And that’s it for the Hello World Tutorial!

Next Step: Adding Route Resolver

Comments


SQL Server's Read Committed Snapshot Isolation

Last update 10 Sep 2017.

I got familiar with Oracle and PostgreSQL databases before I got familiar with SQL Server. When I started using SQL Server I wondered why a condition that would be fine in PostgreSQL would cause problem in SQL Server. Here is an example.

First let’s set up the environment.

create table Product(
    ID      int,
    Code    nvarchar(20),
    Price   bigint
)

insert into Product(ID, Code, Price) values(1, 'A', 200)
insert into Product(ID, Code, Price) values(2, 'B', 100)

Suppose there are two active sessions. One of them is starting first, running this:

begin transaction

-- pretend this is an update process that runs a long time
update Product set Price = 300 where ID = 2

-- should have commit at the end but not yet there

While the other session is running this:

select * from Product where ID = 1

In PostgreSQL the select query would return immediately while in SQL Server it would wait until the update is committed before returning.

This would cause problem if we need to generate reports when the database is actively used for transactions. Either the report would take a very long time to finish or the transactions would have to wait for the report to finish. This last one can be really bad on a busy day.

Back then I found several workarounds from the internet. One suggestion is to use the with (nolock) hint. So the select query becomes:

select * from product with (nolock) where ID = 1

That would work in this case, but the select query would do something called “dirty” read, which in this case would include data modifications from other sessions even if the transaction is not yet committed. Continuing our example, if the first session is running:

begin transaction
update Product set Price = 300 where ID = 2

-- select query from second session would start here

update Product set Price = 500 where ID = 2
commit

And the second session starts between the first update and the second update:

select * from product with (nolock)

It would show the price of product 2 as 300 instead of 100 if the transaction on the first session is rolled back or 500 if the transaction is committed.

By the way PostgreSQL would show 100 as the price of product 2.

Also, sprinkling nolock hint all over the source code is considered bad practice by a lot of people.

Another workaround I found from googling was to create archive database. So we create a second database, copy the data from the original database to this second database periodically, and all report generation should use the second database, leaving the first database dedicated for transaction. This is, of course, a MUCH more complicated solution that requires a lot of effort.

What I found next was the REAL solution.

It turned out SQL Server actually has a feature called read committed snapshot isolation, which by default is not turned on! After I turned on snapshot isolation, SQL Server behaved just like PostgreSQL on situation like above.

With read committed snapshot isolation switched on, SQL Server will save the original data in tempdb when a transaction starts modifying data, and if another session sends a select query, as long as the transaction in the first session is not yet done, SQL Server will return the unmodified data from tempdb. This is oversimplification of course. Better consult the official documentation if you want to know the exact mechanism.

Bottom line is, select queries and data modifications no longer block one another.

Read committed snapshot isolation can be turned on like so:

-- kick out all other users with 2 minutes grace period
alter database mydb set single_user with rollback after 120 seconds

-- turn on read committed snapshot isolation
alter database mydb set read_committed_snapshot on

-- allow everyone else to reconnect
alter database mydb set multi_user

To check whether read committed snapshot isolation is turned on:

select name, is_read_committed_snapshot_on from sys.databases

If you are creating a new database, I strongly suggest turning on read committed snapshot isolation from the beginning. But for a database in production, you’d better learn more about it before turning it on since for certain condition update result will be different.

Comments


Mengurus Akta Kelahiran yang Hilang 2017

Last update 22 Aug 2017.

Catatan pribadi pada saat saya harus membuat ulang akta anak karena aslinya hilang, barangkali di masa depan hilang lagi :)

Saya mencoba mengurus di Suku Dinas Kependudukan dan Catatan Sipil Jakarta Barat tetapi ternyata tidak bisa karena akta nya tahun 2007. Untuk tahun 2007 harus ke Dinas Kependudukan dan Pencatatan Sipil Provinsi DKI Jakarta.

Yang harus dibawa:

Tidak butuh meterai 6000.

Membuat surat pernyataan kehilangan bisa di polsek (Kepolisian Sektor). Waktu itu saya membuat di Kepolisian Sektor Tanjung Duren karena lumayan dekat dengan kantor DisDukCaPil nya. Biaya pembuatan surat hilang menurut polisinya ‘terserah’.

Menurut petugas waktu proses 2 minggu lebih. Saya menyerahkan berkas tanggal 22 Agustus 2017 dan diberi semacam kuitansi untuk mengambil dokumen waktu sudah jadi. Menurut petugas saya bisa kembali untuk mengambil akta yang baru tanggal 7 September 2017. Petugas memberikan nomor handphone yang menurut dia bisa ditanya melalui whatsapp apakah dokumen sudah bisa diambil (untuk menghindari kita datang tetapi dokumen belum bisa diambil), tetapi saya coba tanya tanggal 6 September 2017 melalui whatsapp tidak mendapat jawaban.

Tanggal 7 September 2017 saya datang ke kantor catatan sipil. Ambil nomor antrian. Waktu dipanggil menyerahkan kuitansi. Disuruh menunggu sebentar, kemudian dipanggil dan diberi dokumen akta yang baru. Saya disuruh fotokopi selembar untuk keperluan arsip mereka. Tempat fotokopi ada di kantin di belakang gedung. Saya fotokopi satu, kembalikan ke petugas, oleh petugas disuruh tanda tangan dan tulis nama dan nomor handphone di fotokopi, dan urusan selesai. Tidak dimintai biaya.

Comments


The Many Ways to Call Linq SelectMany

Last update 27 Jul 2017.

I just found out c#’s Enumerable.SelectMany has 4 overloads! I only knew one.

Now figuring out the differences.

Setting up a playing field:

public class Team
{
    public string Name { get; set; }
    public IEnumerable<string> Members { get; set; }
}

public class MatchResult
{
    public Team Team { get; set; }
    public int Score { get; set; }
}

And then let’s create a list of match results:

var results = new List<MatchResult>
{
    new MatchResult
    {
        Team = new Team {Name = "Red", Members = new[] {"Wedge", "Luke"}},
        Score = 8
    },
    new MatchResult
    {
        Team = new Team {Name = "Gold", Members = new[] {"Evaan"}},
        Score = 7
    }
};

Now if I want to get a list of team member with their team score, previously I would do it like this:

var memberScores = results.SelectMany(
    r => r.Team.Members.Select(m => new { Member = m, Score = r.Score })).
    ToList();

And I would get:

[
    { Member = "Wedge", Score = 8 },
    { Member = "Luke", Score = 8 },
    { Member = "Evaan", Score = 7 }
]

But it turned out there is another way to do it:

var memberScores = results.
    SelectMany(
        result => result.Team.Members,
        (result, member) => new { Member = member, Score = result.Score }).
    ToList();

Which is easier to read I think.

The other two overloads are just to add item index if you need it:

var memberScores = results.SelectMany(
    (r, idx) => r.Team.Members.Select(m => new { Member = m, Score = r.Score })).
    ToList();

And

var memberScores = results.
    SelectMany(
        (result, idx) => result.Team.Members,
        (result, member) => new { Member = member, Score = result.Score }).
    ToList();

Comments