2016-09-06

Introduction

If you stumble upon this article, then I presume you are new to Angular 2 and would like to get your hands dirty with the practical examples. Just like you, I am also pretty much new to Angular 2 and that’s why I am writing this series of articles as a reference to other ASP.NET developers who might want to jump on Angular 2.

Luckily, I had the opportunity to review a book on ASP.NET Web API and Angular 2 by Valerio De Sanctis. So, it’s a good time to incorporate the things that I’ve learned from the book into this series of articles. If you are looking for in-depth information about Angular 2 with ASP.NET Web API, I would highly recommend you to grab yourself a copy of the book here.

In my previous article, we’ve done setting up the backbone of our Angular 2 application. As promised, we will continue exploring Angular 2 within ASP.NET Core by using Web API to work with server-side data.

In this series, we will learn how to build a data-driven Angular 2 app from scratch within the context of ASP.NET Core. Here's a glimpse of the output for this series:


What You Will Learn

In this particular series, you will learn the following:

Overview of Angular 2 RC and Core Web API 2

Upgrading from Angular 2 Beta to RC6

Enable Serving Static Files and Diagnostics

Adding the typings.json File

Updating the package.json File

Updating the tsconfig.json File

Updating the AppComponent File

Adding the Angular 2 Module File

Updating the Boot File

Adding the SystemJS File

Updating the index.html

Switching to Gulp

Testing the App

Overview of Web Request and Response Flow

Creating the Backpacker’s Lounge App

Restructuring the App

Integrating NewtonSoft.JSON

Creating the ViewModels

Creating the Web API Controllers

Creating the Client-Side ViewModels

Creating the Client-Side Service

Creating the Angular 2 Components

Enabling Client-Side Routing

Rewriting

Running the App

Tips

Before you go any further, though it’s not required, I would suggest you to read my previous article about Getting Started with Angular 2 in ASP.NET Core, since I will not be covering the details about setting up Angular 2 in ASP.NET Core in this series.

Overview

Before we move further, let’s talk about a brief recap about each framework:

Core Web API

ASP.NET Core Web API is a framework built on top of the .NET Core. It’s made specifically for building RESTful services, which can serve a massive range of the clients including Web Browsers, mobile devices and more. ASP.NET Core has a built-in support for MVC building Web APIs. Unifying the two frameworks makes it simpler to build the apps.

Angular 2

Angular 2 is the second major installment of AngularJS and is entirely written in TypeScript. For the developers who are working with Angular 1.x, you might find a big change in Angular 2, as it is entirely component based and an object orientation is much easier with enhanced DI capability.

We can think of Web API as our data transfer gateway that composes a set of Server-side interfaces/endpoints. This set processes request-response messages, typically expressed in a form of JSON or XML. To put it in other words, our Web API will serve as our central gateway to handle the request from the client, perform the Server-side data operations, and then send back the response to the client (the caller). Angular can be described as a modern client-side library that provides rich features which gives the browser the ability to bind input/output parts of a web page to a flexible, reusable and easily testable JavaScript model.

In this part, we will see the Client-Server capabilities of both frameworks and how they interact with each other. In other words, we need to understand how Angular 2 retrieve and transfer the data from ASP.NET Core Web API. We will be using the Angular 2 RC6 and ASP.NET Core 1.0 RTM to build the Application.

Upgrading to Angular2 RC 6

The demo project in my previous article were using Angular 2 Beta version because that’s the current release at that time of writing. Recently, Angular 2 Release Candidate 6 (RC6) version was out. To align with the latest version, I think it’s a good idea to upgrade to keep up with the most stable release and utilize Angular 2 features to their full extent.

You can find the current release change log here: CHANGELOG.md

Important: The current release of Angular 2 requires at least node v4.x.x and npm 3.x.x. Verify that you are running the versions mentioned, by running node -v and npm -v in a terminal/console window. Older versions produce errors.

Let’s Start Modifying!

Before making any file changes, make sure, that you have the supported versions of Node and NPM. If you have the older versions, uninstall node, download and install the required version here: Node.js

NPM is already integrated in the Node installer, so you don’t have to worry about manually upgrading it.

After the installation, make sure to restart your machine to ensure the updates will take effect. In my case, I have the following versions installed:

Node v6.3.1 (the current release as of this writing).

NPM 3.10.3 (latest version as of this writing).

Keep in mind that this upgrade is also applicable if you were using Angular 2 RC 4 versions and below.

Adding the typings.json File

The typings.json file might not be needed since the release of RC5 and above. If there are cases we need to support backward compatibility and support previous RC versions then, we need to create a new file for creating typings containing node, jasmine and core-js. To do that, just right-click on your project root and then add a new .json file. In my case, I have selected the "NPM Config File" and just renamed it as "typings.json". Replace the default generated content with the following below:

Note: the core-js line in the typings.json file is the only required one, yet we’ve also took the chance to add the jasmine and node typings: you could need them in the near future, should you want to use the Jasmine test framework and/or use code that references objects in the nodejs environment. Keep them there for now, they won’t hurt your project.

Updating the package.json File

If you are not supporting previous versions of angular, I would really recommend you to delete the existing
node_modules
folder before updating the package.json file. This will ensure that we won’t be having a local copy of the previous angular 2 bundles and modules.

If you have decided, open the package.json file and replace everything in it with the following:

The packages with the @ symbol is part of the new Angular 2 bundle: the other ones are loading libraries, helper tools and polyfills for older browser support. The current version of Angular 2 as of this writing is 2.0.0-rc.6. You can check for the updates here. You may also have noticed that we’ve replaced the dependencies to Grunt with Gulp ~ I’ll explain why later in this article. You may remove the existing gruntfile.js file just like what I did if you’d like, but if you prefer using Grunt, feel free to do so. No one is going to fire you for using Grunt :)

You may also have noticed that we’ve added the scripts section to install the typings. Typings manages and install the TypeScript definitions.

Now, save the file to restore the needed packages for our application.

Once installed, you should be able to see something like this:

Figure 1: Application Dependencies

Tip: If Typings didn’t install successfully on Save, the try to use the "Restore Package" option by right-clicking on the "Dependencies" node. Another way is to use the command line to run typings explicitly. To do that, just navigate to the root folder of your app and do a CTRL+SHIFT, then select "Open command window here". In the command line, type in the following command:

npm run typings install

If successful, you should be able to see something like this:
Figure 2: Command Line

As a recap, all Angular 2 dependencies will be installed in the following location in your local drive:

../src/<YourProjectName>/node_modules

Updating the tsconfig.json File

Now open your tsconfig.json file, if you do not have it, you need to create one. Here’s the updated TypeScript JSON Configuration file:

The compileOnSave signals the IDE to generate all files for a given tsconfig.json upon saving. compilerOptions configuration will influence how the Intellisense, and our external TypeScript compiler will work. By excluding the folders in the config, we tell the built-in TypeScript compiler provided by Visual Studio 2015 to disable compiling the external TypeScript files within that location.

Updating the AppComponent File

Since we are upgrading to RC version, we also need to update the component that have references to previous beta version of Angular. In our particular example, we need to update our app.component.ts file to this:

There’s not much to say about the update above. We just changed the import reference to the new name which is @angular/core.

Adding the Angular 2 Module File

Angular Modules, also known as NgModules, have been introduced since Angular2 RC5. This provides a powerful way to organize and bootstrap any Angular2 application: they help developers to consolidate their own set of components, directives and pipes into reusable blocks.

Every Angular2 application since RC5 must have at least one module, which is conventionally called root module and given the AppModule class name.

Now, create a new TypeScript file and name the file as "app.module.ts". If you are following my previous article here, then you may create the file under
Scripts/app
folder. Here’s how the app.module.ts file should look like:

The first line from the config above, adds a reference to the type definitions to ensure our TypeScript compiler will find it. Note that, in case we’re using a CDN or a pre-compiled version of Angular2, we could (and should) definitely remove this line. We then import the basic Angular2 modules that we will need for our app. You can add more Angular 2 module reference in this file when needed. We have also imported the rxjs library definition file(s), which will be useful to compile some Angular2 libraries. We then imported our component "AppComponent". Finally, we have declared our root NgModule: as we can see it consists in an array of named arrays, each one containing a set of Angular2 objects that serves a common purpose: directives, components, pipes, modules and providers. The last one of them contains the component(s) we want to bootstrap, which in our case, the AppComponent file.

For more information about App Module, see: https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html

Updating the Boot File

Open boot.ts file and replace the existing code with the following:

Just like in the app.component.ts file, we changed the reference to the new Angular bundle. Notice that we have referenced the new AppModule that we have created previously. Now we’re just missing an entry point to load with the browser: let’s add it right now.

Adding the SystemJS File

Now, let’s add the systemjs configuration file. Right-click on the wwwroot folder and then select Add > New Item. Under Client-side templates, select "JavaScript File" just like in the figure shown below:

Figure 3: Add New Item Dialog

Name the file as "systemjs.config.js" and then copy the following code below:

The SystemJS.config file will load the application and library modules. We are going to need this loader to build our Angular2 app. For more details about SystemJS config, read on: SystemJS

If you want to use WebPack as your module bundler, see: Introduction to WebPack

Updating the index.html

Now, since we need the SystemJS config to load our app modules and components, then we need to update our index.html to include the new configuration. Replace your index.html file so it will look something like this:

Switching to Gulp

This time, we will be using Gulp as the JavaScript Task Runner to automate our client-side scripts. I’d like to note that switching to Gulp does not mean Grunt is a bad tool. Of course Grunt is still a great tool and used by many. If you prefer, you can always use it to automate tasks for you. I just preferred to use Gulp because of its conciseness and easy task writing. It also uses node.js’ streams, and executes faster, since it does not open/close files, or creates intermediary copies all the time. But then again, every developer has preference, so it’s all up to you which JS task runners you would use :).

Now, let’s add a new file for our Gulp configuration. Go ahead and right click on the project solution and then select Add > New Item. Under Client-side template, select "Gulp Configuration File" as shown in the figure below:
Figure 4: Add New File

Click Add to generate the file, and then replace the default generated configuration with the following code below:

The code above contains three (3) main variables:

gulp - initializes each Gulp plugins that we are going to need for running the tasks.

srcPaths - holds an array of sources that we want to copy and transpile.

destPaths - holds an array of specific location within wwwroot. This is basically where we dump the scripts we defined from the srcPaths.

It also contains five (5) main tasks:

app_clean - This task deletes the existing files from the destination folders we defined.

app - This task compiles, uglify and create sourcemaps for all TypeScript files and place them to wwwroot/app folder, together with their js.map files.

js - This task will copy all JavaScript files from external libraries which is located within the node_modules folder and place them to wwwroot/js folder.

watch - This task watches files defined in app and js tasks that are changed.

default - Define the default task so it will launch all other tasks

Testing the App

Clean and Build your solution and make sure it has no errors. If it builds successfully, then right click on the gulpfile.js and select "Task Runner Explorer"

Make sure to click the "Refresh" button to load the task as shown in the figure below:

Figure 5: Task Runner

Now, right-click to the default task and hit Run. You should be able to see something like this when successful.
Figure 6: Task Running

After successfully running the Task Runner, you should also see that the "app" and "js" folders are generated within your "wwwroot" folder as shown in the figure below:

Figure 7: File Transpiled

Running the application should result to something like this:

Figure 8: Output

If you’ve made it this far, congrats! You have now an Angular 2 RC4 running and now ready to work with the data, using Core Web API.

Tips:

If you are getting the following Typescript Build errors:

“TS2664 Invalid module name in augmentation, module '../../Observable' cannot be found.”

Just follow the solution provided here: https://github.com/Microsoft/TypeScript/issues/8518#issuecomment-229506507

In my case, since I’m using VStudio 2015 Update 3, the fix was to replace the typescriptService.js with the updated version mentioned from the link above. In other words, replace the content of:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TypeScript\typescriptServices.js

with this:

https://raw.githubusercontent.com/Microsoft/TypeScript/Fix8518/lib/typescriptServices.js

Make sure to backup your existing typescriptService.js file before updating.

If you are getting the errors during compilation that says something like these:

TS1005 Build:'=' expected.
TS1005 Build:';' expected.

Just make sure, you exclude the wwwroot in your ts.config. If it doesn’t work, you can try the following options for the fix:

Option 1: Filtering out the location, which contains the *.d.ts files from compiling in your ts.config file.

Option 2: Upgrade to TypeScript 1.9.x or greater Beta versions by running the following command within your project root:

npm install typescript@next

Creating the Application

Before we get our hands dirty, let’s have a quick overview on what happens, when a request is issued by the client and how the Server sends out a JSON Response by looking at the figure, given below:

Figure 9: Web Request and Response Flow

Async Data Requests are any user interactions, as long as it needs to retrieve data from the server, a very typical example includes (yet is not limited to): clicking a button to show data or to edit/delete something, navigating to another view, submitting a form and so on. And in order to respond to any client-issued Async Data Request, we need to build a server-side Web API Controller that would handle the client request (CRUD). In other words, the API will process the request, perform database CRUD operations and return a serialized ViewModel to the client as a Response.

To understand how Angular and Web API connects to each other in real scenarios then let’s go ahead and start building our app.

The Backpacker’s Lounge App

For the entire series, we will be creating an application that covers a bunch of CRUD operations. I love to travel and explore the wonders of nature, so to make this app different and interesting for starters, I have decided to build a simple Backpackers Lounge App.

To make it more clear, we will be creating something like this:
Figure 10: The Main App Layout

In this part of the series, we are going to focus on creating the Home content. Meaning, we are going to create the components for displaying the latest discussions, latest places added and the most viewed places. We are also going to setup the client-side routing and see how the navigation works in Angular2. Adding to that, we will implement a simple master-detail navigation to see how we are going to pass information between components. Finally, we are going to see the basics on how the two-way data-binding will work in Angular2.

Looking at the figure above, we are going to need the following sets of API calls:

api/Lounge/GetLatestDiscussion

api/Place/GetLatestEntries

api/Place/GetMostViewed

The GetLatestDiscussion will return a list of items for discussion. This API method will be hosted within the LoungeController class.

The GetLatestEntries will return a list of items for new places added. The GetMostViewed will return a list of items for places that has the top page views. Both of these API methods will be hosted within the PlaceController class as the API URI defined above suggests.

Restructuring the App

Before we move further, let’s revamp our application structure so it would be more maintainable and to value the separation of concerns.

Looking at the picture shown in figure 10, we need to add some folders to our existing "
app
" folder and then move some existing files.

Let’s start by adding the following folders below under Scripts/app folder:

components

services

viewmodels

The
components
folder is where we store all TypeScript files for Angular 2 related components.

The
services
folder is where we store our TypeScript based services for communicating with ASP.NET Core Web API.

The
viewmodels
folder is where we store our TypeScript based strongly-typed view models.

Now, under the components folder, add the following sub-folders:

home

lounge

about

account

explore

If you have noticed, the folders above matched with our navigation menu shown in figure 10. Note that the folders don’t really need to be the same name as the menus. You can name the folders to whatever you like, but for this demo, let’s stick to that for easy reference. Each folder is where we store the corresponding components for specific features in the website.

Once you have all of that folders set, move the "app.component.ts" to the
components
folder. Your project structure should now look something like this:

Figure 11: The Components Folders

Since we moved the location of our app.component.ts file, then we also need to update the import reference of that file in our app.module.ts file:

Integrating NewtonSoft.JSON

If you’re working with ASP.NET and you’ve never heard about Newtonsoft’s Json.NET, you most certainly missed something that could’ve eased your job. Big time. We’re talking of one of the finest libraries – and most useful tools – ever developed for .NET, at least for the writer: a very efficient (and thus very popular), high-performance JSON serializer, deserializer and all-around framework for .NET, which also happens to be completely Open Source.

To add the Newtonsoft.JSON in our project, go ahead and right click on the project root and then select Manage NuGet Packages. Under "Browse" tab, enter "newtonsoft" in the search bar and it should display the package just like in the following:
Figure 12: Manage Nuget Packages

The latest version of Newtonsoft.Json is 9.01 as of the time of writing. Click Install to add the dependency in our project. After successful installation, you should be able to see the Newstonsoft.Json added in our project references.

Creating the ViewModels

To provide you a quick recap, ViewModels are just classes that house some properties that we only need in the View/UI. From this moment, we are going to use ViewModels as our data transfer objects: sending data from client to server and/or vice-versa.

In this part, we will not be using any database at this point for us to test something out. We’ll just put together some static test data in order to understand how to pass them back and forth by using a well-structured and highly-configurable interface. One of the great things in building a native Web App using ASP.NET and Angular2 is that we can start writing our code without worrying much about the data source: they will come later, and only after we’re sure about what we really need.

Now, it’s time to create the server-side ViewModels.

Create a new folder at the root of your project and name it as "
Data
" and under it, create a sub-folder and name it as "
ViewModels
". Within that folder, create the following class:

LoungeViewModel.cs

PlaceViewModel.cs

We should now have the following structure:
Figure 13: ViewModels Folder

Here are the codes for each class:

The LoungeViewModel.cs

Update the default code generated so it would look similar to the following code below:

The PlaceViewModel.cs

And here’s the code for the PlaceViewModel class:

Both ViewModels above are nothing but just classes that house some properties. Each class is decorated with the [JsonObject(MemberSerialization.OptOut)] attribute that causes the properties to be serialized into JSON unless being decorated by an explicit [JsonIgnore]. We’re making this choice because we’re going to need most of our ViewModel’s properties serialized, as we’ll be seeing soon enough.

We are also limiting the properties defined in our ViewModels at this point. Eventually, we will be adding more to them once we integrate Authorization, Authentication and Image for places. So for now let’s keep rolling.

Creating the Web API Controllers

Now, let’s create the needed API Controllers in our app.

The LoungeController

Create a new class under "
Controllers
" folder and name it as "LoungeController". To do that, just right click on the "Controllers" folder and then select Add > New Item. Under ASP.NET template, select "Web API Controller Class". Replace the default generated code with the following:

The class above make use of Attribute Routing to determine that the class is an API by decorating with this attribute: [Route("api/[controller]")].

The class above contains the following three (3) main API action methods:

Get() – This method returns a single set of data based on a given ID. This method can be invoked using: /api/lounge/
where id is a variable

GetLatestDiscussion() – This method represents a RESTFUL API method, and just calls it’s overload method to actually process the request. This method can be invoked using: /api/lounge/getlatestdiscussion

GetLatestDiscussion(int n) – This method is the overload of GetLatestDiscussion() which takes an int parameter. It use the LINQ OrderByDescending() and Take operators to get the latest records from our test data. This method can be invoked using: /api/lounge/getlatestdiscussion/<n> where n is a variable, representing a number.

It also contains the following private methods:

GetTestData(int num = 99) - A test method that returns a static list of items. We will be using this method to generate a list of LoungViewModel just for us to test something out quickly.

DefaultJsonSettings - A property that gets the default JSON format settings

DefaultNumberOfItems - A property that returns the defaul number of items to be returned in the UI.

MaxNumberOfItems - A property that returns the default maximum number of items to be returned to the UI.

We are going to use the aforementioned private members for displaying a test data in our app. We will for sure split and decouple our data access from within our Controller in next part of this series, but for now, let’s just keep rolling.

The PlaceController

Create another Web API Controller Class and name it as "PlaceController". Again, replace the default code with the following:

You may have noticed that both classes have the pretty much similar implementation. We could probably merge it in one Controller, but I have decided to split it with different class to value the separation of concerns.

You may also have noticed that both classes have the same private members. We could minimize that by creating a separate class/base class, and implement the code there. But for now, let’s just have it on separate class for easy reference.

Creating the Client-Side ViewModels

We will be using TypeScript to define a set of class for us to work with type definitions. In other words, we will not be dealing with raw JSON data and anonymous objects; instead we will be using typed objects: an actual instance of classes.

Under
Scripts/app/viewmodels
folder, add a new TypeScript file and name it as "lounge.ts". Then copy the following code below:

Add another TypeScript file and name it as "place.ts". Copy the following code below:

Notice that we’re not adding all the properties that are present in our server-side ViewModel class: as a general rule of the thumb, we’ll be keeping these classes as lightweight as possible, defining only what we need in the UI: we can always add more properties later, as soon as we need them.

These ViewModels will be used as a client-side, TypeScript class to properly map our JSON-serialized server-side ViewModels that is returned from the Web API controller.

Note: The property names should match with the property names you defined in your server-side ViewModels including the casing.

Creating the Client-Side Service

Now, we need to setup a client-service to fetch the required data from the Web API: we’ll do that by issuing a request to the API Controllers we built earlier. We are going to use the Angular Http client to communicate via XMLHttpRequest (XHR), which is a rather complex HTTP-based API that provides client functionality for transferring data between a client and a server.

The AppService Class

Create another TypeScript file under "
Scripts/app/services
" folder and name it as "app.service.ts". Copy the following code below:

Notice that the code above pretty much resembles our Web API Controllers methods, except that we have merged all the calls to our API within it. This will be the class that our client will use to fetch the data from our WebAPI Controller itself.

Keep in mind that we make use of the Injectable decorator, declaring that the service is an Injectable class: doing that will attach to our class a set of meta data that will be consumed by the DI System upon instantiation. Basically what we’re doing here is telling the DI injector that the constructor parameter(s) should be instantiated by using their declared type(s). The TypeScript code allows a very fluent syntax to achieve this result at constructor level, as it can be seen in the following line:

Creating the Angular 2 Components

The next thing that we are going to do is to create a dedicated component for serving different contents. Let’s start by implementing the "Latest Discussion" master-detail components.

The LoungeList Component

Create a new TypeScript file under "
Scripts/app/components/lounge
" and name the file as "lounge-list.component.ts". Copy the following code below within the file:

The LoungeListComponent will display the latest list of discussions from our mocked data, which is returned from the Web API call. Let’s talk a bit about what we did there:

At the top of the file, we have imported the Angular classes that we need: since we’re creating a Component, we need the Component base class by referencing the @angular/core, and we also need to implement the OnInit interface because our component needs to execute something upon its initialization. We have referenced the Angular2 Router to make use of client-side navigation, because we are going to need it for navigating to the details component. We have also referenced the service that we have created earlier to communicate with our server to get some data. Finally, we have imported the lounge viewmodel for storing the values.

The @component block is where we setup the UI for our Component, including the selector, template and styles. Notice that we used a bit of Angular2 Template Syntax there, in order to get the job done. Specifically, we used a master template, a ngFor directive, a property binding and an event binding. Note that we can also decouple the template and styles in a separate file by defining the templateUrl and styleUrls.

The LoungeListComponent is a class written in TypeScript. This class contains some properties, a constructor which makes use of DI to instantiate the AppService and Router objects. It also composed of methods that will be used in the component itself, specifically in the Angular2 template manipulation, and of course in the client-side routing. The ngOnInit() method is where we get the data from the service which fires on initialization of the component. The onSelect() method takes a Lounge typed object as the parameter. This is where we get the selected item and pass it to the details component by making use of Angular2 routing. For information about Angular 2 Templates, read on: Template Syntax

The LoungeDetail Component

Create another TypeScript component within "
Scripts/app/components/lounge
" and name the file as "lounge-detail.component.ts". Now copy the following code below within that file:

Just like what we did in the lounge-list.component.ts file, we are importing what we need for this specific component. Within template, noticed that we've used the Angular2 ngIf directive to hide the DOM elements when the property item is null or no data associated on it. We also used the Angular2 ngModel directive to implement a two-way data binding syntax for textarea element. This could simply mean that any changes made in the binded element will automatically update the model itself and vice-versa.

We also applied some simple styles to our HTML by defining the @component's styles attribute just to beautify a bit our rendered markup.

The LoungeDetailComponent class implements the logic for this particular component. We have defined a few properties and make use of constructor injection (DI) to initialize objects what we need for the class implementation. This class takes the id parameter from the params observable in the ActivatedRoute service and use the AppService to fetch the data based on the id. You should be able to see how we did that under the ngOnInit() method. The ngOnDestroy() method is responsible for cleaning up the params subscription. Finally, we have implemented an onBack() method to let user navigate back to the Home component view. This event is attached in the Button element defined in our HTML template using Angular2 event binding.

Keep in mind that we will be defining the directives and providers that the Component needs within our app.module.ts file which we will be seeing soon enough.

At this point, we’re done with our basic Lounge master-detail navigation and data binding. Now, let’s do the same to implement the "What’s New?" and "Top Places to Visit" master-detail components.

The PlaceList Component

Create a new TypeScript file under "
Scripts/app/components/explore
" folder and name the file as "place-list.component.ts". Copy the following code below within the file:

The code above has similar implementation with our LoungeListComponent except that we did something different in the ngOnInit() method. We have implemented a switch statement to determine which data to load in the component by passing the class variable as the parameter. The class variable is defined using an @Input() decorator function (@Input() class: string;), that will add the required metadata to make this property available for property binding. We need to do that because we expect this property to be populated from a binding expression within a parent component ~ the Home Component. We did it like that to make this component more reusable and maintainable since the data for "What’s New?" and "Top Places to Visit" components are coming from the same data source.

The PlaceDetail Component

The details for "What’s New?" and "Top Places to Visit" components will also share the same Detail-View component. Let’s go ahead and create it. Add a new TypeScript file on the same folder and name it as "place-detail.component.ts". Now copy the following code below:

import {Component, OnInit, OnDestroy} from "@angular/core";
import {Router, ActivatedRoute} from "@angular/router";
import {AppService} from "../../services/app.service";
import {Place} from "../../viewmodels/place";

@Component({
selector: "place-detail",
template: `
<div *ngIf="item" class="item-details">
<h2>{{item.Name}} - Detail View</h2>
<ul>
<li>
<label>Subject:</label>
<input [(ngModel)]="item.Name" placeholder="Insert the name..."/>
</li>
<li>
<label>Message:</label>
<textarea [(ngModel)]="item.Location" placeholder="Insert a location..."></textarea>
</li>
</ul>
</div>

<div>
<button (click)='onBack()'>Back to Home</button>
</div>
`,
styles: [`
.item-details {
margin: 5px;
padding: 5px 10px;
border: 1px solid 9BCCE0;
background-color: #DDF0D5;
width: 500px;
}
.item-details * {
vertical-align: middle;
}
.item-details ul li {
padding: 5px 0;
}
`]
})

export class PlaceDetailComponent implem

Show more