Angular2 – REST sample app, part 1

It’s time to learn angular2. Stay on trend 🙂 So I decided to play with it and I wanted to make some app that uses REST API. IMO it’s most common example. But unfortunately I didn’t find good sample apps or articles to start. It’s very likely that this article become outdated very soon. But right now I think this could be very useful, because still angular2 in beta personally I faced with a few pitfalls. Going to implement something very simple. But simple is not easy.

Probably I’ll split this article in a few sub-articles. Because there a lot of info. Ok let’s get started.  And the good place to start is https://github.com/angular/angular-seed it’s a boilerplate plroject that I used. could be helpful since a lot of work already have been done there.

Main App Component

So I want to make CRUD for users. I will have a list of users – I can delete user from the list, a page where I can create and update user.

import {Component, ViewEncapsulation} from 'angular2/core';
import {
  RouteConfig,
  ROUTER_DIRECTIVES
} from 'angular2/router';

import {UserList} from '../users/list/user-list';
import {UserCreate} from '../users/create/user-create';
import {UserUpdate} from '../users/update/user-update';

@Component({
  selector: 'app',
  moduleId: module.id,
  templateUrl: './app.html',
  //styleUrls: ['./app.css'],
  encapsulation: ViewEncapsulation.None,
  directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
  { path: '/users', component: UserList, name: 'UserList' },
  { path: '/users/create', component: UserCreate, name: 'UserCreate' },
  { path: '/users/:id/update', component: UserUpdate, name: 'UserUpdate' }
])
export class AppCmp {}

So here we define routes for our app and refer them to our components. We will work with them later. Main template is really simple. (file app.html)


<section class="sample-app-content">

<nav>
    <a &#91;routerLink&#93;="&#91;'/Home'&#93;">Home</a>
    <a &#91;routerLink&#93;="&#91;'/UserList'&#93;">Users</a>
  </nav>


  <router-outlet></router-outlet>
</section>


User Model

Before we go to our controllers CRUD components. We need to implement a User model class.

export class User {
  id:number;
  first_name:string;
  last_name:string;
  email:string;
  password:string;

  constructor(attributes: {} = null) {
    this.attributes = attributes;
  }

  set attributes(attributes: {}) {
    for (var k in attributes) {
      this[k] = attributes[k];
    }
  }

  get attributes(): {} {
    return {
      id: this.id,
      first_name: this.first_name,
      last_name: this.last_name,
      email: this.email,
      password: this.password
    };
  }

}

We had a list of available attributes(fields) for user. A also added getter/setter for attributes – I was under the influence of yii and rails models where you can do things like this user = new User({id: 123, first_name: ‘Serg’}) and retrieve attributes like user.attributes. It’s not so important at this moment. It’s likely that I’ll add some generic class where put methods like this in future. At this point we need attributes and easy way to set and get them.

User List

import {Component} from 'angular2/core';
import {ROUTER_DIRECTIVES} from 'angular2/router';
import {Http} from 'angular2/http';
import {User} from '../../../models/user';

@Component({
  selector: 'user-list',
  templateUrl: './user-list.html',
  //styleUrls: ['./app.css'],
  moduleId: module.id,
  directives: [ROUTER_DIRECTIVES]
})
export class UserList {

  users: User[] = [];

  constructor(private http: Http) {
    this.http.get('/users')
      .map(res => res.json())
      .subscribe(
        (users) => {
          users.forEach(userData => {
            var user: User = new User(userData);
            this.users.push(user);
          });
          //console.log(this.users);
        }
      );
  }

  deleteModel(user: User) {
    if (confirm('Are you sure you want to delete user ' + user.email)) {
      this.http.delete('/users/' + user.id)
        .subscribe(
          (response) => {
            if (response.status === 204) {
              this.users.forEach((u: User, i) => {
                if (u.id === user.id) {
                  this.users.splice(i, 1);
                }
              });
              console.log(this.users);
            }
          }
        );
    }
  }
}

Ok let’s check what we have here. We need to get the data for users from the server. We inject http provider in our constructor. And then fetch the users from the our server. This looks .http.get(‘/users’) really simple right ? I will describe how this work in the next paragraph. At this point we need to know that our server return JSON data with users info. But we still need to transform JSON string into json objects so we do  .map(res => res.json()).  Finally we can work with json data and we create our User models and init it with data that we got from server. We also have deleteModel function here that looks preety verbose I think.



<h1>Users</h1>






<table class="table">


<thead>


<tr>


<th>ID</th>




<th>Email</th>




<th>First Name</th>




<th>Last Name</th>




<th>
        <a href="" &#91;routerLink&#93;="&#91;'/UserCreate'&#93;">
          <i class="fa fa-plus"></i>
        </a>
      </th>


    </tr>


  </thead>




<tbody>


<tr *ngFor="#user of users">


<td>{{user.id}}</td>




<td>{{user.email}}</td>




<td>{{user.first_name}}</td>




<td>{{user.last_name}}</td>




<td>
        <a href="" &#91;routerLink&#93;="&#91;'/UserUpdate', {id: user.id}&#93;">
          <i class="fa fa-pencil"></i>
        </a>
        <a href="javascript:void(0)" (click)="deleteModel(user)">
          <i class="fa fa-trash"></i>
        </a>
      </td>


    </tr>


  </tbody>


</table>



Http Client Tricks

In the previous paragraph we fetch our users like this this.http.get(‘/users’). But wait we didn’t specified baseUrl for our API. Moreover angular http client will use content-type:text/html by default – that also doesn’t work for our API. We can provide extra options as the last argument.

let headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http.post('http://pathtoapi.com/users', {
headers: headers
})

And this will work. But wait we will need to do that for each request to our API. So we need a way how we can extend native http client. For me it was the most tricky part. Since there is lack of info how to do that for angular2. Moreover still not off provided solutions 100% work. Long story short

// request_options.ts 

import {Headers, RequestOptions, RequestOptionsArgs} from 'angular2/http';
import {Injectable} from 'angular2/core';
import {AppConfig} from './config';

@Injectable()
export class AppRequestOptions extends RequestOptions {

  headers:Headers = new Headers({
    'Content-Type': 'application/json'
  });


  merge(options?:RequestOptionsArgs):RequestOptions {
    let result = new AppRequestOptions(super.merge(options));

    result.url = AppConfig.API_BASE_URL + result.url;

    return result;
  }

}

We are overriding native request options with our custom class. That will add correct Content Type header and also adds basic api url to each request. That variant appeared the best for me, as I saw other variants that doesn’t work. Finally we need to add some magic in our main.ts file in order to inject our class correctly.

import {provide, enableProdMode} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';
import {AppRequestOptions} from './app/request_options';
import {AppCmp} from './app/main/app';
import {Http, XHRBackend, RequestOptions, HTTP_PROVIDERS} from 'angular2/http';
import 'rxjs/Rx';


if ('<%= ENV %>' === 'prod') { enableProdMode(); }

bootstrap(AppCmp, [
  HTTP_PROVIDERS,
  ROUTER_PROVIDERS,
  provide(APP_BASE_HREF, { useValue: '/' }),
  provide(RequestOptions, {useClass: AppRequestOptions}),
  provide(Http, { useFactory:
    function(backend, defaultOptions) {
      return new Http(backend, defaultOptions); },
    deps: [XHRBackend, RequestOptions]})
])
  .catch(err => console.error(err));


All code examples are available here https://github.com/radzserg/angular2-rest

I’ll tell about user create and update process in the next part. Have a good Friday :).

 

9 thoughts on “Angular2 – REST sample app, part 1

  1. houssem

    Hi I am working on Angular2 too I saw the video on youtub ;… I gave u a like 😉
    Waiting for you to share the code on github so I can learn more from you and give you some pull requests…
    I recommend that you don’t load the whole users list in the component ‘s constructor, you should call your service to load the data in the ngOnInit callback (the component lifecycle) because it is a heavy operation and it may reduce your website performance , page loading…

    Reply
    1. admin Post author

      Thanks for advice and for like ;). I’m also learn angular2 so any feedback is welcome. Will push the to gihub soon. Just wanted to clean up some stuff.

      Reply
  2. Rashmita

    Hey,
    Thank you for sharing the code.
    I’m following it to create a CRUD application. But reached to a place where I stuck.
    And the place is that you have imported AppConfig from config but I can’t find that config file in this example.

    Will you please suggest me what can be there in that config file ?

    Reply
  3. Pingback: Angular2 – CRUD sample app | Coding Tweaks

Leave a Reply

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