2016年10月19日 星期三

[ASP.NET Core X Angular](2) - Routing

 Angular     MVC core    ASP.NET Core

Introduction


We will start building the routing  with angular 2 router.


Environment

Visual Studio 2015 Update 3
NPM: 3.10.3                                    
Microsoft.AspNetCore.Mvc 1.0.0
angular 2: 2.1.0


MVC : Create new controller and view


Create MVC controller and view

 

Controller

[Area("Basic")]
[Route("Basic/[controller]")]

public class CustomerController : Controller
{
        [Route("[action]")]
        public IActionResult Index()
        {
            return View();
        }
}

View

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>




▌Router


Base Url

If you hadn’t set the base url on _Layout.cshtml, don’t forget it!

<head>
    <base href="/">
    <!--Skip....-->
</head>


Enable router

Like we created “Hello world” module and component, we need to create the following typescripts (under $\wwwroot\app\Basic\Customer, or other path you may like).
1.  customer.module.ts : imports necessary modules and declarations.
2.  customer.component.ts : our directive.
3.  Customer.main.ts  : bootstrapping and target the browser platform.

customer.main.ts

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {CustomerAppModule} from './customer.app.module';
import {enableProdMode} from '@angular/core';

//enableProdMode();
platformBrowserDynamic().bootstrapModule(CustomerAppModule);


customer.component.ts

In order to use the angular router library, import Routes and RouterModule from @aangular/router.

Furthermore, use the directive: router-outlet as the template.

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

@Component({
    selector: 'customer-app',
    template: '<router-outlet></router-outlet>'
})
export class CustomerAppComponent implements OnInit {
    ngOnInit() {
    }


customer.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {CustomerAppComponent }  from './customer.app.component';
import { RouterModule } from '@angular/router';

@NgModule({
    imports: [
        BrowserModule,
        HttpModule,
        RouterModule.forRoot([])  //The routes should be set here!
    ],
    declarations: [CustomerAppComponent],
    bootstrap: [CustomerAppComponent]
})
export class CustomerAppModule { }



/Aears/Basic/Customer/Index.cshtml

Update Index.cshtml to load /app/Basic/Customer/customer.main.js
and put in the directive: customer-app

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>
<hr />
<customer-app>
    <div><p><img src="~/images/gif/ajax-loader.gif" /> Please wait ...</p></div>
</customer-app>

@section Scripts {
    <script>
        System.config({
            map: {app: 'app/Basic/Customer'},
            packages: {app: {main:'customer.main.js',defaultExtension: 'js'}}
        });
        System.import('app/customer.main').then(null, console.error.bind(console));
    </script>
}



We will set our routes into RouterModule.forRoot([]) later, now we run the application and navigate to http://localhost:xxxx/Basic/Customer/Index

We will see an error :  Error: Cannot match any routes. URL Segment: 'Basic/Customer/Index'

That means the angular router is working but doesn’t match any route setting because we have not set any yet.




TIPS :
You can set the default launch url in $\Properties\launchSettings.json

"IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "Basic/Customer/Index",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }, …



Create the Single Pages

In this sample, we will have the following pages :
/Basic/Customer/Index
/Basic/Customer/Create
/Basic/Customer/Update

So we first create the above components and html and will implement the real content in the later chapter. For example, the Create component and html should be looked like this…

customer.index.component.ts

import {Component, OnInit} from '@angular/core';
import {Routes} from "@angular/router";

@Component({
    selector: 'customer-index',
    templateUrl: '/app/Basic/Customer/customer-index.component.html'
})

export class CustomerIndexComponent implements OnInit {
    title: string;
    constructor() {
        this.title = "Customers:Index";
    }
    ngOnInit() {
    }
}


customer.index.component.html

<h3>{{title}}</h3>
<hr />
<ul>
    <li>
        <a [routerLink]="['/Basic/Customer/Create']">Go to Create</a>
    </li>
    <li>
        <a [routerLink]="['/Basic/Customer/Edit']">Go to Edit</a>
    </li>
</ul>


Notice that in [routerLink], the name should be “/” + “The route path”.

Set the routes


Set the routes in RouterModule.forRoot()
and don’t forget to import the component and declare them!

customer.app.module

//Skip...
import {CustomerIndexComponent} from './customer-index.component';
import {CustomerCreateComponent} from './customer-create.component';
import {CustomerEditComponent} from './customer-edit.component';

@NgModule({
    imports: [
        //Skip...       
RouterModule.forRoot([
            { path: 'Basic/Customer/Index', component: CustomerIndexComponent },
            { path: 'Basic/Customer/Create', component: CustomerCreateComponent },
            { path: 'Basic/Customer/Edit', component: CustomerEditComponent },
            { path: '', redirectTo: '/Basic/Customer/Index', pathMatch: 'full' }
        ])
    ],


    declarations: [CustomerAppComponent, CustomerIndexComponent, CustomerCreateComponent, CustomerEditComponent],
    bootstrap: [CustomerAppComponent]
})
export class CustomerAppModule { }




Set the default route if not match any routing

Set a default route on the last line of routes.
When the url doesn’t match any route with the routing, it will match the this one.

const appRoutes: Routes = [
    { path: 'Basic/Customer/Index', component: CustomerIndexComponent },
    { path: 'Basic/Customer/Create', component: CustomerCreateComponent },
    { path: 'Basic/Customer/Edit/:id', component: CustomerEditComponent },
    { path: '', redirectTo: '/Basic/Customer/Index', pathMatch: 'full' },
    { path: '**', redirectTo: 'Basic/Customer/Index', pathMatch: 'full' }
];



The router works now


 






Inject the routes (Refactoring)


In latest Angular CLI, it supports creating new project with a default routing module.
Use the command like this : $> ng new <project name> --routing



And it’s default being injected to the AppModule!





We will refactor the current route settings and inject them to customer.app.module from customer.route.

First create customer.route.ts

app/Basic/Customer/customer.app.route.ts

import {CustomerIndexComponent} from './customer-index.component';
import {CustomerCreateComponent} from './customer-create.component';
import {CustomerEditComponent} from './customer-edit.component';
import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const appRoutes: Routes = [
    { path: 'Basic/Customer/Index', component: CustomerIndexComponent },
    { path: 'Basic/Customer/Create', component: CustomerCreateComponent },
    { path: 'Basic/Customer/Edit', component: CustomerEditComponent },
    { path: '', redirectTo: '/Basic/Customer/Index', pathMatch: 'full' }
];
export const CustomerRoutes: ModuleWithProviders = RouterModule.forRoot(appRoutes);



/app/Basic/Customer/customer.app.module.ts

import {CustomerRoutes} from './customer.route';

@NgModule({
    imports: [
        BrowserModule,
        HttpModule,
        CustomerRoutes
    ],
    //Skip...



It’s done!




Send parameter with route path


Notice that when we want to edit a customer, we need to make the Customer Edit Component know which customer we are editing.

There are at least two ways to achieve the goal: Send customer id to Edit Component.
1.  Thru a Service.
2.  Thru url parameter.

Here we will use url parameter for sending the customer’s id.


Route with parameter

app/Basic/Customer/customer.app.route.ts

First update the route of Customer Edit component.

const appRoutes: Routes = [
    //…
    { path: 'Basic/Customer/Edit/:id', component: CustomerEditComponent }
];



app/Basic/Customer/customer-index.route.ts

The event of clicking the [Edit] button is as following.

private editCustomer(item: Customer) {
    this.router.navigate(['Basic/Customer/Edit', item.Id]);
}

And the html should be like this,
<input type="button" class="btn btn-info" value="Edit" (click)="editCustomer(item)" />

So that when we click the [Edit] button on a customer, we will navigate to
http://localhost:4240/Basic/Customer/Edit/11


Receive route parameter

And how to get the parameter value from Url? The interface ActivatedRoute will do us the favor.

app/Basic/Customer/customer-edit.component.ts


//...
import {Router, ActivatedRoute} from '@angular/router';

@Component({
    selector: 'customer-edit',
    providers: [CustomerService, RestUriService],
    templateUrl: '/app/Basic/Customer/customer-edit.component.html'
})

export class CustomerEditComponent implements OnInit {
    //...
    constructor(
        private router: Router,
        private route: ActivatedRoute,
        private custService: CustomerService) {
          //...
        }

    ngOnInit() {
        this.route.params.subscribe(params => {
           let custIdValue = params['id'];
            let custId = +custIdValue; //Equales to parseInt
            console.log("query id = " + +custIdValue);
        });
    }
}





Start implementing the content of SPA



We can now start implementing our content and services of SPA.
But first, let’s create some fake data and style our pages.

Style it

I won’t give much details about the layout of HTML but focus on the typescripts.
You can take a look at my sample codes in Github.

customer.index.component.ts

We will do the following works:
1.  Create const data and use ngFor to display them.
2.  Use router’s navigate function to change current route.

import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';

@Component({
    selector: 'customer-index',
    templateUrl: '/app/Basic/Customer/customer-index.component.html'
})

export class CustomerIndexComponent implements OnInit {
    title: string;
    customers: any[];
    constructor(private router:Router) {
        this.title = "Customers:Index";
        this.customers = CUSTOMERS;
    }

    ngOnInit() {
    }

    //Go to create page
    private goToCreate() {
        this.router.navigate(['Basic/Customer/Create']);
    }
}

const CUSTOMERS: any[] =
    [{ "Id": 1, "Name": "<b>JB</b>", "Phone": "0933XXXXXX", "Age": 35 }
    //...
];


customer.index.component.html

<input type="button" class="btn btn-primary" value="Create New" (click)="goToCreate()" />
<!--Skip some html-->
<tr *ngFor="let item of customers; let sn=index">
                    <td class="col-sm-1 text-center">{{item.Id}}</td>
                    <td class="col-sm-2" [innerHtml]="item.Name">
                    </td>
                    <td class="col-sm-2">{{item.Phone}}</td>
                    <td class="col-sm-1">{{item.Age}}</td>
                    <td class="col-sm-2">
                        <input type="button" class="btn btn-info" value="Edit" (click)="editCustomer(item)" />
                        &nbsp;
                        <input type="button" class="btn btn-danger" value="Delete" (click)="deleteCustomer(item)" />
                    </td>
</tr>


Result




What’s next?

In the next tutorial, we will use the current SPA and build advanced content for our website.


Github


 





Reference



沒有留言:

張貼留言