Generating a New Project

Angular supports many powerful tools like server-side compilation and service workers. Using these advanced tools requires a specific project structure and build chain. Keeping track of all these details while simultaneously developing new features can be a thankless task. Fortunately, the Angular team has bundled up all their best practices into a command-line generator that allows us to easily create new components while still adhering to best practices. Install it globally with: npm install -g @angular/cli. (This book uses version 6.0.8 of the Angular CLI.) You’ll use it throughout the Angular section of this book.

Using the Angular CLI

With that in mind, let’s use the CLI to bootstrap this chapter’s project: a photo gallery. This project will provide lots of opportunities to connect dynamic loading with the interactivity of the page. The user can view their photo gallery as well as specific photos, and edit details about each photo. To start off the app, run ng new rx-photos --routing in the directory you want to create the app in. The new command generates a brand-new Angular application, along with all of the scaffolding needed to build and serve that application. The --routing parameter tells ng to also add in observable-powered routing for this single-page app.

Move into your newly created directory and browse around a bit. The CLI generated a basic app, along with tests and other infrastructure. Take a look at package.json to see what tasks can be run with your new app. When you’re satisfied with your directory browsing, return to the root of the project and run ng serve to fire up a server for the photo project. Browse to http://locahost:4200, and you see a demo page showing that you’ve set everything up correctly as seen in the screenshot.

images/ng2-base-app.png

Once the server is up and running, it’s time to generate the rest of the scaffolding for your photo application. The eventual goal of this application is to allow a user to search, browse, save, and tag photos. This requires three pages:

  • Searching and browsing photo results
  • Viewing saved photos
  • Editing and tagging photos
Joe asks:
Joe asks:
What If I Don’t See the Demo Page?

You might not see the demo page for several reasons. Here are some debugging tips to get you started:

  • Wait a bit. Especially on older computers, the initial compile step might take some time.

  • Make sure you’re using the latest version of the ngcli by reinstalling ngcli and rerunning the serve command.

  • To remove and reinstall your dependencies, run rm -r node_modules && npm i at the project root.

Each page requires a root component to control that page. The ng tool can create a new item from a base schematic and add it to your application with the generate command. In this case, the goal is to generate components, so the command works like this:

=>  ng generate component search-header
<= CREATE src/app/search-header/search-header.component.css (0 bytes)
 CREATE src/app/search-header/search-header.component.html (32 bytes)
 CREATE src/app/search-header/search-header.component.spec.ts (671 bytes)
 CREATE src/app/search-header/search-header.component.ts (296 bytes)
images/aside-icons/note.png

You don’t need to type out the full command every time. The command above could be shortened to ng g c search-header.

The photo project needs more than one component. Generate components named results-list,saved-list and edit-photo. One final thing: copy the contents of index.html—from the finished project included with this book—into the same file in your project. The contents include some styles to make your app look a bit better, so you can focus on the observable side of things. Assets are loaded from the project server, so make sure you have that running.

You’ve now created the five components that the photo application will use. The browser still displays the same old page; to get these new components to display, we need to modify the HTML found in app.component.html. Fortunately, adding a new component is easy. Delete all of the generated HTML and enter the following:

 <app-search-header>
 </app-search-header>

Now that app-search-header is being rendered to the page, let’s put some content in it. Add this to search-header.component.html:

 <header>
  <div class=​"search-container"​>
  <input class=​"search"​ placeholder=​"Search..."
  autofocus ​[​formControl​]="​searchQuery​"​>
  </div>
 </header>

At this point, the page has a green header bar with a search box. The search box won’t work; time to hook that up by importing the tools needed, starting with HttpClient.

Angular provides its own client for working with AJAX requests. In fact, it provides two such clients. The original one, Http, is deprecated. It had a solid core, but interacting with it was clunky and repetitive. The new tool, HttpClient brings several advantages.

First, it assumes that a response will be JSON, saving tedious time writing out .map(response => response.json()) for every request. It also accepts a generic type, further improving our editor’s awareness of what’s going on. Finally, it resurrects HttpInterceptors from AngularJS (unfamiliar with HttpInterceptors? You’ll find out later in this chapter). There’s a lot packed up in this little library, so let’s get started.

Since HttpClient is new to the Angular ecosystem, it’s not included by default. We need to explicitly import the module in the root app module (Angular modules represent a collection of components, services and other modules). Open app.module.ts and add the following lines:

 import​ { BrowserModule } ​from​ ​'@angular/platform-browser'​;
 import​ { NgModule } ​from​ ​'@angular/core'​;
 import​ { AppComponent } ​from​ ​'./app.component'​;
 import​ { PhoneNumComponent } ​from​ ​'./phone-num/phone-num.component'​;
import​ { HttpClientModule } ​from​ ​'@angular/common/http'​;
import​ {FormsModule, ReactiveFormsModule} ​from​ ​'@angular/forms'​;
 
 @NgModule({
  declarations: [
  AppComponent,
  PhoneNumComponent
  ],
  imports: [
  BrowserModule,
HttpClientModule
  FormsModule,
  ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
 })
 export​ ​class​ AppModule { }

First, import the http module into the file. This is included with the @angular/common package, so there’s no need to install more packages.

At this point, add the import for reactive forms as well, which will be used later in this chapter.

Once the modules are imported into the file, they need to be passed into the module declaration, so all of the components and services in this module can access the variables the previously imported modules export.

Now that app.module.ts has been updated, we can import HttpClient into a service. Generate a service inside your project with ng g service photos. This service contains most of the work we do in this chapter. In Angular, services are where the heavy data lifting happens. Components should be used to translate data to and from the view.

Open the newly-created photos.service.ts file. The Angular CLI has generated the outline of a service:

 import​ { Injectable } ​from​ ​'@angular/core'​;
 
 @Injectable({
  providedIn: ​'root'
 })
 export​ ​class​ PhotosService {
 
 constructor​() { }
 }
Joe asks:
Joe asks:
What Does providedIn Mean?

An Angular app can be made up of any number of modules. These modules can be split up and dynamically loaded on the frontend as needed. Angular needs to know which module we want our service to be under. Providing a service in root means that the entire application can access a single, shared instance of this service (and is the default option).

The first order of business is to bring in HttpClient. There are two steps for injecting anything in Angular. The first is to import the class from the Angular library itself. This import is used for type hinting as well as informing Angular what needs to be injected:

import { HttpClient } from ’@angular/common/http’;

Editor autoimports

images/aside-icons/warning.png

Some editors automatically import variables as you type them in your file. When this happens, be sure you’re not importing HttpClient from selenium-webdriver/http, which is also installed as part of the default Angular setup.

The second is to add a private parameter to the constructor function, named whatever we like, with the type of the class we just injected:

 api = ​'http://localhost:3000/api/ng2ajax'​;
 constructor​(​private​ http: HttpClient) { }

To make things easier, an api property is also added above the constructor, detailing the URL of the API that these HTTP requests will hit. If the URL of the API ever changes, only one update needs to be made.

The type annotation on http is required, so the Angular compiler knows what to inject into the service when it is initially created. The private label is added so TypeScript knows that it should be attached to the this of our object. Now that we have access to the client, it’s time to use it. The simplest use of HttpClient is a GET request:

 http.​get​(someURL)
 .subscribe({
  next: result => console.log(result),
  err: err => console.err(err),
  done: () => console.log(​'request finished'​)
 });

This looks remarkably similar to the earlier work with the ajax constructor. However, there’s a structural change, now that you’re working in Angular—the service should not subscribe. Remember, observables are lazy. The service doesn’t want to make any requests until it’s sure there’s a component that wants to know about the results. Instead of doing everything in a single method, we’ll add a method that does everything up to the subscribe and returns the observable. Later on, any component that wants to request data will add the subscription.

 searchPhotos(searchQuery: string): Observable<IPhoto[]> {
 return​ ​this​.http.​get​(​this​.api + ​'/imgSearch/'​ + searchQuery);
 }

Return Annotations

The new syntax after the closing parenthesis in the function argument is a type annotation declaring what that function returns. In this case, searchPhotos returns an Observable. The function doesn’t return just any observable, the declaration can also specify the type of the events that observable emits. In this case, each event from the observable contains an array of IPhotos. TypeScript doesn’t know what an IPhoto is, so you’ll see an error in the console when Angular tries to compile this file. Let’s use TypeScript’s interface keyword to define what an IPhoto is. Add this to the top of your file, below the import declarations.

 export​ ​interface​ IPhoto {
  url: string;
  id: any;
 }

This interface declares that anything with the type IPhoto will have a url property set to a string and an id property, which can be anything. This also implicitly declares that anything IPhoto will not have any additional properties. This interface is exported so that other components can use it. TypeScript also allows you to define optional properties by adding a question mark to the name of the property. If we’d wanted a third, optional property called name, we could have added it like so: name?: string;.

While syntactically correct, this method will still raise complaints from TypeScript. The annotation claims that the method returns an Observable, which it does, but specfically, an observable where every event contains an array of objects conforming to the IPhoto interface. By default, HttpClient returns a much blander type: Observable<Object>—not what we want. With the old Http service, a lot of type casting would be needed to achieve this. The new HttpClient accepts a generic type where the developer can specify exactly what’s coming back in the AJAX request at the point where it’s made:

 searchPhotos(searchQuery: string) {
 return​ ​this​.http.​get​<IPhoto[]>(​this​.api + ​'/imgSearch/'​ + searchQuery);
 }

TypeScript is smart enough that it figures out what’s coming back from the defintion on this.http.get<IPhoto[]>, which means that the method doesn’t need to explicity define a returned type. Now that a method is making an AJAX call, let’s build out the components to put that data on the page.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.226.66