-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path1_basic-with-search.prompty
103 lines (84 loc) · 115 KB
/
1_basic-with-search.prompty
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
---
name: C# AI Buddy
description: |
🤖 你的 C# 夥伴 — C# AI Buddy 現已上線!
💻 我隨時準備解答你對 C# 的問題,提供程式碼最佳化建議,助你快速掌握這門強大的程式語言。
🚀 無論你是初學者還是資深開發者,C# AI Buddy 都將成為你開發旅程中不可或缺的得力助手!
sample:
BOT_USER_INPUT: |
如何使用 YARP 第三方套件處理 Angular SPA 重導向?
knowledge: |
[
{
"output": "URL: https://dev.to/paulmojicatech/nginx-and-angular-spa-redirect-11ji\nTITLE: NGINX and Angular SPA Redirect\nCONTENT: \n \n\n \n \n\n \n \n \n \n Purpose\n\n\nIn this post, we will discuss how to configure NGINX when hosting an Angular application so that refreshing a page does not give you a 404 error.\n\n\n \n \n The Problem\n\n\nSPA's (Single Page Applications) have changed the way we develop web applications in the last 8 - 10 years (though it is falling out of fashion lately per my post). A SPA runs the entire application on the client (browser). This makes an application much more responsive. \n\nHowever, one of the pitfalls is that routing also happens on the client. This becomes an issue both with SEO (search engine optimization) as well as trying to refresh a route that is not the home route. The problem is that the web server does not know the route because routing happens on the client. So foo.com/bar is an unknown route to the web server. The web server only know foo.com and it serves the index.html page when that requests comes in. The /bar piece is something that the client knows how to handle because there is javascript sent from the web server to the client that is knows how the routing should work.\n\n\n \n \n The Solution\n\n\nThere are several solutions to get around the routing issue. One is to use MPA's (multi page applications). Both Angular (Angular Universal, Scully, and more recently released Analog) and React (NextJs and Remix) have meta-frameworks that provide a server side rendered solution.\n\nThe other solution, which is the one this article will discuss, is allowing NGINX handle the routing.\n\n\n \n \n NGINX Config\n\n\nNGINX is a very fast reverse proxy that acts as a web server to host your web app. It has several configuration files that decide how incoming requests to the web server should be handled. \n\nBelow is a snippet of the configuration you can add that will serve the index.html file (which contains the SPA routing information) to all unknown incoming URLs.\n\n\n\nserver {\n listen 80;\n listen [::]:80;\n server_name localhost;\n\n location / {\n root /usr/share/nginx/html;\n index index.html;\n try_files $uri $uri/ /index.html =404;\n }\n}\n\n\n\n Enter fullscreen mode\n \n\n\n Exit fullscreen mode\n \n\n\n\n\n\n\n\n\nThe important part of this code block is try_files $uri $uri/ /index.html =404;. This tells NGINX if a URL comes in that it does not recognize, use the URI (Uniform Resource Identifier) and serve the index.html file. Note: The =404; part should never be hit but it is common practice to add it as well. This says if index.html is not found, serve the default 404 page.\n\n\n \n \n Conclusion\n\n\nThis was a short article the shows how you can configure NGINX to workaround a limitation in SPAs where routing is broken because the web server does not know how to process routes be default.\n\nIf you would like to hear more about web development, please follow me on BlueSky, Mastodon(@[email protected]), or LinkedIn.\n\nHappy Coding!\n\n\n \n \n \n\n\n\n "
},
{
"output": "URL: https://www.stackhawk.com/blog/angular-open-redirect-guide-examples-and-prevention/\nTITLE: Angular Security Tips: Open Redirect Examples and Prevention\nCONTENT: Open redirect in Angular.I'm sure at some point you've been redirected from one site to another. Website redirections are quite commonly used today for a variety of purposes. But when was the last time you paid attention to what site you're being redirected to? If done right, redirection is as harmless as it sounds. However, in some cases, it can become alarming for you and your users. In this post, I'll talk about open redirects—what they are and what impact they can have. I'll also walk you through how you can detect and fix open redirects in your Angular application.Are Redirects a Sign of Problems?When you're signing up on a website through your Google or Facebook account, the site may redirect you to another page first. Then it processes your authentication and redirects you to the actual website. Online retailers often use this pattern when you make online payments. For instance, if you're booking flights or hotels for your trip, the website usually redirects you to a different website for making payments. Thus, redirects are in a number of use cases. However, when a website allows other users to modify these redirects on their pages, a redirect becomes a vulnerability—an open redirect vulnerability. But how does this happen, and what are its impacts on your site and users? Open Redirect ExplainedTo give you a better idea of what open redirect is, let's take the simple example of password resets. Redirection in Password Reset LinksIf you've forgotten your password on a website, you request a form for setting a new password. The website then generally sends you a password reset link to your email. You click this link and land on a password reset form. You enter your new password, press the Submit button, and voilà! Your password is now reset. You're then safely redirected to the homepage. Visually, here's what the process looks like: Password reset process.Notice in step 2, you receive a password reset link. Let's look at an example of a password reset link:Anatomy of a reset password link.The above link, /resetPassword?token=123&next=home, contains a route to the page of the website—that is, /resetPassword shown above. It contains some query parameters. The first query parameter is the authentication token. It's sent in the reset password request the site will make to its server. The second parameter is next. It contains a redirect URL. The CatchLet's focus on the final step a little. Most websites navigate you somewhere after you've reset your password. Some websites even redirect you to the login page. They tend to do this to enhance your user experience. In the above example, the website intends to navigate to the home page after you've reset your password. However, how your website handles this redirection answers the question of whether it's a vulnerability. To reiterate, what if I could simply change the next parameter in the URL to something else?Changing the redirect URL.In the above example, an attacker changes the next property, so the revision is /resetPassword?token+123&next=attacker.com. If the website now after submitting the form redirects to attacker.com, you're in trouble! The attacker could get your authentication token in the headers of the request. Furthermore, she could potentially take control of your account on the site! Redirection doesn't seem that harmless now, does it?Open redirect.Open Redirect in AngularIn light of the previous example, let's see how you can detect and fix open redirects in your Angular application. That way you can see open redirect in action. Let's jump straight into it. SetupFirst, create a fresh Angular app by running: ng new angular-open-redirect --routingThe --routing flag configures some initial routing in the app for you. You need this set up so you can add and read query parameters from your URLs. Once you're done with the above command, you need to create two components. Let's create the first component, the Home Component, using the Angular command-line interface: ng g c HomeNext, create the Reset Password component: ng g c ResetPasswordNow that you have your two components up, let's create routes for each of them. Inside your app-routing.module.ts, add the following code: import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { ResetPasswordComponent } from './reset-password/reset-password.component';\n\nconst routes: Routes = [\n\n {\n path:'resetPassword',component:ResetPasswordComponent\n },\n {\n path:'home', component:HomeComponent\n },\n];\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)],\n exports: [RouterModule]\n})\nexport class AppRoutingModule { }To add the router in your component, go to your app.component.ts file, and render the Router component: <router-outlet></router-outlet>Your setup is done! Let's kick-start the Angular app by running: ng serverIf you now visit http://localhost:4200/home, you should see the Home component. And as a result, if you visit http://localhost:4200/resetPassword, you should see the Reset Password component: Reset Password component route.Awesome! Let's fill in these pages with some templates. Add Page TemplatesLet's add some HTML to your Home page. Inside your home.component.html file, add the following code: <div class=\"container\">\n <h1>Welcome! 🏠</h1>\n <h4>This is the Home Page</h4>\n</div>Next, let's create a simple Password Reset form user interface inside your Reset Password component. Head over to the reset-password.component.html file, and add the following code: <div class=\"container\">\n <h1>Reset Password Form</h1>\n <div class=\"form col\">\n <div class=\"form-field\">\n <label for=\"password\">Enter your new password:</label>\n <input type=\"password\" id=\"password\" />\n </div>\n <div class=\"form-field\">\n <label for=\"newpassword\">Confirm your new password:</label>\n <input type=\"password\" id=\"newpassword\" />\n </div>\n <div class=\"form-field center\">\n <button>Submit</button>\n </div>\n </div>\n</div>Lastly, let's also add the following styles inside the styles.css: /* You can add global styles to this file, and also import other style files */\nbody {\n background: #41295a; /* fallback for old browsers */\n background: -webkit-linear-gradient(\n to right,\n #2f0743,\n #41295a\n ); /* Chrome 10-25, Safari 5.1-6 */\n background: linear-gradient(\n to right,\n #2f0743,\n #41295a\n ); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */\n color: #fff;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif;\n}\n\n.container {\n display: flex;\n flex-direction: column;\n margin: 100px auto;\n align-items: center;\n justify-content: center;\n}\n\n.col {\n display: flex;\n flex-direction: column;\n}\n\n.form-field {\n width: 400px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin: 20px 0;\n}\n\ninput {\n outline: none;\n border: none;\n border-radius: 5px;\n padding: 10px 20px;\n}\n\n.center {\n justify-content: center;\n}\n\nbutton {\n padding: 10px 20px;\n border-radius: 5px;\n border: none;\n outline: none;\n cursor: pointer;\n font-weight: bold;\n}Great! Your Home page should now look like this:Home page.Similarly, your Reset Password page should look like this: Reset Password page.Add Template LogicLet's add some logic to your Reset Password page. First, I want the two input fields to bind to some variable in your component. This way, you can keep track of what the user is typing. Then, you'll use ngModel to have two-way data binding in your template. For this, you first need to import the FormsModule inside your app.module.ts file: import { FormsModule } from '@angular/forms';And add this inside your imports:@NgModule({\n ...\n imports: [\n BrowserModule,\n AppRoutingModule,\n FormsModule //<-- This one\n ],\n ...\n})\nexport class AppModule { }Then, go inside your reset-password.component.ts file, and add the following code: import { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n selector: 'app-reset-password',\n templateUrl: './reset-password.component.html',\n styleUrls: ['./reset-password.component.css']\n})\nexport class ResetPasswordComponent implements OnInit {\n\n newPassword:any; //Input binding for password field\n confirmNewPassword:any; //Input binding for confirm password field\n token:any; //Variable to store token\n redirectUrl:any; //Variable for the URL to be redirected to after password reset is complete\n\n constructor( private Route: Router, private route: ActivatedRoute) { }\n\n ngOnInit(): void {\n\n //Grab query parameters from the URL\n this.route.queryParams.subscribe(params=>{\n this.token=params.token;\n this.redirectUrl=params.next;\n })\n }\n\n //This function is called when submit button is clicked\n handleSubmit(){\n console.log(this.newPassword,this.confirmNewPassword)\n\n //make an API call reset password in the db\n\n this.handleRedirectAfterSubmit();\n\n }\n\n //This function handles the redirect after password is reset\n handleRedirectAfterSubmit(){\n window.location.href=this.redirectUrl\n }\n\n}Let's walk through what's happening above. Inside the ngOnInit() function, you get the required query parameters from the URL. You need the token and the redirect link, so you save both to the component's state variables. You can also create two other variables, newPassword and confirmNewPassword, to bind them to your input fields. Finally, you have a handleSubmit() function. This fires when you submit the Reset Password form. The other function, handleRedirectAfterSubmit(), fires after you reset the password. It handles the redirection to the Home page. Remember, the redirection is dynamic in nature. This is because the redirected URL is fetched from the query parameters of the Password Reset page's URL. You also need to update the template to call these functions and bind the input fields: <div class=\"container\">\n <h1>Reset Password Form</h1>\n <div class=\"form col\">\n <div class=\"form-field\">\n <label for=\"password\">Enter your new password:</label>\n <input type=\"password\" id=\"password\" [(ngModel)]=\"newPassword\" />\n </div>\n <div class=\"form-field\">\n <label for=\"newpassword\">Confirm your new password:</label>\n <input type=\"password\" id=\"newpassword\" [(ngModel)]=\"confirmNewPassword\" />\n </div>\n <div class=\"form-field center\">\n <button (click)=\"handleSubmit()\">Submit</button>\n </div>\n </div>\n</div>The Open Redirect VulnerabilityLet's go to the URL http://localhost:4200/resetPassword?token=123&next=home. You should see the Reset Password page with the form. If you now type in the passwords and hit the Submit button, the app should redirect you to the Home page. That's because you take the redirect URL from the next property inside your URL's query parameter. You then use window.location.href to change the URL to this new location. But what happens when you go to this instead? http://localhost:4200/resetPassword?token=123&next=https:%2F%2Fwww.google.com If you now submit the form, the app redirects you to Google.com! This means your Angular application has an open redirect vulnerability. An attacker can change this URL to attacker.com and redirect the user to her lethal website instead. Prevent Open Redirect in AngularThere are a number of ways to detect and fix open redirects in your Angular application. In this case, you can simply write code that allows only client-side redirects. Inside your reset-password.component.ts, you need to update your handleRedirectAfterSubmit() function as shown below: //This function handles the redirect after password is reset handleRedirectAfterSubmit(){ this.Route.navigateByUrl(this.redirectUrl) }If you now go back to the route with changed next property and hit the Submit button again, here's what you'll see: Open Redirect fixed using client-side routing.The app throws an error! This allows your site to redirect only to other client-side routes of your site. It disables all external redirects, thereby preventing your site from being a victim of the open redirect vulnerability. Safely Handle External RedirectsThere may be cases where you need to allow redirects to external websites. For instance, you may need to integrate third-party services for authentication and payments. In these situations, you'll have to revert to the previous code that allowed redirects to external websites. But then how do you get rid of the open redirect vulnerability? The best practice here is to keep a list of external websites you'll possibly redirect to. This is a no-brainer, since you'll always know what third-party services you're using in your application. Let's say in this case you want to allow redirects to https://www.google.com and https://www.youtube.com but prevent redirects to https://facebook.com. You can simply create an array that stores the URLs of the acceptable websites:const allowedRedirects=[ 'https://www.google.com', 'https://www.youtube.com']And before redirecting, you can figure out if the website you're redirecting to is safe by checking its entry against your permitted list of redirects. Here's how the updated handleRedirectAfterSubmit() function looks: //This function handles the redirect after password is reset handleRedirectAfterSubmit(){ //this.Route.navigateByUrl(this.redirectUrl) if(allowedRedirects.includes(this.redirectUrl)) window.location.href=this.redirectUrl; }If you now change the next property to https://www.google.com and click the Submit button, it redirects you to Google. However, if you change it to https://www.facebook.com, Angular stalls you on the Reset Password page. All this allows you to enable external redirects in your Angular application while still being safe from open redirects. Conclusion and Exploring FurtherIn this post, you learned all about open redirects and how to cure them in your Angular application. You can explore more solutions to combat open redirects. If you're using a front-end framework, you should always try to use framework-specific solutions for any kind of redirections. There's lots more to learn on StackHawk. You can create a free account or check out the searchable blog to find out more about topics that matter to you. This post was written by Siddhant Varma. Siddhant is a full stack JavaScript developer with expertise in frontend engineering. He’s worked with scaling multiple startups in India and has experience building products in the Ed-Tech and healthcare industries. Siddhant has a passion for teaching and a knack for writing. He's also taught programming to many graduates, helping them become better future developers.StackHawk | October 1, 2021"
},
{
"output": "URL: https://www.reddit.com/r/dotnet/comments/y8e36v/is_webform_migration_to_spa_using_yarp_practical/\nTITLE: \nCONTENT: "
},
{
"output": "URL: https://www.twilio.com/en-us/blog/angular-routing-single-page-applications-javascript-nodejs-html\nTITLE: How to Handle Routing in Angular Single Page Applications (SPAs) with JavaScript and Node.js\nCONTENT: \n\n \n \n \n\n\n\n\n \n \n \n \n \n\n \n \n \n \n \n \n \n Written by\n Contributor\n \n \n \n \n\n\n\n \n \n \n\n\n Opinions expressed by Twilio contributors are their own\n \n \n\n \n\n \n \n\n \n \n \n \n \n \n \n \n\n\n\n\n \n \n \n \n \n \n\n\n \n \n \n How to Handle Routing in Angular Single Page Applications (SPAs) with JavaScript and Node.js\n \n\n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n Routing is an essential concept in Single Page Applications (SPA). When your application is divided into separated logical sections, and all of them are under their own URL, your users can easily share links among each other.\nIn my previous post, Building an App from Scratch with Angular and Webpack, I covered how to start working with Angular and Webpack. Today we will take a closer look at implementing more components and navigating between them.\nIn this post we will:\nNavigate between components.Use path and query parameters.Prefetch data with resolvers.Create lazy loaded modules.\nTo accomplish these tasks, make sure you:\nInstall Node.js and npm (at the time of writing I am using Node.js v8.5.0 and npm 5.3.0),Are familiar with the knowledge covered in Building an App from Scratch with Angular and Webpack.\nCreating Components\nAs a codebase, we will use the code from the previous Angular and Webpack blog post. Clone it and install the dependencies by running:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tgit clone -b angular_and_webpack_demystified_step3 https://github.com/maciejtreder/angular-universal-pwa.git myAngularApp\ncd myAngularApp\nnpm install\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \n \n Right now our application is fairly limited with just one page. Let’s add more components to it:\n\n \n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tmkdir src/app/home\ntouch src/app/home/home.component.ts\nmkdir src/app/posts\ntouch src/app/posts/list.component.ts\ntouch src/app/posts/list.component.html\ntouch src/app/menu.component.ts\nmkdir src/app/model\ntouch src/app/model/ipost.ts\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \n \n Now we need also install @angular/router which we will be playing around with today.\n\n \n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tnpm install --save @angular/router\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nOkay, we have a lot of new files. We should walk through them and modify them. We will start by modifying an entry point of the application – src/app/app.component.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Component } from '@angular/core';\n\n@Component({\n selector: 'my-app',\n template: '<menu></menu><router-outlet></router-outlet>',\n})\nexport class AppComponent {\n\n constructor(){\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNext, we need to create one of the new components – the HomeComponent. It will be displayed as a “welcome message” when our user navigates to the main page of our app. Place the following code into src/app/home/home.component.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Component } from '@angular/core';\n\n@Component({\n template: '<h1>Hello World!</h1>'\n})\nexport class HomeComponent {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNow we will create a second component – ListComponent. Here, the user will be able to see a list of posts from https://jsonplaceholder.typicode.com/posts. Update src/app/posts/list.component.ts to contain:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Component, OnInit } from '@angular/core';\nimport { EchoService } from '../services/echo.service';\nimport { Observable } from 'rxjs/Observable';\nimport { IPost } from '../model/ipost';\n\n@Component({\n templateUrl: `list.component.html`\n})\nexport class ListComponent implements OnInit {\n public posts: Observable<IPost[]>;\n\n constructor(private echoService: EchoService) {}\n\n public ngOnInit(): void {\n this.posts = this.echoService.getPosts();\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nPlace the following code into src/app/posts/list.component.html:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\t<h1>Posts list</h1>\n<ul>\n<li *ngFor=\"let post of posts | async\">\n\t\t{{post.id}} - {{post.title}}\n</li>\n</ul>\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNow we need to create a method used in ListComponent – echoService.getPosts();. This method will be returning an IPost type, so let’s create it first. Place this code in src/app/model/ipost.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\texport interface IPost {\n\tid: number;\n\ttitle: string;\nbody: string;\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nAnd this code in src/app/services/echo.service.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Observable } from 'rxjs/Observable';\nimport { IPost } from '../model/ipost';\n\n@Injectable()\nexport class EchoService {\n\tconstructor(private httpClient: HttpClient) {}\n\n\tpublic getPosts(): Observable<IPost[]> {\n\t\treturn this.httpClient.get<IPost[]>('https://jsonplaceholder.typicode.com/posts');\n\t}\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nAdd Routing\nWe are ready to tell Angular how the user will be navigating within our application. To do that we are registering RouterModule.forRoot() in the AppModule. Copy this code to src/app/app.module.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { NgModule } from '@angular/core';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { ListComponent } from './posts/list.component';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { MenuComponent } from './menu.component';\nimport { RouterModule } from '@angular/router';\nimport { EchoService } from './services/echo.service';\nimport { HttpClientModule } from '@angular/common/http';\n \n@NgModule({\n bootstrap: [ AppComponent ],\n imports: [\n BrowserModule,\n HttpClientModule,\n RouterModule.forRoot([\n { path: '', redirectTo: '/home', pathMatch: 'full' },\n { path: 'home', component: HomeComponent },\n { path: 'posts', component: ListComponent }\n ])\n ],\n declarations: [ AppComponent, MenuComponent, ListComponent, HomeComponent],\n providers: [EchoService]\n})\nexport class AppModule {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nApart from routes, we also declared a MenuComponent used inside AppComponent, just before . Go on and create it by placing this code in src/app/menu.component.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Component } from '@angular/core';\n\n@Component({\n selector: 'menu',\n template: `\n <ul>\n <li><a routerLink=\"home\" routerLinkActive=\"active\">Home</a></li>\n <li><a routerLink=\"posts\" routerLinkActive=\"active\">Posts list</a></li>\n </ul>\n `,\n styles: [`\n :host {margin: 0; padding: 0}\n ul {list-style-type: none; padding: 0;}\n li {display: inline-block;}\n a {border: 1px solid #666666; background: #aaaaaa; border-radius: 5px; box-shadow: 1px 1px 5px black; color: white; font-weight: bold; padding: 5px; text-decoration: none}\n a.active {text-decoration: underline; color: darkslategray;}\n li + li a {margin-left: 20px;}\n `]\n})\nexport class MenuComponent {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nIf you take a closer look, you will see the connection between ‘routerLink’ and routes declared in the AppModule. This is how we are linking stuff in Angular; routerLink is an Angular built-in directive which takes ‘path’ as a parameter and matches it with ‘path’ declared in RouterModule. When there is a match, it loads given component into , which we added earlier into src/app/app.component.ts, next to the\n:\n\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\ttemplate: '<menu></menu><router-outlet></router-outlet>',\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n We have also introduced one more Angular directive, routerLinkActive. By adding it, we are telling Angular that whenever a given route is activated, we want to add to the <a> tag a given class (in our case active).\nNow we are ready to compile and run our app:\n \n \n \n \n \n \n \n \n\n\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n \n \n Click on the “Posts list” link at the top and the page should change respectively:\n\n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n \nYou’ll find all the code up to this point in this GitHub repository which you can clone:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tgit clone -b angular_routing_step1 https://github.com/maciejtreder/angular-universal-pwa.git myAngularApp\ncd myAngularApp/\nnpm install\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nPath Params\nSo far, you know how to navigate between components. For some pages we might want to make Angular pass some values via the URL. Here path parameters comes into use.\nBefore we start writing code, we need to create a file in which we will place a new component:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\ttouch src/app/posts/post.component.ts\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nFirst, we are going to prepare a route with a placeholder for the value which we want to pass in the routes definition. Add the following route and component import to src/app/app.module.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { NgModule } from '@angular/core';\nimport { PostComponent } from './posts/post.component';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { ListComponent } from './posts/list.component';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { MenuComponent } from './menu.component';\nimport { RouterModule } from '@angular/router';\nimport { EchoService } from './services/echo.service';\nimport { HttpClientModule } from '@angular/common/http';\n \n@NgModule({\n bootstrap: [ AppComponent ],\n imports: [\n BrowserModule,\n HttpClientModule,\n RouterModule.forRoot([\n { path: '', redirectTo: '/home', pathMatch: 'full' },\n { path: 'home', component: HomeComponent },\n { path: 'posts', component: ListComponent },\n { path: 'posts/:id', component: PostComponent },\n ])\n ],\n declarations: [ AppComponent, MenuComponent, ListComponent, HomeComponent, PostComponent],\n providers: [EchoService]\n})\nexport class AppModule {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNow you need to create the PostComponent. Place this code in the src/app/posts/post.component.ts file:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Observable } from 'rxjs';\nimport { IPost } from '../model/ipost';\nimport { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { EchoService } from '../services/echo.service';\n\n@Component({\n template: '<h1>{{(post | async)?.title}}</h1><p>{{(post | async)?.body}}</p>'\n})\nexport class PostComponent implements OnInit {\n public post: Observable<IPost>;\n\n constructor(private route: ActivatedRoute, private echoService: EchoService){}\n\n ngOnInit() {\n this.post = this.echoService.getPost(this.route.snapshot.params['id']);\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nWe are injecting the ActivatedRoute service in the constructor; it is representing the actual routing state of our application. With the use of this service, we can retrieve information about the active path, outlets, or pathParams. In our case, we are retrieving them by calling this.route.snapshot.params[‘id’]. Note that the index name which we are calling on this.route.snapshot.params is exactly the same as the one we used for the placeholder in app.module.ts. After retrieving the post ID from the URI, we are passing it to the echoService.getPost(id: number) method to retrieve the given post.\nLet’s implement this method! Add the following code to the src/app/services/echo.service.ts file:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tconstructor(private httpClient: HttpClient) {}\n\npublic getPost(id: number): Observable<IPost> {\n return this.httpClient.get<IPost>('https://jsonplaceholder.typicode.com/posts/' + id);\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nAnd check if the routing works as expected:\n\n \n \n \n \n \n \n \n \n\n\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nHere is what you should see after navigating to http://localhost:3000/posts/1: \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n \nThe final step for this section is linking posts in the ListComponent to the PostComponent. Modify src/app/posts/list.component.html as follows:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\t<h1>Posts list</h1>\n<ul>\n <li *ngFor=\"let post of posts | async\">\n <a [routerLink]=\"['/posts', post.id]\">\n {{post.title}}\n </a>\n</li>\n</ul>\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nWe are passing an array to the routerLink directive. As a first element, we are telling Angular which route we want to activate; the second one is a value which we want to pass as a pathParam. Now every post title listed in the ListComponent routes to the PostComponent and passes the id of given post. Then PostComponent retrieves it from the back end and displays it.\nCongratulations! Now you know how to use path parameters in Angular!\nQuery Params\nWe are displaying 100 posts in the ListComponent. That’s a lot. We should make it more readable by adding pagination.\nFirst, replace the current code in src/app/posts/list.component.ts with the following:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Component, OnInit } from '@angular/core';\nimport { EchoService } from '../services/echo.service';\nimport { Observable } from 'rxjs/Observable';\nimport { IPost } from '../model/ipost';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n templateUrl: `list.component.html`,\n styles: [`\n a:hover {cursor: pointer}\n `]\n})\nexport class ListComponent implements OnInit {\n public posts: IPost[];\n public page: number;\n\n private allPosts: Observable<IPost[]>;\n\n constructor(private echoService: EchoService, private activatedRoute: ActivatedRoute, private router: Router) {}\n\n public ngOnInit(): void {\n this.allPosts = this.echoService.getPosts();\n this.allPosts.subscribe(posts => {\n this.activatedRoute.queryParams.subscribe(params => {\n this.page = +params['page'] || 0;\n this.posts = posts.filter(post => post.id > this.page * 10 && post.id < this.page * 10 + 11);\n })\n });\n }\n\n public nextPage(): void {\n this.router.navigate(['posts'], {queryParams: {page: this.page + 1}});\n }\n\n public previousPage(): void {\n this.router.navigate(['posts'], {queryParams: {page: this.page - 1}});\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nWhat we are doing here is displaying a filtered part of the allPosts array, based on the query parameter page, retrieved from this.activatedRoute.queryParams observable.\nThe last step is changing the template. Copy this code to src/app/posts/list.component.html:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\t<h1>Posts list</h1>\n<a (click)=\"previousPage()\" *ngIf=\"page > 0\">previous</a>\n<a (click)=\"nextPage()\" *ngIf=\"page < 9\">next</a>\n<ul>\n <li *ngFor=\"let post of posts\">\n <a [routerLink]=\"['/posts', post.id]\">\n {{post.title}}\n </a>\n </li>\n</ul>\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nLet’s check if pagination works. Re-run:\n\n \n \n \n \n \n \n \n \n\n\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nGo back to the List page at: http://localhost:3000/posts and click on the “previous” and “next” links. The content of the page should update. You should also see the URL update with query parameter called page.\nYou’ll find all the code up to this point in this GitHub repository which you can clone:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tgit clone -b angular_routing_step2 https://github.com/maciejtreder/angular-universal-pwa.git myAngularApp\ncd myAngularApp/\nnpm install\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nPrefetching Data with Resolvers\nThere is one more thing which is not looking nice in our app. Did you notice the delay between loading component and retrieving data displayed in the template? It occurs because the flow of retrieving data in our applications is:\nUser clicks on the link -> Angular navigates to the route and loads component -> component retrieves data from back end -> data is displayed to the user.\nWouldn’t it be nice to first retrieve data and then load component when the data is already fetched and ready to display? That’s job for Resolver. Let’s play with it a little bit.\nCreate the necessary files:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tmkdir src/app/services/resolvers\ntouch src/app/services/resolvers/posts.resolver.ts\ntouch src/app/services/resolvers/post.resolver.ts\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nWe will start by creating the PostsResolver. Place this code in src/app/services/resolvers/posts.resolver.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Injectable }from '@angular/core';\nimport { Observable }from 'rxjs/Observable';\nimport { Resolve, RouterStateSnapshot,\n ActivatedRouteSnapshot } from '@angular/router';\n\nimport { IPost } from '../../model/ipost';\nimport { EchoService } from '../echo.service';\n\n@Injectable()\nexport class PostsResolver implements Resolve<IPost[]> {\n constructor(private echoService: EchoService) {}\n\n resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<IPost[]> {\n return this.echoService.getPosts();\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nAs you can see we moved the retrieving posts list logic from ListComponent into this file. Now you can edit the ListComponent to get this data from the router. Make the following changes in src/app/posts/list.component.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { Component, OnInit } from '@angular/core';\nimport { IPost } from '../model/ipost';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n templateUrl: `list.component.html`,\n styles: [`\n a:hover {cursor: pointer}\n `]\n})\nexport class ListComponent implements OnInit {\n public posts: IPost[];\n public page: number;\n\n constructor(private activatedRoute: ActivatedRoute, private router: Router) {}\n\n public ngOnInit(): void {\n this.activatedRoute.queryParams.subscribe(params => {\n this.page = +params['page'] || 0;\n this.posts = this.activatedRoute.snapshot.data.posts.filter(post => post.id > this.page * 10 && post.id < this.page * 10 + 11);\n });\n }\n\n public nextPage(): void {\n this.router.navigate(['posts'], {queryParams: {page: this.page + 1}});\n }\n\n public previousPage(): void {\n this.router.navigate(['posts'], {queryParams: {page: this.page - 1}});\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nThe last step is to inform Angular Router that we are using Resolver to prefetch data. We will do that in the AppModule. Edit it accordingly (src/app/app.module.ts):\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { NgModule } from '@angular/core';\nimport { PostComponent } from './posts/post.component';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { ListComponent } from './posts/list.component';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { MenuComponent } from './menu.component';\nimport { RouterModule } from '@angular/router';\nimport { EchoService } from './services/echo.service';\nimport { PostsResolver } from './services/resolvers/posts.resolver';\nimport { HttpClientModule } from '@angular/common/http';\n\n@NgModule({\n\tbootstrap: [ AppComponent ],\n\timports: [\n\t\tBrowserModule,\n\t\tHttpClientModule,\n\t\tRouterModule.forRoot([\n\t\t\t{ path: '', redirectTo: '/home', pathMatch: 'full' },\n\t\t\t{ path: 'home', component: HomeComponent },\n\t\t\t{ path: 'posts', component: ListComponent, resolve: {posts: PostsResolver} },\n\t\t\t{ path: 'posts/:id', component: PostComponent },\n\t\t])\n\t],\n\tdeclarations: [ AppComponent, MenuComponent, ListComponent, HomeComponent, PostComponent],\n\tproviders: [EchoService, PostsResolver]\n})\nexport class AppModule {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNow we can bundle the app again, and check if we still see the delay between loading component and retrieving data displayed in it:\n\n \n \n \n \n \n \n \n \n\n\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNavigate to http://localhost:3000 and click on posts list link. And..? Data is prefetched before Angular navigates to the component.\nError Handling\nWe will use a similar concept to retrieve data for PostComponent. The only additional logic we will add here is error handling, in case that post won’t be found in the back end.Copy this code to src/app/services/resolvers/post.resolver.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport 'rxjs/Rx';\nimport { Injectable }from '@angular/core';\nimport { Observable }from 'rxjs/Observable';\nimport { Resolve, RouterStateSnapshot,\n ActivatedRouteSnapshot } from '@angular/router';\n\nimport { IPost } from '../../model/ipost';\nimport { EchoService } from '../echo.service';\n\n@Injectable()\nexport class PostResolver implements Resolve<IPost> {\n constructor(private echoService: EchoService) {}\n\n resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<IPost> {\n return this.echoService.getPost(+route.paramMap.get('id')).catch(() => Observable.of({title: 'Post not found', body: 'Post with given id doesn\\'t exist'} as IPost));\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNow we should change PostComponent accordingly (src/app/posts/post.component.ts):\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { IPost } from '../model/ipost';\nimport { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\n\n@Component({\n template: '<h1>{{post.title}}</h1><p>{{post.body}}</p>'\n})\nexport class PostComponent implements OnInit {\n public post: IPost;\n\n constructor(private route: ActivatedRoute){}\n\n ngOnInit() {\n this.post = this.route.snapshot.data.post;\n }\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nAnd routing in src/app/app.module.ts:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { NgModule } from '@angular/core';\nimport { PostComponent } from './posts/post.component';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { ListComponent } from './posts/list.component';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { MenuComponent } from './menu.component';\nimport { RouterModule } from '@angular/router';\nimport { EchoService } from './services/echo.service';\nimport { PostsResolver } from './services/resolvers/posts.resolver';\nimport { HttpClientModule } from '@angular/common/http';\nimport { PostResolver } from './services/resolvers/post.resolver';\n\n@NgModule({\n bootstrap: [ AppComponent ],\n imports: [\n BrowserModule,\n HttpClientModule,\n RouterModule.forRoot([\n { path: '', redirectTo: '/home', pathMatch: 'full' },\n { path: 'home', component: HomeComponent },\n { path: 'posts', component: ListComponent, resolve: {posts: PostsResolver} },\n { path: 'posts/:id', component: PostComponent, resolve: {post: PostResolver} },\n ])\n ],\n declarations: [ AppComponent, MenuComponent, ListComponent, HomeComponent, PostComponent],\n providers: [EchoService, PostsResolver, PostResolver]\n})\nexport class AppModule {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNow PostComponent uses prefetched data as well. Let’s check if error handling works appropriately.\n\n \n \n \n \n \n \n \n \n\n\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nNavigate to http://localhost:3000/posts/101. Here is what you should see:\n\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n \nYou’ll find all the code up to this point in this GitHub repository you can clone:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tgit clone -b angular_routing_step3 https://github.com/maciejtreder/angular-universal-pwa.git myAngularApp\ncd myAngularApp/\nnpm install\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nLazy Loading\nWe have our app complete and running. We are prefetching data to improve the user experience. Can we do even more? Yes! Right now our app is bundled into file dist/app.js, and this file is loaded in the user browser even if he only reaches the main page. We can enforce Angular Compiler and Webpack to split it into more files, and load them on demand when the user navigates to different parts of the app. This concept is called Lazy Loading.\nFirst, create the file we are going to edit:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\ttouch src/app/posts/posts.module.ts\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nStart by preparing our new module PostsModule. Place the following code in the src/app/posts/posts.module.ts file:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { ListComponent } from './list.component';\nimport { PostComponent } from './post.component';\nimport { PostResolver } from '../services/resolvers/post.resolver';\nimport { PostsResolver } from '../services/resolvers/posts.resolver';\nimport { EchoService } from '../services/echo.service';\nimport { CommonModule } from '@angular/common';\n\n@NgModule({\n declarations: [ListComponent, PostComponent],\n imports: [\n CommonModule,\n RouterModule.forChild([\n { path: '', component: ListComponent, resolve: {posts: PostsResolver} },\n { path: ':id', component: PostComponent, resolve: {post: PostResolver} }\n ])\n ],\n providers: [EchoService, PostsResolver, PostResolver]\n})\nexport class PostsModule {}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nWhat do we have here? We declared RouterModule.forChild(), in which we are specifying what routes should be served from this module. We also moved our resolvers here: PostResolver, PostsResolver, and EchoService used by resolvers to prefetch data. The last thing we do is declare components, ListComponent and PostComponent to which this module is navigating.\nNow that we moved all of this code into a separate module, we do not require it any longer in the AppModule. Replace the code in the src/app/app.module.ts file with this code:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\timport { NgModule } from '@angular/core';\nimport { AppComponent } from './app.component';\nimport { HomeComponent } from './home/home.component';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { MenuComponent } from './menu.component';\nimport { RouterModule } from '@angular/router';\nimport { HttpClientModule } from '@angular/common/http';\n\n@NgModule({\n bootstrap: [ AppComponent ],\n imports: [\n BrowserModule,\n HttpClientModule,\n RouterModule.forRoot([\n { path: '', redirectTo: '/home', pathMatch: 'full' },\n { path: 'home', component: HomeComponent },\n { path: 'posts', loadChildren: './posts/posts.module#PostsModule'},\n ])\n ],\n declarations: [ AppComponent, MenuComponent, HomeComponent],\n})\nexport class AppModule {\n}\n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n\n\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nAs you can see from the output, we have one more file created by the Angular compiler:\n\n \n \n \n \n \n \n \n \n\n\n\n\t\n\t\n\t\t\n\t\n\t\n\n \n\n\t\t\tHash: 38a5e7c390e0623c5b35\nVersion: webpack 3.10.0\nTime: 6265ms\n Asset Size Chunks Chunk Names\n 0.app.js 759 kB 0 [emitted] [big] \n app.js 1.81 MB 1 [emitted] [big] main\nassets/styles/main.css 364 bytes [emitted] \nassets/img/sandbox.png 167 kB [emitted] \n index.html 563 bytes [emitted] \n\t\t\n\n\n \n\n\n \n \n \n \n \n \n \n\n\n \nLet’s check how the app behaves now. First we navigate to http://localhost:3000 and take a look at the Network tab of Developer Tools:\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n \nWe performed eight requests while loading HomeComponent. Now navigate to the Posts List:\n\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n\n \n \n \n \n \n \n \n\n\n \nAs you can see, the rest of the application is loaded on demand, only when the user navigates to the given route.\nSummary\nToday, we learned how to create routing in the Angular app. We covered the basics as well as more advanced concepts like prefetching data using path resolvers, passing data with pathParams, and decreasing load time with lazy loading.\nI hope that this post was helpful for you and you will start to use your new knowledge in your one-in-a-million app!\nTake a look at the GitHub repository for step 4 and check out angular-universal-pwa to learn more Angular features.\nMaciej Treder:[email protected]://www.maciejtreder.com@maciejtreder (GitHub, Twitter, StackOverflow, LinkedIn)\n \n \n \n\n \n \n\n \n "
},
{
"output": "URL: https://m.youtube.com/watch?v=UZ533qvWl10\nTITLE: \nCONTENT: "
},
{
"output": "URL: https://github.com/microsoft/reverse-proxy/issues/469\nTITLE: YARP with single page web applications - Angular · Issue #469 · microsoft/reverse-proxy\nCONTENT: \n \n\n\n \n Skip to content\n\n \n \n\n \n \n \n\n\n\n\n\n \n\n \n\n \n\n\n\n Navigation Menu\n\n \n\n \n \n \n \n \n \n \n \n \n \n \n GitHub Copilot\n Write better code with AI\n \n\n \n\n\n \n \n \n \n Security\n Find and fix vulnerabilities\n \n\n \n\n\n \n \n \n \n Actions\n Automate any workflow\n \n\n \n\n\n \n \n \n \n Codespaces\n Instant dev environments\n \n\n \n\n\n \n \n \n \n Issues\n Plan and track work\n \n\n \n\n\n \n \n \n \n Code Review\n Manage code changes\n \n\n \n\n\n \n \n \n \n Discussions\n Collaborate outside of code\n \n\n \n\n\n \n \n \n \n Code Search\n Find more, search less\n \n\n \n\n\n \n \n\n\n \n \n \n\n\n \n \n \n Explore\n \n \n Learning Pathways\n\n \n\n\n \n \n White papers, Ebooks, Webinars\n\n \n\n\n \n \n Customer Stories\n\n \n\n\n \n \n Partners\n\n \n\n\n \n \n Executive Insights\n\n \n\n\n \n \n\n\n\n \n \n \n \n \n \n \n \n \n GitHub Sponsors\n Fund open source developers\n \n\n \n\n\n \n \n \n \n \n \n \n \n The ReadME Project\n GitHub community articles\n \n\n \n\n\n \n \n \n \n\n\n\n \n \n \n \n \n \n \n \n Enterprise platform\n AI-powered developer platform\n \n\n \n\n\n \n \n\n\n\n \n Pricing\n\n\n \n \n\n \n \n\n\n\n \n \n \n \n\n \n \n \n \n Provide feedback\n \n \n \n \n \n \n \n \n \n\n\n \n \n\n \n \n \n \n Saved searches\n \n Use saved searches to filter your results more quickly\n \n \n \n \n \n \n \n\n \n \n\n\n\n \n\n \n Sign up\n \n \n \n \n\n\n \n \n\n \n\n\n\n\n\n\n\n\n \n\n\n \n\n\n\n\n\n\n \n \n \n \n \n\n \n\n\n\n\n\n\n \n \n\n \n \n \n \n\n \n Notifications\n You must be signed in to change notification settings\n\n \n\n \n Fork\n 863\n\n \n\n \n \n \n Star\n 8.7k\n\n \n\n\n\n \n\n \n\n\n \n\n \n \n\n\n \n\n \n\n\n\n\n \n \n\n\n\n \n \n \n\n \n\n\n\n\n\n \n\n\n\n\n \n \n DescriptionDescribe the bug\nYARP doesn't proxy HTTP requests to web applications\nTo Reproduce\n\nNode.js should be installed on system (Pre-requisite)\n\nOn powershell/Terminal:\n\nnpx @angular/cli@next new admin-console --strict --routing --style css\ncd admin-console\nnpm start (This exposes the angular app on port 4200 by default)\n\n\nNow, create an ASP.NET application and route all traffic to this UI application\npublic void ConfigureServices(IServiceCollection services)\n{\n\tvar webRoutes = new ProxyRoute[] {\n new ProxyRoute {\n RouteId = \"adminConsoleUIRoute\", ClusterId = \"adminConsole\", Match = {Path = \"/{{**catch-all}}\",},\n }\n };\n\tvar webClusters = new Cluster[] {\n new Cluster {\n Id = \"adminConsole\",\n Destinations = {{\"destination1\", new Destination {Address = \"http://localhost:4200\"}}}\n }\n };\n\n\tservices.AddReverseProxy().LoadFromMemory(webRoutes.ToList(), webClusters.ToList());\n}\n\nOnce we run the ASP.NET app, if we navigate to http://localhost:5000, YARP returns HTTP 404 and doesn't show the UI app running on http://localhost:4200\n\n\nFurther technical details\n\nInclude the version of the packages you are using - Latest master code (Added project reference instead of nuget)\nThe platform (Linux/macOS/Windows) - MacOS\nMetadataLabelsDevelopmentNo branches or pull requestsIssue actions\n\n\n\n\n \n\n\n\n \n\n\n\n \n \n\n \n\n\n\n\n \n\n\n \n\n \n\n \n\n \n\n\n\n\n\n "
},
{
"output": "URL: https://m.youtube.com/watch?v=R9poad3a6S0\nTITLE: \nCONTENT: "
},
{
"output": "URL: https://m.youtube.com/watch?v=7PPsADGDeHw\nTITLE: \nCONTENT: "
},
{
"output": "URL: https://m.youtube.com/watch?v=DFXi8WGCxvg\nTITLE: \nCONTENT: "
},
{
"output": "URL: https://m.youtube.com/watch?v=Zfdnyj-yUkA\nTITLE: \nCONTENT: "
},
{
"output": "URL: https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-authentication-sample-angular-spa-app\nTITLE: Configure authentication in a sample Angular SPA by using Azure Active Directory B2C\nCONTENT: \n\t\n\t\tSkip to main content\n\n\t\t\n\t\t\t\tThis browser is no longer supported.\n\t\t\t\tUpgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.\n\t\t\t\t\n\t\t\t\n\t\t\n\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\n\t\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\tConfigure authentication in a sample Angular single-page application by using Azure Active Directory B2C\n\t\t\t\t\t\t\t\t\t\t\tArticle01/11/2024\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\tIn this article\n\t\t\t\t\t\t\t\t\t\tThis article uses a sample Angular single-page application (SPA) to illustrate how to add Azure Active Directory B2C (Azure AD B2C) authentication to your Angular apps.\nOverview\nOpenID Connect (OIDC) is an authentication protocol built on OAuth 2.0 that you can use to securely sign in a user to an application. This Angular sample uses MSAL Angular and the MSAL Browser. MSAL is a Microsoft-provided library that simplifies adding authentication and authorization support to Angular SPAs.\nSign in flow\nThe sign-in flow involves the following steps:\n\nThe user opens the app and selects Sign in.\nThe app starts an authentication request and redirects the user to Azure AD B2C.\nThe user signs up or signs in and resets the password, or signs in with a social account.\nUpon successful sign-in, Azure AD B2C returns an authorization code to the app. The app takes the following actions:\n\nExchanges the authorization code for an ID token, access token, and refresh token.\nReads the ID token claims.\nStores the access token and refresh token in an in-memory cache for later use. The access token allows the user to call protected resources, such as a web API. The refresh token is used to acquire a new access token.\n\n\n\nApp registration\nTo enable your app to sign in with Azure AD B2C and call a web API, you must register two applications in your Azure AD B2C tenant:\n\nThe single-page application (Angular) registration enables your app to sign in with Azure AD B2C. During app registration, you specify the redirect URI. The redirect URI is the endpoint to which the user is redirected after they authenticate with Azure AD B2C. The app registration process generates an application ID, also known as the client ID, that uniquely identifies your app. This article uses the example App ID: 1.\n\nThe web API registration enables your app to call a protected web API. The registration exposes the web API permissions (scopes). The app registration process generates an application ID that uniquely identifies your web API. This article uses the example App ID: 2. Grant your app (App ID: 1) permissions to the web API scopes (App ID: 2).\n\n\nThe following diagram describes the app registrations and the app architecture.\n\nCall to a web API\nAfter the authentication is completed, users interact with the app, which invokes a protected web API. The web API uses bearer token authentication. The bearer token is the access token that the app obtained from Azure AD B2C. The app passes the token in the authorization header of the HTTPS request.\nAuthorization: Bearer <access token>\n\nIf the access token's scope doesn't match the web API's scopes, the authentication library obtains a new access token with the correct scopes.\nSign out flow\nThe sign-out flow involves the following steps:\n\nFrom the app, users sign out.\nThe app clears its session objects, and the authentication library clears its token cache.\nThe app takes users to the Azure AD B2C sign-out endpoint to terminate the Azure AD B2C session.\nUsers are redirected back to the app.\n\nPrerequisites\nBefore you follow the procedures in this article, make sure that your computer is running:\n\nVisual Studio Code or any other code editor.\nNode.js runtime and npm.\nAngular CLI.\n\nStep 1: Configure your user flow\nWhen users try to sign in to your app, the app starts an authentication request to the authorization endpoint via a user flow. The user flow defines and controls the user experience. After users complete the user flow, Azure AD B2C generates a token and then redirects users back to your application.\nIf you haven't done so already, create a user flow or a custom policy. Repeat the steps to create three separate user flows as follows:\n\nA combined Sign in and sign up user flow, such as susi. This user flow also supports the Forgot your password experience.\nA Profile editing user flow, such as edit_profile.\nA Password reset user flow, such as reset_password.\n\nAzure AD B2C prepends B2C_1_ to the user flow name. For example, susi becomes B2C_1_susi.\nStep 2: Register your Angular SPA and API\nIn this step, you create the registrations for the Angular SPA and the web API app. You also specify the scopes of your web API.\n2.1 Register the web API application\nTo create the web API app registration (App ID: 2), follow these steps:\n\nSign in to the Azure portal.\n\nMake sure you're using the directory that contains your Azure AD B2C tenant. Select the Directories + subscriptions icon in the portal toolbar.\n\nOn the Portal settings | Directories + subscriptions page, find your Azure AD B2C directory in the Directory name list, and then select Switch.\n\nIn the Azure portal, search for and select Azure AD B2C.\n\nSelect App registrations, and then select New registration.\n\nFor Name, enter a name for the application (for example, my-api1). Leave the default values for Redirect URI and Supported account types.\n\nSelect Register.\n\nAfter the app registration is completed, select Overview.\n\nRecord the Application (client) ID value for later use when you configure the web application.\n\n\n\n2.2 Configure scopes\n\nSelect the my-api1 application that you created (App ID: 2) to open its Overview page.\n\nUnder Manage, select Expose an API.\n\nNext to Application ID URI, select the Set link. Replace the default value (GUID) with a unique name (for example, tasks-api), and then select Save.\nWhen your web application requests an access token for the web API, it should add this URI as the prefix for each scope that you define for the API.\n\nUnder Scopes defined by this API, select Add a scope.\n\nTo create a scope that defines read access to the API:\n\nFor Scope name, enter tasks.read.\nFor Admin consent display name, enter Read access to tasks API.\nFor Admin consent description, enter Allows read access to the tasks API.\n\n\nSelect Add scope.\n\nSelect Add a scope, and then add a scope that defines write access to the API:\n\nFor Scope name, enter tasks.write.\nFor Admin consent display name, enter Write access to tasks API.\nFor Admin consent description, enter Allows write access to the tasks API.\n\n\nSelect Add scope.\n\n\n2.3 Register the Angular app\nFollow these steps to create the Angular app registration:\n\nSign in to the Azure portal.\nIf you have access to multiple tenants, select the Settings icon in the top menu to switch to your Azure AD B2C tenant from the Directories + subscriptions menu.\nIn the Azure portal, search for and select Azure AD B2C.\nSelect App registrations, and then select New registration.\nFor Name, enter a name for the application. For example, enter MyApp.\nUnder Supported account types, select Accounts in any identity provider or organizational directory (for authenticating users with user flows).\nUnder Redirect URI, select Single-page application (SPA), and then enter http://localhost:4200 in the URL box.\nUnder Permissions, select the Grant admin consent to openid and offline access permissions checkbox.\nSelect Register.\nRecord the Application (client) ID value for use in a later step when you configure the web application.\n\n\n2.5 Grant permissions\nTo grant your app (App ID: 1) permissions, follow these steps:\n\nSelect App registrations, and then select the app that you created (App ID: 1).\n\nUnder Manage, select API permissions.\n\nUnder Configured permissions, select Add a permission.\n\nSelect the My APIs tab.\n\nSelect the API (App ID: 2) to which the web application should be granted access. For example, enter my-api1.\n\nUnder Permission, expand tasks, and then select the scopes that you defined earlier (for example, tasks.read and tasks.write).\n\nSelect Add permissions.\n\nSelect Grant admin consent for <your tenant name>.\n\nSelect Yes.\n\nSelect Refresh, and then verify that Granted for ... appears under Status for both scopes.\n\nFrom the Configured permissions list, select your scope, and then copy the scope full name.\n\n\n\nStep 3: Get the Angular sample code\nThis sample demonstrates how an Angular single-page application can use Azure AD B2C for user sign-up and sign-in. Then the app acquires an access token and calls a protected web API.\nDownload a .zip file of the sample, or clone the sample from the GitHub repository by using the following command:\ngit clone https://github.com/Azure-Samples/ms-identity-javascript-angular-tutorial.git\n\n3.1 Configure the Angular sample\nNow that you've obtained the SPA sample, update the code with your Azure AD B2C and web API values. In the sample folder, under the src/app folder, open the auth-config.ts file. Update the keys with the corresponding values:\n\n\n\nSection\nKey\nValue\n\n\n\n\nb2cPolicies\nnames\nThe user flow or custom policy that you created in step 1.\n\n\nb2cPolicies\nauthorities\nReplace your-tenant-name with your Azure AD B2C tenant name. For example, use contoso.onmicrosoft.com. Then, replace the policy name with the user flow or custom policy that you created in step 1. For example: https://<your-tenant-name>.b2clogin.com/<your-tenant-name>.onmicrosoft.com/<your-sign-in-sign-up-policy>.\n\n\nb2cPolicies\nauthorityDomain\nYour Azure AD B2C tenant name. For example: contoso.onmicrosoft.com.\n\n\nConfiguration\nclientId\nThe Angular application ID from step 2.3.\n\n\nprotectedResources\nendpoint\nThe URL of the web API: http://localhost:5000/api/todolist.\n\n\nprotectedResources\nscopes\nThe web API scopes that you created in step 2.2. For example: b2cScopes: [\"https://<your-tenant-name>.onmicrosoft.com/tasks-api/tasks.read\"].\n\n\n\nYour resulting src/app/auth-config.ts code should look similar to the following sample:\nexport const b2cPolicies = {\n names: {\n signUpSignIn: \"b2c_1_susi_reset_v2\",\n editProfile: \"b2c_1_edit_profile_v2\"\n },\n authorities: {\n signUpSignIn: {\n authority: \"https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_susi_reset_v2\",\n },\n editProfile: {\n authority: \"https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_edit_profile_v2\"\n }\n },\n authorityDomain: \"your-tenant-name.b2clogin.com\"\n };\n \n \nexport const msalConfig: Configuration = {\n auth: {\n clientId: '<your-MyApp-application-ID>',\n authority: b2cPolicies.authorities.signUpSignIn.authority,\n knownAuthorities: [b2cPolicies.authorityDomain],\n redirectUri: '/', \n },\n // More configuration here\n }\n\nexport const protectedResources = {\n todoListApi: {\n endpoint: \"http://localhost:5000/api/todolist\",\n scopes: [\"https://your-tenant-namee.onmicrosoft.com/api/tasks.read\"],\n },\n}\n\nStep 4: Get the web API sample code\nNow that the web API is registered and you've defined its scopes, configure the web API code to work with your Azure AD B2C tenant.\nDownload a *.zip archive, or clone the sample web API project from GitHub. You can also browse directly to the Azure-Samples/active-directory-b2c-javascript-nodejs-webapi project on GitHub by using the following command:\ngit clone https://github.com/Azure-Samples/active-directory-b2c-javascript-nodejs-webapi.git\n\n4.1 Configure the web API\nIn the sample folder, open the config.json file. This file contains information about your Azure AD B2C identity provider. The web API app uses this information to validate the access token that the web app passes as a bearer token. Update the following properties of the app settings:\n\n\n\nSection\nKey\nValue\n\n\n\n\ncredentials\ntenantName\nThe first part of your Azure AD B2C tenant name. For example: contoso.\n\n\ncredentials\nclientID\nThe web API application ID from step 2.1. In the earlier diagram, it's the application with App ID: 2.\n\n\ncredentials\nissuer\n(Optional) The token issuer iss claim value. Azure AD B2C by default returns the token in the following format: https://<your-tenant-name>.b2clogin.com/<your-tenant-ID>/v2.0/. Replace <your-tenant-name> with the first part of your Azure AD B2C tenant name. Replace <your-tenant-ID> with your Azure AD B2C tenant ID.\n\n\npolicies\npolicyName\nThe user flow or custom policy that you created in step 1. If your application uses multiple user flows or custom policies, specify only one. For example, use the sign-up or sign-in user flow.\n\n\nresource\nscope\nThe scopes of your web API application registration from step 2.5.\n\n\n\nYour final configuration file should look like the following JSON:\n{\n \"credentials\": {\n \"tenantName\": \"<your-tenant-name>\",\n \"clientID\": \"<your-webapi-application-ID>\",\n \"issuer\": \"https://<your-tenant-name>.b2clogin.com/<your-tenant-ID>/v2.0/\"\n },\n \"policies\": {\n \"policyName\": \"b2c_1_susi\"\n },\n \"resource\": {\n \"scope\": [\"tasks.read\"] \n },\n // More settings here\n} \n\nStep 5: Run the Angular SPA and web API\nYou're now ready to test the Angular scoped access to the API. In this step, run both the web API and the sample Angular application on your local machine. Then, sign in to the Angular application, and select the TodoList button to start a request to the protected API.\nRun the web API\n\nOpen a console window and change to the directory that contains the web API sample. For example:\ncd active-directory-b2c-javascript-nodejs-webapi\n\n\nRun the following commands:\nnpm install && npm update\nnode index.js\n\nThe console window displays the port number where the application is hosted:\nListening on port 5000...\n\n\n\nRun the Angular application\n\nOpen another console window and change to the directory that contains the Angular sample. For example:\ncd ms-identity-javascript-angular-tutorial-main/3-Authorization-II/2-call-api-b2c/SPA\n\n\nRun the following commands:\nnpm install && npm update\nnpm start\n\nThe console window displays the port number of where the application is hosted:\nListening on port 4200...\n\n\nGo to http://localhost:4200 in your browser to view the application.\n\nSelect Login.\n\n\nComplete the sign-up or sign-in process.\n\nUpon successful sign-in, you should see your profile. From the menu, select TodoList.\n\n\nSelect Add to add new items to the list, or use the icons to delete or edit items.\n\n\n\nDeploy your application\nIn a production application, the redirect URI for the app registration is typically a publicly accessible endpoint where your app is running, like https://contoso.com.\nYou can add and modify redirect URIs in your registered applications at any time. The following restrictions apply to redirect URIs:\n\nThe reply URL must begin with the scheme https.\nThe reply URL is case-sensitive. Its case must match the case of the URL path of your running application.\n\nNext steps\n\nLearn more about the code sample\nEnable authentication in your own Angular application\nConfigure authentication options in your Angular application\nEnable authentication in your own web API\n\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\n\n\t\t\t\t\t\t\n\t\n\tFeedback\n\t\n\n\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tAdditional resources\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tIn this article\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\n\n\t\n\n\t\n\n"
},
{
"output": "URL: https://www.dotnetcurry.com/angularjs/1467/angular-routing-spa\nTITLE: Building Single Page Applications (SPA) with Angular Router\nCONTENT: \n Routing in Angular enables navigation from one page to another while the user performs application tasks. Routing also allows you to define the modular structure of your application. However, there is much more to routing than just moving the user between multiple views of an application.\nIn this Angular Routing - Basic to Advanced tutorial, we will learn:\nPart 1\n\n basics of routing with Angular\n the need for routing\n getting started instructions to add Routing to an Angular project\n configurations for routes\n creating links that perform view transitions\n\nPart 2\n\n asynchronously resolving a route after obtaining data\n conditionally redirecting to an alternate route\n authorization checks on a route etc.\n and more.\n\n\nThis tutorial is from the DotNetCurry(DNC) Magazine with in-depth tutorials and best practices in JavaScript and .NET. This magazine is aimed at Developers, Architects and Technical Managers and covers Angular, React, Vue.js, C#, Design Patterns, .NET Core, MVC, Azure, DevOps, ALM, and more. Subscribe to this magazine for FREE and receive all previous, current and upcoming editions, right in your Inbox. No Spam Policy.\n\nAngular Routing – Basic to Advanced\nOff late Single Page Applications (SPAs) have become a norm with rich web UI applications being built as SPAs.\nSPAs have a unique advantage that the user doesn’t lose context between page transitions. Yester-year applications reload the entire page as the user tries to navigate between views. The flicker not only affects user experience, it is also inefficient. An update to a section of the page needs the whole page to reload.\nSPAs address this problem.\nImagine a travel website. One of the screens show a list of destinations. The User clicks on a link in the list and the whole page loads to show the details, although the Page header, footer, navigation controls (like breadcrumbs, left nav) etc. haven’t reloaded at all. They might have been updated to show the latest content, but they haven’t reloaded the whole view. This leads to a better and rich user experience.\nEditorial note: A way to circumvent the entire page reload problem is to use AJAX which would allow the client to communicate with the server, without reloading the entire page. However, a key difference between SPAs and AJAX is that SPA is a ‘paradigm’ for how to develop web apps and it may or may not use AJAX to achieve its goal. AJAX on the other hand is a ‘technique’ about communicating with the network and server in the background.\nAngular provides router API to manage a SPA. The Angular Router takes care of the following.\n- Allows a URL to represent a view in the application. For example, my-travel-website.com/destinations represents a list screen. And my-travel-website.com/destination/hyderabad represents a view that shows detailed information about the travel destination Hyderabad.\n- Create links that can preform view transitions. In other words, navigate from one view to the other.\n- Allow creating deep-links to specific pages in the application. Without routing and URL tied to a view, the user doesn’t have a way to deep-link to a screen and would need to go through the screens before it, every single time. Users can’t bookmark or save a link on the page. Routing solves this problem.\nFor example, if a user wants to get to the details screen for London, he/she may navigate directly to my-travel-website.com/destination/london. The User doesn’t need to navigate to the list screen and subsequently click on London.\n- It is convenient for users to go back and forward using browser buttons. Routing doesn’t take away the ease of using browser back and forward buttons or actions.\nPart 1 - Getting Started with Angular Router\nIf you are using Angular CLI for getting started with a new Angular project, use the following command to include routing.\nng new my-travel-app –-routing\nThe --routing flag indicates routing to be added to the solution. It does the following to add routing features.\n1. Install Angular router from the MonoRepo. To install it manually use the following command\nnpm install -S @angular/router\n2. It’s preferred to separate routing logic to an Angular module of its own. Hence, a module named AppRoutingModule is created in a file app-routing.module.ts\n3. Import Routes and RouterModule from @angular/router\n4. Configure routes. Next section explains route configuration in details.\n5. Provide route configuration as a parameter to forRoot function on the RouterModule. Import the returned module.\n@NgModule({ // routes has the route configuration. \n// It is a variable of type Routes\n imports: [RouterModule.forRoot(routes)], \n exports: [RouterModule]\n})\nexport class AppRoutingModule { }\nAlso note that the RouterModule is exported. It allows the routing to be consumed in the root module where the routing is used.\nNotice the code snippet in app.module.ts. It is the root module in the sample application.\nimport { AppRoutingModule } from './app-routing.module';\n\n\n@NgModule({\n declarations: [\n AppComponent,\n ],\n imports: [\n BrowserModule,\n AppRoutingModule\n ],\n bootstrap: [AppComponent]\n})\nexport class AppModule { }\nRoute configuration\nAs described earlier, SPAs use routing to tie a view to a URL (which is nothing but a route). Route configuration defines route pattern (or the URL pattern). It ties a component for a given route pattern. A Component renders the view.\nA route configuration object starts with two basic fields:\nurl – route pattern\ncomponent – the component to render when a route pattern matches.\nAny application will have more than one routes to configure. Hence route configuration is an array of objects. Consider the following routes.\nconst routes: Routes = [\n {\n path: \"destinations\",\n component: DestinationListComponent\n },\n {\n path: \"destination/:city\",\n component: DestinationDetailsComponent\n }\n];\nIn the sample application detailed in this article, we have two views\n1. List screen, showing list of destinations to travel. When the URL matches destinations (see Figure 1), DestinationListComponent is rendered. It shows a view for list of cities.\n2. Details screen, showing a detailed view of the selected city.\n\nFigure 1: List screen\nNotice that city, is a route parameter. Hence, it’s qualified with a colon. The DestinationDetailsComponent can read the value and query details.\nMandatory route parameters\nTraditionally web applications shared data between two pages in the URL.\nSections of the URL might define data required for the route. In the above example (/destination/:city), when the user navigates from list screen, the selected city is passed on as a route parameter. Imagine that the user selected Hyderabad. The URL /destination/:city translates to /destination/Hyderabad. The URL has the city name, which is nothing but the data. Here, the data, city name is very much part of the URL pattern.\nNow we will have to use the data provided in the URL. The Component can access the URL parameter to query details about a particular city. Consider the following code snippet.\n// ... For brevity show only the lines of code relevant \nimport { ActivatedRoute, ParamMap } from '@angular/router';\n\n@Component({\n // ... For brevity show only the lines of code relevant \n})\nexport class DestinationDetailsComponent implements OnInit {\n\n private destination: string;\n\n constructor(private route: ActivatedRoute) { }\n\n ngOnInit() {\n this.destination = this.route.snapshot.params.city;\n }\n\n}\nThe statement this.route.snapshot.params.city retrieves route parameter city’s value. In the example destination/Hyderabad, the value is Hyderabad.\nNotice ActivatedRoute is imported and injected into the component. It refers to the route associated while loading the component. It has a field snapshot, which has details about the route at that point in time. It is of type ActivatedRouteSnapshot.\nOne more level down, the child field params are key value pairs of data. They have all the route parameters for the given URL. In the above example, city is the only route parameter. Hence, we can retrieve the value of the parameter with variable name provided in the route configuration (city).\nYou may use the city value to query details of the city and show it in the details screen. See Figure 2.\n\nFigure 2: Details Screen\nOptional route parameters\nIn a different scenario, data could be optional. Just to tweak the example a little bit, imagine /destination would show the first city in the list. To show details of a specific city, provide city name in the URL. In this case, city will become an optional parameter. The prior approach with city name as a route parameter wouldn’t work because city name is part of the URL pattern.\nIf the city name is not provided, the pattern doesn’t match. Hence, we would add a new route configuration to support optional city name. For clarity, let’s name the new route destination2.\nNote: It need not be given a different name destination2. We may continue to name it as destination and a different configuration object without the mandatory city name in the route.\n{\n path: \"destination2\",\n component: DestinationDetailsComponent\n }\nAs a convention, in a URL, optional values are supplied with Query parameters (query strings). The URL could be /destination2?city=Hyderabad. Or it may just be /destination2, in which case we will show the first city details. City is not part of route configuration anymore.\nThe component can now retrieve city name from queryParameters field instead of params. Refer to the following line of code for city name from query parameters.\nthis.route.snapshot.queryParams.city;\nNotice we are using the same component DestinationDetailsComponent for both the routes (destination and destination2). In the example, the component needs to handle retrieving details from the route param or a query param.\nConsider the following code snippet:\n// ... For brevity show only the lines of code relevant \nimport { ActivatedRoute } from '@angular/router';\n\n@Component({\n// ... For brevity show only the lines of code relevant \n})\nexport class DestinationDetailsComponent implements OnInit {\n\n private destination: string;\n\n constructor(private route: ActivatedRoute) { }\n\n ngOnInit() {\n // Get city name from params or query params\n let cityName = this.route.snapshot.params.city || this.route.snapshot.queryParams.city;\n if(cityName){\n this.destination = cityName;\n }else{\n // Get first city name from the list.\n }\n }\n}\nOptional fragments in the URL\nIt is a common practice in URLs to use breakpoints or hash fragments. They qualify a section of the page or view.\nLet’s further alter the destination details view in the code sample. When no optional parameter was specified instead of always showing the first value, we may use an optional segment to decide first or last.\nAccess the fragment values using the following code statement. On the snapshot object, retrieve value from the fragment (like param or queryParam):\nlet fragment = this.route.snapshot.fragment;\nSubsequently, pick the last city from the list if the segment value is last. See the code snippet and Figure 3.\nthis.destination = cityObjects && _.isArray(cityObjects) && ((fragment && fragment === \"last\") ? cityObjects[cityObjects.length-1] : cityObjects[0] );\n\nFigure 3: Show last city in the list when fragment value is last\nThe sample component will show the first city details (in the list), when no fragment or query string is provided.\n\nFigure 4: URL without any optional params\nAdditional Route Configurations\nSo far, we have seen three route configurations, one for destination list screen and two more for destination details screen.\nIt’s common to have a default route, which will be used when there is no route specified (neither destinations nor destination/[city name]). Imagine we have decided to make the list screen as the default route when no value specified.\nConsider the following configuration object\n{\n path: \"\",\n redirectTo: \"/destinations\",\n pathMatch: \"full\"\n }\nWhen path is an empty string, we redirect to /destinations (the list screen). The field pathMatch qualifies the configuration and redirects to the destinations route when the given path matches completely .\nIn this case, it’s an empty path.\nWhenever there is no path after domain name, it defaults to destinations route.\nWe may use pathMatch value prefix. It would qualify the configuration when the given path includes path specified in the configuration. Consider the following snippet.\n{\n path: \"list\",\n redirectTo: \"/destinations\",\n pathMatch: \"prefix\"\n }\nIt matches any path that includes list at the beginning of the route. Above configuration matches the path /list/cities. However, it doesn’t match if the list is not at the beginning. For example, 0 or /test/list/cities doesn’t match.\nPlease note for an empty string (as the path), which is default route for the application, pathMatch value should be full. If prefix is used, it will match every URL. It’s an empty string after all.\nRouter Outlet\nRouter-Outlet acts as a placeholder for the component at a given route.\nRouting is applied on router-outlet.\nIn a typical application, a root component will contain the router-outlet. There could be other components on root component beyond the router outlet. They could be header, footer, navigational controls like side nav etc.\nSee Figure 5 which depicts an application skeleton. Notice the Destination List component next to left nav, between header and footer. Routing is applied here as the router-outlet is positioned here. As route changes, a component configured for the given route takes place of the router-outlet.\nIn this case, as the route is /destinations, list component is shown on the outlet.\n\nFigure 5: Application Skeleton\nSee Figure 6 with details screen in place of router-outlet:\n\nFigure 6: Details screen on the router-outlet\nConsider the following code snippet. It’s the root component (app.component) template (html file). Notice the highlighted router-outlet. As route changes, a component is rendered at this place.\n<div style=\"background-color: beige; padding: 10px 10px 10px 10px; text-align: center\">\n <strong> This is a placeholder for header. </strong>\n</div>\n\n<table style=\"min-height: 400px\">\n <tr>\n <td style=\"background-color: lightblue;min-width: 150px; padding-left: 15px\">\n <div > \n <strong>Left Nav</strong> \n <div>item 1</div>\n <div>item 2</div>\n\n </div>\n </td>\n <td style=\"padding-left: 50px; background-color: lightcyan; min-width: 800px;\">\n <router-outlet></router-outlet> \n </td>\n </tr>\n</table>\n\n<div style=\"background-color: beige; padding: 10px 10px 10px 10px; text-align: center\">\n <strong> This is a placeholder for footer. </strong>\n</div>\nRouter Link\nWe have almost come to the end of Part 1 of this article just by describing the creation and configuration of routes which renders a view or a component based on a URL (route).\nHowever, how do we link to a route in a SPA built with Angular?\nWe will use Router Link. It’s a directive part of @angular/router module.\nUse router on an anchor or a button element. Consider the following code snippet:\n<div> <a routerLink=\"/destinations\">Home</a> </div>\nHere the home link takes the user back to list screen. However, the route is static, so to create a dynamic link, consider using the directive as follows.\n<a [routerLink]=\"['/destination', dest.name]\"> <strong>{{dest.name}}</strong></a>\nIn this case, the directive is concatenating array of values to create a dynamic route. The dynamic value includes name of the city.\nFollowing are more input attributes used often with Router Link.\nQuery Params:\nTo the queryParams input attribute, provide a JSON object representing key value pairs on the query string.\nIn the following example, the value will be the selected city. The anchor element with the routerLink directive creates a link, clicking on which the user navigates to a route shown in Figure 7.\n<a [routerLink]=\"['/destination']\" [queryParams]=\"{cityName: dest.name}\" > <strong>{{dest.name}}</strong></a>\n\nFigure 7: Query string created based on input attribute queryParams\nWe can pass multiple key value pairs in the object for multiple query strings. The following code snippet creates a link, clicking on which, the target route will have multiple query string separated by ampersand (&). See Figure 8 for the target route.\n<a [routerLink]=\"['/destination']\" [queryParams]=\"{debug: true, cityName: dest.name}\" > <strong>{{dest.name}}</strong></a>\n\nFigure 8: Multiple query strings\nFragments\nPass fragments in the link with fragment input attribute. Consider the following code snippet. See Figure 9 for the resultant route/URL.\n<a [routerLink]=\"['/destination']\" fragment=\"last\" > <strong> Last >>> </strong></a>\n\nFigure 9: Fragment appended\nWith this, we conclude Part 1.\nThe purpose of Part 1 was to accustom you with the basics of routing. Part 1 began by introducing routing, details configuring routes and briefly described creating links. Angular monorepo has included routing package and the module.\nTake a break, or go through the concepts again since we will be needing them in the second part of this article.\nPart II – Angular Routing – Advanced Scenarios\nWhile the first part aimed as a getting started guide covering the basics of defining routes, Part 2 discusses some important features including asynchronously resolving a route after obtaining data, conditionally redirecting to an alternate route, authorization checks on a route etc.\nLet us get started.\nBase Element\nThis section describes a base element in index.html required for routing. It’s always there for an application created with Angular-CLI. If your application is a custom setup, it’s recommended to add this element.\nA base element is required in index.html, with in the head tag. It defines how the relative path for the routing works. By default, it is set to a “/”. We can change it to a different path indicating that the router will now use the new base root (for relative path).\nWith the base element shown here, the router will use /qa-instance-1 in the URL. This is useful when your application is not deployed at the root. Let’s say, Index.html for the application maps to a domain-name.com/qa-instance-1. We can set the base URL so that all relative paths automatically use the base path.\n<base href=\"/qa-instance-1\">\n\nFigure 10: Base href set to qa-isntance-1\nPlease note, the base element could also have a target attribute. In an example, if target value is set to __blank, all hyperlinks open in a separate window. It doesn’t affect the way Angular router works.\nLocation Strategy\nThis section compares retro style hash path in the URL with HTML5 style URL and routing. Angular router provides backward compatible hash path (#page1) in the route. By default, routes and URLs are seamless, for example my-domain-name.com/page1.\nYesteryear applications used URL in the link to decide,\ni. Whether to reload the page or\nii. Go to a section in the page without invoking server reload. The URL may have hash path/bookmark (eg. #section2) in which case there will not be a server reload. Without hash path if the URL changes, page will reload.\nModern browsers support navigating to a URL without a reload and a hash path. It would just be another URL, that doesn’t reload from the server. In an example, navigation between my-domain-name.com/page1 and my-domain-name.com/page2 can happen without a server reload. There is no hash string required in the path.\nAngular application provides both the options:\ni. PathLocationStrategy – It is the default strategy with Angular router. In the code sample (My Travel), navigation between /destinations and /destination/city-name occurs without invoking a server reload.\nii. HashLocationStrategy – The hash strategy is not recommended. If server-side rendering is required, this strategy doesn’t integrate seamlessly. If you absolutely need this approach, provide the additional option useHash with a value true on the router module. Consider the following code snippet.\n@NgModule({\n imports: [RouterModule.forRoot(routes, {useHash:true})],\n exports: [RouterModule]\n})\nexport class AppRoutingModule { }\nIt prefixes every URL with a #. The URLs would be my-domain-name/#/destinations and my-domain-name/#/destination/city-name. See Figure-11.\n\n\nFigure 11: URL with HashLocationStrategy\nMore details\nURL changes contained in the browser are made possible using the following HTML5 API - pushState and replaceState on window.history object. However, Angular Router by default uses pushState API.\nYesteryear browsers considered every new URL as a resource on the server. If we are moving from my-domain.com/page1.html to page2.html, the browser would request for page2.html which physically existed on the server. In case it’s not a static html page, then ASP.NET or other similar server-side technologies created the content (or page2) on the fly.\npushState API allows changing the location on the window object without actually verifying the resource page2.html exists. window.history and state information are updated. It allows SPAs (single page applications) to navigate routes without server-reload.\nLearn more about the HTML API at the following MDN Web Docs - https://developer.mozilla.org/en-US/docs/Web/API/History_API\nEnable tracing of router events and data\nWe can enable tracing during navigation for debugging purposes. The Router events raised in the process are logged to the browser console.\nEnable tracing by adding the flag while bootstrapping the router module. Consider following code snippet and Figure 12.\n@NgModule({\n imports: [RouterModule.forRoot(routes, {enableTracing:true})],\n exports: [RouterModule]\n})\nexport class AppRoutingModule { }\n\nFigure 12: Router events captured with tracing\nRouter Events\nHere’s the complete list of Router Events. (reference: Angular documentation- https://angular.io/guide/router#router-events )\nNavigationStart - This event occurs when navigation to the given route starts.\nNavigationEnd - This event occurs when navigation to the given route ends.\nNavigationCancel - An event occurs when navigation is cancelled. This could be due to Route Guard or resolve returning false during navigation.\nNavigationError - An event occurs when navigation fails with an exception.\nRouteConfigLoadStart - This event occurs before route configuration is lazy loaded by the router.\nRouteConfigLoadEnd - This event occurs after route configuration is lazy loaded by the router.\nRoutesRecognized - This event occurs when router correctly parses and recognized route configuration.\nGuardsCheckStart - Route Guards are used for authorization check. This event occurs at the beginning of guard check.\nGuardsCheckEnd - Route Guards are used for authorization check. This event occurs at the end of guard check.\nChildActivationStart - This even occurs before activating child route.\nChildActivationEnd - This even occurs after activating child route.\nActivationStart - This event occurs before activating/invoking a route.\nActivationEnd - This event occurs before activating/invoking a route.\nResolveStart - This event occurs at the beginning of resolving route configuration.\nResolveEnd - This event occurs at the end of resolving route configuration.\nPlease note, these events can be subscribed to, and we can perform additional action when the event occurs. Consider the following code snippet in ngOnInit of a component. router is an instance of @angular/router/Router.\nthis.router.events.subscribe( \n(event) =>\nconsole.log(\"Router event captured\", event));\nHere, event is an instance of any one of the above event classes. For example, it could be an instance of NavigationStart. All the above classes/events are inherited from RouterEvents class.\nStatic and Dynamic data to the component at route\nThis section describes passing static or dynamic data to the component at a given route. This data could be used in the component after the route is resolved. In a static data example, route configuration provides page title. In a dynamic data example, route configuration supplies an observable, resolving city details. Component just shows the provided city information.\nRouting configuration can include additional data to be sent to the component. To demonstrate this functionality, consider the following example.\nTo the city details route, let’s pass additional static data, window title. The Component can read this information and set the title.\nconst routes: Routes = [{\n path: \"destination/:city\",\n component: DestinationDetailsComponent,\n data: { title: \"My City Details\"}\n },…] // Routes configuration\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)], // set configured routes object\n exports: [RouterModule]\n})\nexport class AppRoutingModule { }\nThe Component has this data available at this.route.snapshot.data. We will use the Title service imported from '@angular/platform-browser'. Here’s the code snippet.\nconstructor(private route: ActivatedRoute,\n private titleService: Title // Title Service injected\n…) { }\n\n ngOnInit() {\n // Set title\n this.titleService.setTitle(this.route.snapshot.data.title || \"\");\nNotice Figure 13 below. The Window title is set with static data sent from route configuration.\n\nFigure 13: Window title set by the component\nFetch data on the fly before loading the route\nWhile the data being passed to the route in the earlier example was static, we can obtain data on the fly.\nThis can be done asynchronously, via an observable or a promise, and will load the route only when resolved. (It may also return a dynamic value synchronously or instantly. If it’s a time-consuming operation, preferably make it an observable or a promise).\nWe can write code to not redirect to the route when the data is not available. We may also redirect to a different route. Some prefer to fallback to an error page.\nIn the code sample we will see shortly, we will redirect to the list screen if a particular city data is unavailable.\n1. As a first step we will create a new service that will fetch the data on the fly, before loading the route. If you are using Angular CLI, run the following command to create the new service.\nng generate service providers/destination-resolver\nThis will create a DestinationResolverService in providers folder.\n2. Consider implementing Resolve interface in @angular/router. While it’s not mandatory, it’s recommended. All that the Angular route configuration needs is the resolve function on the service. However, this interface ensures that the correct method signature is maintained. An incorrect function signature returns as an error at the build time.\nHere’s the code snippet.\nimport { Injectable } from '@angular/core';\nimport { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';\nimport { DataAccessService } from './data-access.service';\nimport { EMPTY, of } from 'rxjs';\nimport { take, mergeMap } from 'rxjs/operators';\nimport * as _ from 'lodash';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class DestinationResolverService implements Resolve<any> {\n\n resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {\n return this.dataAccess.queryDestinationByCity(route.params.city).pipe(\n take(1),\n mergeMap(city => {\n if (city && _.isArray(city) && city.length > 0) {\n return of(city); // returns observable based on the given object.\n } else { // if city details are not available, redirect to list screen\n this.router.navigate(['/destinations']); \n return EMPTY; // an empty observable\n }\n })\n );\n \n }\n\n constructor(private dataAccess: DataAccessService, private router: Router) { }\n}\nIn the sample, the ActivateRouteSnapshot instance is used to get the selected city name from the URL (route parameter).\nThe resolve function queries city details with the help of dataAccess service and returns the resultant observable. Notice, if the city details are available, we return the resultant object, which could be used in the component. Otherwise, we redirect to /destinations route, which is a list screen.\n3. Use the resolver service (we just created) in the route configuration.\nconst routes: Routes = [\n {\n path: \"destination/:city\",\n component: PredefinedDestinationDetailsComponent,\n resolve: {\n destinationDetails: DestinationResolverService\n }\n },\n// ... for brevity removing remaining route configuration objects\n];\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)],\n exports: [RouterModule]\n})\nexport class AppRoutingModule { }\nNotice, the object passed to the component is referred to as destinationDetails as configured above.\n4. Create a new component with Angular CLI to use the city details provided\nng generate component predefined-destination-details\n5. The following code snippet reads dynamic data object obtained while resolving the route:\nexport class PredefinedDestinationDetailsComponent implements OnInit {\n\n constructor(private router: ActivatedRoute) { }\n\n destination: any;\n\n ngOnInit() {\n this.router.data\n .subscribe( (data: {destinationDetails: any}) => {\n this.destination = data.destinationDetails[0];\n})\n }\n\n}\nThis code sample obtains the object from the ActivatedRoute instance and sets it to a component level class variable. This object destination is used in the template file for showing city details. The code snippet below is for template details.\n<div> <a routerLink=\"/destinations\">Home</a> </div>\n<strong>Welcome to {{destination && destination.name}}</strong>\n<div>It's a city in {{destination && destination.country}}</div>\n<div>Plan to visit in {{destination && destination.bestTime}}</div>\nAuth Guard\nThis section describes authentication and authorization checks before redirecting to a route. Because they are asynchronous, they can be based on results from a server-side API call.\nWhile the dynamic data with resolve retrieves data before navigating to the route, the same principle can be applied to authorization. Consider the following code snippet with CanActivate.\n1. Create a new authorization service with Angular CLI\nng generate service authorization\n2. Implement the CanActivate interface. The canActivate function will be invoked by router before activation. If the function returns true it will activate the route. When false, the service can redirect to an error page.\nexport class AuthorizationService implements CanActivate {\n\n canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>{\n // Perform authorization check\n // ...\n // End authorization check\n if(authorizationCheck){\n return of(true);\n }else{\nthis.router.navigate(['/error']);\nreturn of(false);\n }\n }\n\n constructor(private router: Router ) { }\n}\nNote that the canActivate function can asynchronously return an observable<boolean> (like above code snippet), promise<boolean> or a synchronous Boolean value.\n3. Configure the router to use an authorization check\n{\n path: \"destinations\",\n component: DestinationListComponent,\n canActivate: [AuthorizationService]\n },\nHere are additional configuration parameters for authorization check.\nCanActivateChild: Authorization check to activate child routes. Use authorization service that implements CanActivateChild interface. When the canActivateChild function resolves to true, it will activate child routes.\nCanDeactivateChild: Validation to discard changes at a route.\nActivatedRoute Vs ActivatedRouteSnapshot\nIn earlier examples, in a component as a route, when we wanted to read information from the URL or a route (be it route params, query string or fragment) we used activatedRouteInstance.snapshot.\nHere, the snapshot is an object of ActivatedRouteSnapshot. It provides data in the URL at that point in time. It’s an immutable object, which means every time we read snapshot information with a different route param, a new object is created. Also, we shouldn’t inject ActivatedRouteSnapshot as the immutable instance value doesn’t change.\nThere is another way to implement the same. Use observables on ActivateRoute instance, which changes over time. Consider following code snippet\nimport { ActivatedRoute, ParamMap } from '@angular/router';\nimport { switchMap } from 'rxjs/operators';\nImport ParamMap for accessing route params. Import switchMap for iterating values returned by Observable.\nIn the snippet shown below, instead of using snaptshot on this.route (instance of ActivatedRoute) use paramMap, which returns an Observable<ParamMap>. Pipe the result with iterator, switchMap. Access the route parameter city in the switchMap implementation.\nthis.route.paramMap.pipe(\n switchMap((params: ParamMap) => this.dataAccess\n .getDestinationDetails(params.get('city')))\n )\nConclusion\nRouting is an important aspect of building a SPA (Single Page Application). All modern frameworks, be it Angular, React or Vue have a library for managing routing. Angular mono-repo comes with a routing module as well. It is well-rounded and provides all required features for taking care of navigation between views.\nIn this two-part tutorial on Angular Routing, we began with instructions to setup routing, then describe route configuration, an outlet where routing is applied and the links that are source of navigation.\nIn the second part, we got into the nitty-gritty details on features that control composing the URL (location strategy), additional optional configurations like the flag that enables tracing routing events. The tutorial then discussed how to conditionally control transition to a route based on an API response, be it the data for the component or authentication/authorization check.\nReferences:\nAngular documentation on routing- https://angular.io/guide/router\nMDN Web Docs - https://developer.mozilla.org/en-US/docs/Web/API/History_API\nBlog: Understanding Router State- https://vsavkin.com/angular-router-understanding-router-state-7b5b95a12eab\nRouterEvent documentation - https://angular.io/api/router/RouterEvent\nThanks to Ravi Kiran for reviewing the article technically.\nThis article has been editorially reviewed by Suprotim Agarwal.\n\n\n \n\n \n\t\n\t\n\t\t\tC# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.\n We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle). Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.\n Click here to Explore the Table of Contents or Download Sample Chapters!\n \n \n\t\t\n \n \n\t\t \n \n \n \n \n \n V Keerti Kotaru is an author and a blogger. He has authored two books on Angular and Material Design. He was a Microsoft MVP (2016-2019) and a frequent contributor to the developer community. Subscribe to V Keerti Kotaru's thoughts at his Twitter account. Checkout his past blogs, books and contributions at kvkirthy.github.io/showcase.\n \n \n\n "
}
]
---
system:
### Role Description:
You are a C# software development partner, specializing in assisting professional developers with any questions, suggestions, or clarifications related to C#. Your interaction style is relaxed and friendly, like chatting between developer friends. Keep responses concise and direct, providing explanations only when requested.
### Interaction Structure:
1. **Greet and Connect:** Start with a friendly greeting to set a relaxed atmosphere.
2. **Prompt Specific Query:** Ask for specific details about the C# issue or topic they need help with.
3. **Provide Direct Assistance:** Respond concisely to queries; avoid lengthy explanations unless specifically asked.
4. **Offer Further Help:** After providing assistance, ask if there's anything else related to C# they need help with.
5. **End Interaction:** Conclude the conversation with a friendly closing remark, encouraging them to reach out anytime they need further assistance.
6. **Language**: Respond in the same language as the user. BUT when the user uses 中文: (Respond in 正體中文zh-tw not 簡體中文zh-cn. Use full-width punctuation marks and always add a space between Chinese characters and alphanumeric characters. 專有名詞使用台灣正體中文常用術語。) Default to English if unclear.
### AI Operation Guide:
- **Quick Response:** Aim for quick and straightforward answers to mimic real-time chat between friends.
- **Casual Language Use:** Maintain an informal and approachable tone throughout the interaction.
- **Provide Reference Links:** Always provide users with reference links when using Reference knowledge. Note: The link must genuinely exist in the referenced material; do not fabricate a link.
- **Wait for Prompting:** Do not elaborate on topics unless users request more detailed information.
- **Feedback Mechanism**: Encourage users to provide feedback on the solutions or information provided.
### Sample Prompt:
Hey! What C# challenge are you tackling today?
### Additional Prompt:
Got it! Need help with anything else related to C#? Or do you have something else on your mind?
### Closing Prompt:
Great! If you need more C# help feel free to reach out anytime. Happy Coding! 👋
### Reference knowledge
Please refer to the following information to answer.
{{knowledge}}
\---
ALWAYS PROVIDE USERS WITH REFERENCE LINKS IF THEY ARE RELATED TO THE USER'S QUESTION OR WERE USED IN YOUR RESPONSE.
ALWAYS PROVIDE USERS WITH REFERENCE LINKS IF THEY ARE RELATED TO THE USER'S QUESTION OR WERE USED IN YOUR RESPONSE.
ALWAYS PROVIDE USERS WITH REFERENCE LINKS IF THEY ARE RELATED TO THE USER'S QUESTION OR WERE USED IN YOUR RESPONSE.
assistant:
🤖 遇見你的最佳 C# 助手 — C# AI Buddy!
💻 我能夠即時解答你的 C# 相關問題,提供程式碼優化建議,並協助你快速掌握這門強大的程式語言。
🚀 無論你是初學者還是經驗豐富的開發者,C# AI Buddy 都將成為你 Coding 之旅中不可或缺的得力助手!
user:
{{BOT_USER_INPUT}}