Updated on
Lazy loading tells the Angular router to download part of the application the first time someone navigates to it, instead of shipping all of it in the first bundle. A route swaps its component property for loadChildren or loadComponent, and the value becomes a dynamic import() the router calls on demand.
Here we build the owner feature as its own area, wire it behind a lazy route, and fetch the owner list from the API once someone navigates there. Angular HttpClient and Environment Files built the repository service this uses.
For complete navigation and all the basic instructions of the Angular series, check out the Introduction of the Angular series.
What Is Lazy Loading in Angular?
Lazy loading means the browser downloads a part of an Angular application only when the user first navigates to it, instead of shipping all of it up front.
The router is where this happens. A route with a component property pulls that component into the bundle that declares the route. A route with loadComponent or loadChildren holds a dynamic import() instead, and the router runs that import the first time someone navigates there.
The payoff is the first load. An application with an admin area, a reporting area and a settings area sends none of them to a visitor who only opens the home page.
The cost is a pause on that first navigation, while the chunk downloads. The router accepts a preloading strategy that fetches lazy chunks in the background once the application has started, trading a little bandwidth for that pause.
Lazy loading is a routing decision rather than a component one, which is why it is configured in the routes and nowhere else.
What Replaces the Feature Module in a Standalone Application?
A lazily loaded feature lives in its own folder: a component to render, and a routes file that says how to reach it. There is no module to generate, so the CLI writes the component and we write the routes file ourselves.
ng g component owner/owner-list --skip-tests
That writes owner-list.ts, owner-list.html and owner-list.css under src/app/owner/owner-list, and --skip-tests leaves out the spec file, the same flag the earlier parts used.
Nothing else in the application changes, and that is the difference a standalone project makes. There is no owner.module.ts with a declarations array to add the component to, and no app.module.ts to import that module into. A standalone component names its own dependencies in its own imports array, so a feature needs no module to hold them.
What we want is for the Owner Actions menu to show this component’s content. So first, just for testing purposes, let’s put a single paragraph in the component’s template, owner-list.html:
<p>This is owner-list component page.</p>
The feature also needs a routes file, so let’s create owner.routes.ts beside the component folder:
import { Routes } from '@angular/router';
export const routes: Routes = [];
It exports a plain Routes array and nothing else: no decorator, no NgModule, nothing to register anywhere. The array is empty at this point because the file has to exist before the application’s routes can import it.
How Do We Configure a Lazy-Loaded Route?
A lazy route is a route whose destination is a dynamic import(). The router calls that import on first navigation, waits for the chunk, and then activates whatever came back.
loadChildren loads a whole feature area. Its import resolves to a set of child routes, and every path inside is mounted under the parent’s path, so a parent path of owner and a child path of list produce /owner/list.
loadComponent loads a single component, which suits a route with no children of its own.
Both replace component on that route, and they can appear together, with the component acting as the shell the child routes render inside.
One rule decides whether any of it works. Nothing loaded eagerly may import the lazy file, because a single import from an eagerly loaded module pulls the whole feature back into the initial bundle. The build succeeds, the route still works, and the laziness is silently gone.
The diagram below shows what that leaves in the first bundle, and what waits until someone navigates to the owner feature.
With the feature in place, let’s point the application’s routes at it, in app.routes.ts:
import { Routes } from '@angular/router';
import { Home } from './home/home';
import { NotFound } from './error-pages/not-found/not-found';
export const routes: Routes = [
{ path: 'home', component: Home },
{ path: 'owner', loadChildren: () => import('./owner/owner.routes').then(m => m.routes) },
{ path: '404', component: NotFound },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', redirectTo: '/404' }
];
The owner route carries loadChildren instead of component, and its value is a function that returns a dynamic import(). Angular’s route loading strategies guide states the rule in one line: “The loadChildren property lazily loads child routes during route matching.” The router calls that function the first time someone navigates into /owner, and reads the routes array off the file it gets back. The wildcard route stays last, because the router takes the first match it finds.
The import list at the top of the file is the other half of the setup. Home and NotFound are imported there, so they travel in the first bundle. owner.routes is not imported anywhere in the eagerly loaded application, and that is what keeps the feature out of that bundle.
So when we open the Home page, the browser downloads only what the root of the application needs. The owner feature’s chunk arrives the first time we click the Owner Actions menu and not before, which is the reason lazy loading matters as an application grows.
How Do Child Routes Work Under a Parent Path?
To enable navigation to the OwnerList component, the feature’s routes file needs a route of its own:
import { Routes } from '@angular/router';
import { OwnerList } from './owner-list/owner-list';
export const routes: Routes = [
{ path: 'list', component: OwnerList }
];
With this setup, we expose our OwnerList component on the http://localhost:4200/owner/list endpoint. The list path is relative to the owner path that loaded the file, so the two join into one URL, and this array registers itself nowhere. provideRouter(routes) in app.config.ts registers the top-level array once, and the router mounts anything it loads later under the path that loaded it.
An older NgModule project wrapped the same array in RouterModule.forChild(routes) inside a second module file. A standalone routes file needs neither.
Now we have to modify the menu.html file:
<div class="collapse navbar-collapse" id="collapseNav" [collapse]="!isExpanded" [isAnimated]="true">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" [routerLink]="['/owner/list']" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}"> Owner Actions </a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Account Actions </a>
</li>
</ul>
</div>
After all of these modifications, we can run our app and click the Owner Actions link. As soon as we do that, our new component shows up, and the link gets an active class style:
Now we know how to set up the routing for a feature area, and for the component inside that feature as well.
Subscription and Data Display
When we navigate to the Owner Actions menu, we want to show all of the owners to the user. So, when the owner list component loads, the application asks the server for them.
We already have our Owner interface created in the previous part, and we use it here.
That said, let’s modify the owner-list.ts file:
import { DatePipe } from '@angular/common';
import { Component, OnInit, inject, signal } from '@angular/core';
import { Owner } from '../../_interfaces/owner.model';
import { OwnerRepositoryService } from '../../shared/services/owner-repository.service';
@Component({
imports: [DatePipe],
selector: 'app-owner-list',
styleUrl: './owner-list.css',
templateUrl: './owner-list.html',
})
export class OwnerList implements OnInit {
owners = signal<Owner[]>([]);
private repository = inject(OwnerRepositoryService);
ngOnInit(): void {
this.getAllOwners();
}
private getAllOwners = () => {
const apiAddress: string = 'api/owner';
this.repository.getOwners(apiAddress)
.subscribe(own => {
this.owners.set(own);
})
}
}
The owners field is a signal, which is a wrapper around a value that notifies Angular when that value changes. We create it with signal<Owner[]>([]), an empty array until the request comes back, and this.owners.set(own) inside the subscription is the one line that fills it.
Reading a signal means calling it, in the class and in the template alike, so the template below writes owners() rather than owners. An Angular 22 application runs without zone.js and its components are OnPush, so nothing polls this component for changes: Angular re-renders it because its template read a signal and that signal changed.
To accomplish that, let’s replace that test paragraph with the real table, in owner-list.html:
<div class="row">
<div class="offset-10 col-md-2 mt-2"> <a href="#">Create owner</a> </div>
</div> <br>
<div class="row">
<div class="col-md-12">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Owner name</th>
<th>Owner address</th>
<th>Date of birth</th>
<th>Details</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
@for (owner of owners(); track owner.id) {
<tr>
<td>{{owner.name}}</td>
<td>{{owner.address}}</td>
<td>{{owner.dateOfBirth | date: 'dd/MM/yyyy'}}</td>
<td><button type="button" id="details" class="btn btn-primary">Details</button></td>
<td><button type="button" id="update" class="btn btn-success">Update</button></td>
<td><button type="button" id="delete" class="btn btn-danger">Delete</button></td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
We use some basic Bootstrap classes to create a table showing the owner’s data. Inside that table, @for loops over owners(), and track owner.id tells Angular which row is which when the collection changes. Then, by using interpolation {{}}, we show owner properties on the page. For the dateOfBirth property we use the date pipe, | date: 'dd/MM/yyyy', to format it the way we want to see it on a screen, and the component imports DatePipe for it, because a standalone component brings in whatever its own template uses.
We could split this table into a parent component and a child component. It stays as one here, because the owner list is all this part needs.
In our application we are going to use the date format MM/dd/yyyy, but here we use dd/MM/yyyy just to demonstrate the way to change the format with pipes without too much effort.
Now we can start our server application, the API whose write actions we added in part 6, where the POST, PUT and DELETE actions are built. Once it runs, we can start our Angular app and navigate to the Owner Actions link:
When Do We Use loadComponent or @defer?
loadChildren is the right tool for a feature area with several routes. A route that has no children of its own can point loadComponent at a component file directly, and the router loads that one component on first navigation.
The Angular documentation shows both on one route: loadComponent for the area’s shell and loadChildren for the routes that render inside it. Neither form needs .then(m => m.SomeModule) to pull a class off an imported module.
The saving is a file per feature. The older shape needed a feature module and a feature routing module before one component could load lazily. The newer one needs a routes file, and the components name their own dependencies.
Deferrable views cover what routing cannot. A @defer block loads its contents on a trigger such as viewport or interaction, so a heavy chart far down a page arrives when the reader scrolls to it rather than when the route activates.
| Route property | What it loads | When the code is fetched | Status today |
|---|---|---|---|
component: OwnerList | one component, imported normally | with the bundle that declares the route | fine for small routes every visitor reaches |
loadComponent: () => import('./owner/owner-list') | one standalone component | on first navigation to that route | the standalone default for a leaf route |
loadChildren: () => import('./owner/owner.routes').then(m => m.routes) | a child Routes array and everything it names | on first navigation into the child path | the standalone way to lazy load a feature area |
loadChildren: () => import('./owner/owner.module').then(m => m.OwnerModule) | an NgModule and everything it declares | on first navigation into the child path | works, but Angular recommends standalone components instead of NgModule for all new code |
@defer { } in a template | a component and its dependencies | on the block's trigger: idle, viewport, interaction, hover, immediate, timer or when | template-level deferral, for content that is not its own route |
One error message is worth knowing before converting an older feature. Point loadComponent at an NgModule and the Angular router’s route-configuration check refuses it, in a message that names the fix: “You are using ‘loadComponent’ with a module, but it must be used with standalone components. Use ‘loadChildren’ instead.”
Conclusion
Lazy loading is a small change to one route that decides how much code a first-time visitor has to download. The owner feature now lives in its own folder behind its own routes file, and none of it reaches the browser until somebody opens it.
In this post we have learned:
- What lazy loading is, and that it is configured on routes rather than on components
- How to put a feature behind
loadChildrenso its code arrives on first navigation - How child routing works under a parent path, and why the wildcard route stays last
- How to hold the fetched owners in a signal and display them with
@for, formatting a date with the date pipe - When
loadComponentsuits a route better thanloadChildren, and when@deferfits better than either
In Angular Error Handling: HTTP Errors and Error Pages, we handle the failures these requests can return, and build the details page for a single owner.
This series follows the conventions collected in our Angular best practices guide, which is worth a read before starting a project of your own.
Tested with Angular 22.1.5, Angular CLI 22.1.7, ngx-bootstrap 22.0.0, Bootstrap 5.3.8, TypeScript 6.0.3 and Node 24.




If you get results from the API but nothing shows up in the table except for buttons, you need to change the case on the Owner object properties to capitalize the first letter, which is how it is being returned by the API. As soon as I did that, the data appeared and the cells were not blank.
I double-checked things and tried various suggestions, but I can’t get beyond this error when trying to view the owner list
“Media type not present. Please add Accept header with the required media type.”
I see the code in owner-repository.service.ts generates headers for post and put, but not for get.
Just want to add that my code includes changes from https://code-maze.com/hateoas-aspnet-core-web-api/ which is what is throwing the error. It seems that it expects an Accept header from the angular call, which is not provided for Get.
I solved the issue by adding headers to the Get request
const headers = { ‘Accept’: ‘application/json’ };
return this.http.get<Owner[]>(this.createCompleteRoute(route, this.envUrl.urlAddress), { headers });
hi, the data you get in the owner list component name address dob etc where does that data come from we created an interface but i’m confused where the data comes from
an json serveror anything
Hello. This article is part of the Angular series, you can find the link to see all the articles from the series at the top of this article.
Now, in the previous article, we have created a repository file that sends an HTTP request to the API. In this one, once you create the OwnerList component, inside the .ts file, we call that method from the repository with:
private getAllOwners = () => { const apiAddress: string = 'api/owner'; this.repository.getOwners(apiAddress) .subscribe(own => { this.owners = own; }) }As you can see the “this.repository.getOwners” will call the method from the repository and subscribe to the result.
So, this is where we get our data.
Hello Ben. My suggestion. Go through each article from the series and clear the cache on each post CTRL+F5. You are seeing some cached things currently from the previous posts. Once you clear the cache and visit the previous article in the series, the one working with HTTP, you will find both owner repository and the owner interface created.
Hi Marinko, I just tried Chrome Incognito and sure enough, the missing steps are there. 🙂 In my other Chrome tabs I can open Chrome Dev Tools then right-click-hold the refresh button and click Empty Cache and Hard Reload.
Thank you for your guidance and support.
Just curious… is it possible to please remove my large profile pic from the previous post? I did not realize it would come through as “larger than life” per se… ha, I don’t need to promote “self”. I was trying to have it appear as the smaller post pic on the left side but I can’t seem to figure out where you set that option on this blogging/comment platform.
Best regards.
Done. If you still see it, just clear the cache. All the best.
Thank you.
Hi there, I’ve been traipsing through the various series’ for several weeks now… very nice, lots to learn, that’s for certain! Actually, I was having to fit in some fixes on my end since I’d been using Angular 13, and was having issues with Bootstrap 5 & jQuery.
But then lo and behold yesterday I happened to either refresh one of the tut browser tabs I’ve had opened for days, or I checked the git repo, and noticed the source was updated 5 hours before to Angular 13! What! 🙂 It was a nice surprise. I started comparing the old guide tutorial notes against the new, Parts 7, 8, 9, etc, and found too many differences that I just started over at Angular Part 7 instead of trying to piecemeal in and retrofit the code blocks.
No worries though, still thankful just the same.
Best regards.
Hello Ben. Yes, I’ve cimpletely recreated this Angular series. I am sorry that it got you in the middle of the way but trust me this one is so much better. No JQuery or JqueryUI libraries. Now, you have ngx-bootstrap that provides only angular components, so we are Angular all the way. Much cleaner and better solution. I am pretty sure you will find it that way as well.
Thanks for a very good series. Dont know why when I run api alone it returns data but when I run angular form it just shows headers of the table and no data /Owners is returned. Any clue ?
Well, it can be many things. Have you tried opening developer tools and inspecting the Network tab once you send the request, probably you can find some info there? Also, try downloading our source code and comparing it with yours, maybe you missed something. Check if the request from your angular app even gets to the API’s endpoint… As I said, there can be many reasons.
Thanks for your quick response. Actually its status is pending due to unknown reason. Whole page is rendered except this get request. HTTPS issue ?
I can’t really be sure. Have you tried comparing it to our project? I never faced something similar with our project.
Issue is resolved. Actually port number which returns data when VS IIS express is run when pasted in the browser cannot be used in angular enviornment class. Changed webapi output to console application and got the actual port from console and used that in enviornment class.
Thanks for your timely responses
Hi, I have successfully completed pt10 of this series. When I run the server and client I get a CORS policy error in the console and no data is retrieved from the API. Is there something wrong with the Startup.cs and ServiceExtensions.cs code? It should work out of the box but doesn’t. I am using .NET 5
Hello James. It should work as shown in our articles. Have you tried comparing your solutions with ours? You can also download our source code (part 6 for the server app and part 10 for the client app) and test it on your machine.
Hi Marinko, you are correct. The problem is solved as my launchSettings.json was wrong. Thankyou.
For anyone else having this issue, I had to modify the following to use “name:” and then move app.UseCors(“CorsPolicy”) above other middleware.
options.AddPolicy(name: “CorsPolicy”,
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
Thanks Sam B,
I had the same issue and I did not know what to do as everything I did was the same as mentioned here in this great series, you made my day!
Thanks Again and many thanks for Marinko! you’re Awesome!
My code is written link this in app-routing.module.ts
import { NgModule, Component } from ‘@angular/core’;
import { Routes, RouterModule } from ‘@angular/router’;
import { HomeComponent } from ‘./home/home.component’;
import { MenuComponent } from ‘./menu/menu.component’;
import { NotFoundComponent } from ‘./error-pages/not-found/not-found.component’;
//import { OwnerModule} from ‘./owner/owner.module’
const routes: Routes = [
{ path: ‘home’, component: HomeComponent},
{ path: ”, redirectTo: ‘/home’, pathMatch: ‘full’ },
{ path: ‘404’, component : NotFoundComponent},
{ path: ‘**’, redirectTo: ‘/404’, pathMatch: ‘full’},
{ path: ‘owner’, loadChildren: () => import(‘./owner/owner.module’).then(m => m.OwnerModule) },
];
and in owner-module.ts
import { NgModule } from ‘@angular/core’;
import { CommonModule } from ‘@angular/common’;
import { OwnerListComponent } from ‘./owner-list/owner-list.component’;
import { RouterModule } from ‘@angular/router’;
@NgModule({
declarations: [OwnerListComponent],
imports: [
CommonModule,
RouterModule.forChild([
{ path: ‘list’, component: OwnerListComponent }
])
]
})
but it is redirect me on the error page, can you help me how to resolve and where I doing mistake, because I am unable to find the error and any warning as well as solution. I used routing file separately as par tutorial https://code-maze.com/angular-material-navigation/#creatingroutes
I can try to help you.
In the app-routing.module file you don’t have a CommonModule imported and in the imports array. Then, could you place your lazy loading to the second place, right below the route to the home component in the routes array in the same file. Also if you want to be completely sure that you are doing the right thing, please visit this repo and inspect these three files: https://github.com/CodeMazeBlog/angular-material-series/tree/material-table/ang-material-OwnerAccount/src/app
https://github.com/CodeMazeBlog/angular-material-series/blob/material-table/ang-material-OwnerAccount/src/app/owner/owner.module.ts
https://github.com/CodeMazeBlog/angular-material-series/blob/material-table/ang-material-OwnerAccount/src/app/owner/owner-routing/owner-routing.module.ts
Here you will find the lazy loading implemented with all the separate files, so you could compare with your code.
Modules, components, services, declarations, subscriptions… Oh man! 🙂
I never ever used angular for my frontend and it seems it’s developed especially for people who wants more control over whats happening on frontend side.
Probably I’ll finish the angular part and read it again. 🙂
Anyway thanks for great articles.
Hello Mursel. Just take your time and go step by step. It has a lot of things to learn, but it is a great framework, for me, the best one (Just personal opinion). I hope you will enjoy entire series. Best regards.
Doesn’t the “RepositoryService” be added into the owner.module.ts file? as a provider?? it throw an exception and adding the repo solved. Kindly clarify
Hello. You don’t need to add it to the owner.module.ts file. What so ever, you don’t need to add it to the app.module file (from angular 6+ version) because it is already provided in the root of your app. For more clarification, please read the previous article in this series, or at least this complete part: http://34.65.74.140/net-core-web-development-part9/#angularServices All the best
Is it not considered best practice to retrieve data within a resolve method in the router?
Hello ccpony. It is a good practice. You need a separate resolver service to do that. A little more steps but it works fine because a data is fetched prior navigating to our page.
Greetings Marinko and Vladimir I am amazed by the quality of the learning code you provide!
At this point I also created the Accounts List and all the data are being displayed perfectly. Although I wanted to show on the three collumns also the Name of the Account owner. So I went back to create the new API Call.
My solutions were a little dumb and provided 1) a foreach 2) an IQueryable join which I did not like.
How would you solve joining the tables?
Hello Akis. If you remember our previous conversation, where I explained to you how to populate AccountExtended object, the solution to this problem is the same. You need one request in which you will collect a list of AccountExtended objects (in the same way as I have explained to you in our conversation). Then on the client side you are going to have an array of AccountExtended objects where you can iterate through it and display an Owners name something like this: account?.owner.name… (for example)
Hope this helps. Just keep it going with a hard work, it always pays of.
Solid article.
Could be just in my case and it’s really small issue, easily solved by everyone, but this line inside owner-list.component.ts causes error
“import { RepositoryService } from ‘app/shared/services/repository.service’;”
error:
“ERROR in src/app/owner/owner-list/owner-list.component.ts(2,35): error TS2307: Cannot find module ‘app/shared/services/repository.service’.”
solution, use this path:
“import { RepositoryService } from ‘./../../shared/services/repository.service’;”
Hello mate. Well, I am using the AutoImport extension in Visual Studio Code and it is performing all the imports for my side. And, to this day I have never noticed that it is offering two paths: one like mine and one like yours. For me, both are working correctly but I believe you are right. All my other imports in that file are similar to your path “./../..” so I am going to modify this in the article as well.
Thank you very much for the suggestion.
Great tutorial!
One thing that is missing is that you need to add RepositoryService to the providers array in app.module.ts
providers: [
EnvironmentUrlService,
RepositoryService
],
Hi Bill. First of all thank you for the reading and commenting. You are totally right, I forgot to mention that part. You must include the RepositoryService inside the app module. Thank you very much for this suggestion, I am going to fix this in the HTTP part of this series. I am glad you like the tutorial series.
All the best mate.
You don’t need this if you use Angular 6+ method of providing services:
@Injectable({
providedIn: ‘root’,
})
when i click on link owner action its happened :
core.js:1448 ERROR Error: Uncaught (in promise): TypeError: __webpack_require__.e is not a function
TypeError: __webpack_require__.e is not a function
at webpackAsyncContext (eval at ./src/$$_lazy_route_resource lazy recursive (main.bundle.js:13), :11:29)
at SystemJsNgModuleLoader.loadAndCompile (core.js:6558)
at SystemJsNgModuleLoader.load (core.js:6542)
at RouterConfigLoader.loadModuleFactory (router.js:4589)
at RouterConfigLoader.load (router.js:4569)
at MergeMapSubscriber.eval [as project] (router.js:2061)
at MergeMapSubscriber._tryNext (mergeMap.js:128)
at MergeMapSubscriber._next (mergeMap.js:118)
at MergeMapSubscriber.Subscriber.next (Subscriber.js:92)
at ScalarObservable._subscribe (ScalarObservable.js:51)
at webpackAsyncContext (eval at ./src/$$_lazy_route_resource lazy recursive (main.bundle.js:13), :11:29)
at SystemJsNgModuleLoader.loadAndCompile (core.js:6558)
at SystemJsNgModuleLoader.load (core.js:6542)
at RouterConfigLoader.loadModuleFactory (router.js:4589)
at RouterConfigLoader.load (router.js:4569)
at MergeMapSubscriber.eval [as project] (router.js:2061)
at MergeMapSubscriber._tryNext (mergeMap.js:128)
at MergeMapSubscriber._next (mergeMap.js:118)
at MergeMapSubscriber.Subscriber.next (Subscriber.js:92)
at ScalarObservable._subscribe (ScalarObservable.js:51)
at resolvePromise (zone.js:809)
at resolvePromise (zone.js:775)
at eval (zone.js:858)
at ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:4740)
at ZoneDelegate.invokeTask (zone.js:420)
at Zone.runTask (zone.js:188)
at drainMicroTaskQueue (zone.js:595)
at ZoneTask.invokeTask [as invoke] (zone.js:500)
at invokeTask (zone.js:1517)
Hi Leonardo. Thank you very much for reading the post. About the error. It is so large, one of those generic errors in the Angular and it doesn’t say to much. I have tried to scramble my code in so many ways to reproduce this error but I couldn’t do it 😀 So I have two suggestions for you. First one is to download the source code from this post and to compare it with your own. Second one is to upload your code somewhere and to share link with me, then I will take a look at it and will try to find the origin of this error. I would like to hear from you. All the best.
ok i Will try this.
vou will continue the post part 11 ?
Yes of course. In Monday the part 11 is going to be published. It is going to be continuation to this part. If you like my posts and find them useful, you may subscribe to the Code-maze. Then you are going to be notified when ever new article is published. Thank you one more time for reading our posts.
I cannot wait for the rest of the posts. I like of style of writing. You make hard to understand concept very easy to comprehend. Is there a reason you did not use the SPA Angular template in Visual Studio? Does the template offers any benefits than the app created by the cli? One thing I noticed about the template, it might not be suitable for large project since the front-end and the back-end are all in one project.
Hello Sumu. I am very glad that you like our posts and find them useful. Thank you very much for the reading and for all the nice words. About your question, well… you gave the right answer 😀 I am not using the VS template because I like to keep client side coding complete separate from the server side. So the main reason is the separation of concerns. I am not saying that VS template for angular is bad or anything like that, just this is my style and how I prefer to write a project.