Looking for pointers on routing between feature modules in angular - javascript

I was working on a big project of mine in angular, discovered feature modules and routing modules, then tried to implement that in order to better organize the project. When I did this, the app became very disfunctional. Since then, i made this test project to try to implement routing between feature modules, on a smaller, more managable scale.
This test project works, but there are some small problems that I know will cause issues down the line, and id like to resolve.
There are two big problems as I see it:
<a routerLink="some/link> does not work in feature modules, only app module: it renders in the markup as plaintext with no working link. I tried importing routerLink to the feature modules module.ts file, as a last ditch effort, but still nothing.
I was hoping that routing to a feature module, if configured that way, could display different mark up and styling, for example- routing to module-a shows one navigation menu, and routing to module-b shows another. Instead, the default behaivor happens- app.component is displayed everywhere, and routing
to a feature module makes the url specified component appear in place of router-outlet. Id like to disable this default behaivor if possible, so that components routed to in one feature module have one set of styles and features, and components routed to in another module have different styling and features- as if router-outlet recognizes that feature-a/component is part of feature-a, and in turn loads that modules' html and css as the app.component instead of the root app.component.
Attached the source code below, for this test project. I only included source for module feature-a, as feature-b is in essence the same thing with different text, to prevent unneeded cluttering
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FeatureAModule } from './feature-a/feature-a.module';
import { FeatureBModule } from './feature-b/feature-b.module';
#NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
FeatureAModule,
FeatureBModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
App.routing.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { ChalpComponent } from './feature-a/chalp/chalp.component';
import { FeatureAComponent } from './feature-a/feature-a.component';
import { FeatureBComponent } from './feature-b/feature-b.component';
import { SkoneComponent } from './feature-b/skone/skone.component';
const routes: Routes = [
/* { path: 'feature-a', component: FeatureAComponent,
children: [
{ path : 'feature-a/chalp', component: ChalpComponent }
]
},
{ path: 'feature-b', component: FeatureBComponent,
children: [
{ path : 'feature-b/skone', component: SkoneComponent }
]
}
*/
{ path : 'feature-a/chalp', component: ChalpComponent },
{ path : 'feature-b/skone', component: SkoneComponent },
{ path: 'feature-a', component: FeatureAComponent },
{ path: 'feature-b', component: FeatureAComponent },
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
markup for app.component:
<h1>Inside App-Module now!</h1>
Go to feature A for chalp: <a routerLink="feature-a/chalp">Chalp</a>
Go to feature B for Skone: <a routerLink="feature-b/skone">Skone</a>
<router-outlet></router-outlet>
feature-a routing + module file
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { RouterModule, Routes, RouterOutlet, RouterLink } from '#angular/router';
import { FeatureAComponent } from './feature-a.component';
import { ChalpComponent } from './chalp/chalp.component';
const routes : Routes = [
{ path : '', component : FeatureAComponent },
{ path : 'chalp', component: ChalpComponent}
]
#NgModule({
declarations: [ChalpComponent],
imports: [
CommonModule,
RouterModule.forChild(routes)
]
})
export class FeatureAModule { }
chalp- a component within feature-a
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-chalp',
templateUrl: './chalp.component.html',
styleUrls: ['./chalp.component.css']
})
export class ChalpComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
chalp markup
<p>chalp works!</p>
<a routerLink="../">Go back one</a>

The answer is cleaner:
use lazily loaded feature modules.
// root module routing:
router.forRoot([
// this is what ng documentation suggests
{
path: 'admin',
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
},
each of these unique route prefixes
existing already in our bloated application.
now is stored and carefully imported depending
on when the contained resources are required
by the user.
...
])
//then in each feature modules' routing module
router.forChild([
// treat the first word as root, so url is not admin/admin!
{ path: '', component: AdminComponent,
children: [
/*All urls in our app which
have 'admin' prefix*/
]
}
]
Two big lessons out of this excursion into typescript:
always know the framework and the way its designed before using it.
not only do the feature modules handle imports and routing, they also each import
a shared module called util which houses the service main module, all types and interfaces, componentry, pipes and directives that the whole app uses.
In the future, knowing this will help me better design applications. The core app is what
all our feature modules plug into and what gets bootstrapped when the application is served. Now there is a unified structure to how imports and exports are structured, and
you never have a question about how to get to a service or interface.

Related

Angular ssr, page rerender issue

I am new to Angular ssr, you can see code below
Issue
If I enter to client route directly, first it shows
Rendered by server
but quickly after that it rerenders page and shows:
Rendered by browser
I think I know why this happens, but to be sure can someone provide a good explanation? also, Can I somehow avoid this behavior and force browser to render html that comes from server? should I worry about this?
client.component.ts
#Component({
selector: "client",
template: "<p>Rendered by {{ renderer }}</p>",
styleUrls: ["./dumco.component.css"]
})
export class ClientComponent implements OnInit {
renderer: string;
bla: any = [];
constructor(private http: HttpClient, #Inject(PLATFORM_ID) platformId: any) {
this.renderer = isPlatformBrowser(platformId) ? "Browser" : "Server";
}
}
app-routing.module.ts
import { NgModule } from "#angular/core";
import { RouterModule, PreloadAllModules } from "#angular/router";
import { AppComponent } from "./app.component";
import { CompfComponent } from "./compf/compf.component"
import { HomeComponent } from "./home/home.component"
export const appRoutes = [
{
path: "",
component: HomeComponent
},
{
path: "comp",
component: CompfComponent
},
{
path: "client",
loadChildren: () => import("./client/client.module").then(m => m.ClientModule),
data: { title: "Static Data - Clients" }
},
];
// preloadingStrategy: PreloadAllModules,
#NgModule({
imports: [RouterModule.forRoot(appRoutes, { onSameUrlNavigation: "reload", initialNavigation: 'enabled' })],
exports: [RouterModule]
})
export class AppRoutingModule { }
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule } from "#angular/common/http";
import { AppRoutingModule } from "./app-routing.module"
import { AppComponent } from './app.component';
import { CompfComponent } from './compf/compf.component';
import { HomeComponent } from './home/home.component';
import { TransferHttpCacheModule } from "#nguniversal/common"
#NgModule({
declarations: [
AppComponent,
CompfComponent,
HomeComponent
],
imports: [
HttpClientModule,
TransferHttpCacheModule,
BrowserModule.withServerTransition({ appId: 'serverApp' }),
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
app.server.module.ts
import { NgModule } from '#angular/core';
import { ServerModule, ServerTransferStateModule } from '#angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
This is normal behaviour for angular universal. Here is the normal flow:
You make a request to the server
Angular universal creates and renders the components (including API calls) server side to generate the HTML content. The content is sent back to the client browser. (In your example, the HTML will contain "rendered by server")
The browser renders the HTML.
Once the page is rendered and the dom document loaded, the client side angular application is boostrapped.
The client side angular app creates and render components (and makes API calls). It will render "rendered by browser" in your case.
You do not really have to worry about this. In a real situation, you'd have your components make API calls. To prevent the client side making the same calls that have already been made server side, you can use angular TransferState to serialise the API data in the HTML generated server side, so that the client side can use that data straight away instead of making API calls again.
That way, the HTML generated by the client should be the same as the one that came from the server. (Unless of course you specifically display different data server and client side, like in your example)

Angular 7 with adding new component 'the server component' isn't appear in browser when I add a component tag?

It is first time To use Angular Now I'm learning angular 7:
- I create a new custom component in
- |src folder
---| app
----|| server (name of my component)
then I added in app.component.html the name of component
I see in the tutorial show in browser
the server component
even if he add it empty element
I do all steps in
server.component.ts file
import { Component } from '#angular/core';
#Component({
selector:'app-server',
templateUrl: './server.component.html'
})
export class ServerComponent {
}
& app.module.ts file
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { ServerComponent } from './server/server.component';
#NgModule({
declarations: [
AppComponent,
ServerComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Have you define your <app-server></app-server> somewhere?
For instance in the app.component.html
<app-navbar></app-navbar>
<app-server></app-server>
<app-footer></app-footer>
Also you will find an example here : https://angular.io/tutorial/toh-pt1
In your app.component.html add :
<app-server></app-server>
Thus you'll see its HTML.
What you did is in fact kind of import it and make it visible to other components declared in AppModule, but you didnt call it actually.

Angular route doesn't update to child's path

I have a project with file structure
app/
(all the files that get automatically built)
app-routing.module.ts
components/
layout/
top/
side/
banner/
pages/
home/
prehistory/
prehuman/
australopithecine/
homininia/
earlyHuman/
outOfAfrica/
agriculture/
ancient/
(several directories like in prehistory)
post-classical/
(several directories like in prehistory)
Each directory under pages/ was built in the CLI with ng g c ___ so that it has all the usual files. I'm trying to build the router so that it reflects the directory structure with child routers, so I have in app-routing.module.ts the following code. Note that since I'm at the early stages I haven't fully written out all the children and their sub-children, I just wanted to get a small part of it built and tested before building out the rest.
app-routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { HomeComponent } from './components/pages/home/home.component';
import { PrehistoryComponent } from './components/pages/prehistory/prehistory.component';
export const routes: Routes = [
{path:'', children: [
{path:'prehistory', component:PrehistoryComponent},
{path:'', component:HomeComponent},
]}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
In my AppComponent I used header and other tags to control CSS styles, but the important part is that there's a portion of the screen reserved for the header, for the side, and then the content which is the part that changes based on the address.
app.component.html
<header>
<app-top></app-top>
</header>
<aside>
<app-side></app-side>
</aside>
<content>
<router-outlet></router-outlet>
</content>
And the links are in the TopComponent.
top.component.html
<div><app-banner></app-banner></div>
<div id="breadcrumb">
<nav>
<a [routerLink]="['']" routerLinkActive="active">Home</a>
<a (mouseover)="onHover()" [routerLink]="['prehistory']" routerLinkActive="active">Prehistory</a>
</nav>
</div>
top.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-top',
templateUrl: './top.component.html',
styleUrls: ['./top.component.css'],
})
export class TopComponent implements OnInit {
constructor(private route: ActivatedRoute) {
}
onHover = function() {
console.log(this.route.url._value[0]);
}
ngOnInit() {
}
}
When my browser navigates to /prehistory the page loads correctly but the console prints out "" indicating the root route URL. And when I've also tried logging to console the parent directory, it prints null, confirming that the activated route is representing the root directory even though the page has navigated away from it.
home.component.html
<p>
home works!
</p>
<router-outlet></router-outlet>
prehistory.component.html
<p>
prehistory works!
</p>
<router-outlet></router-outlet>
Using .forChild instead of .forRoot seems to break the page. I am wondering if maybe I need to use a service to pass information around? Like maybe I need a service to somehow collect the current route from the content tag and pass that over to the TopComponent? But I'm not sure how I would get a service that collects the route from the content tag.
I think the most important is to activate this property: useHash: true and then manage the path correctly with the name of each component.
Try changing to this code:
export const routes: Routes = [
{ path: 'home', component: HomeComponent},
{path:'children', component: [
{path:'prehistory', component:PrehistoryComponent},
{path:'**', component:HomeComponent},
]},
{ path: '**', pathMatch: 'full', redirectTo: 'home'}
];
#NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule]
})

Angular 1 and Angular 2 Hybrid Application Routing Issue (angular component not displaying)

I have an Angular 1 and Angular 2 hybrid application that was set-up using the following guides, Upgrading from AngularJS and Migrating Angular 1 Application to Angular 2. My root component looks like this:
import { NgModule, Component } from '#angular/core';
import { RouterModule, UrlHandlingStrategy } from '#angular/router';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { RouterUpgradeInitializer } from '#angular/router/upgrade';
import { MyModule } from './Mymodule/my-module';
export class Ng1Ng2UrlHandlingStrategy implements UrlHandlingStrategy {
shouldProcessUrl(url: any) {
return url.toString().startsWith("/Mymodule");
}
extract(url: any) {
return url;
}
merge(url: any, whole: any) {
return url;
}
}
#Component({
selector: 'root-component',
template: `
<router-outlet></router-outlet>
<div class="ng-view"></div>
`
})
export class RootComponent { }
#NgModule({
imports: [
BrowserModule,
UpgradeModule,
MyModule,
RouterModule.forRoot([])
],
providers: [
{ provide: UrlHandlingStrategy, useClass: Ng1Ng2UrlHandlingStrategy }
],
bootstrap: [RootComponent],
declarations: [RootComponent]
})
export class Ng2AppModule {
constructor(public upgrade: UpgradeModule) { }
}
And my "main.ts" is the following:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { setUpLocationSync } from '#angular/router/upgrade';
import { Ng2AppModule } from "./Angular2/app.rootcomponent";
// This is the entry point for the AngularJS/Angular hybrid application.
// It bootstraps the Angular module 'Ng2AppModule', which in turn bootstraps
// the AngularJS module 'angular1App'.
platformBrowserDynamic().bootstrapModule(Ng2AppModule).then(ref => {
const upgrade = (<any>ref.instance).upgrade;
// bootstrap angular1
upgrade.bootstrap(document.body, ['angular1App']);
setUpLocationSync(upgrade);
});
The module 'MyModule' follows:
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { TestDataListComponent } from './testdata-list.component';
#NgModule({
imports: [
RouterModule.forChild([
{
path: 'Mymodule', children: [
{ path: 'testdata', component: TestDataListComponent }
]
}
])
],
declarations: [TestDataListComponent ]
})
export class MyModule {
}
And 'TestDataListComponent' component is very simple:
import { Component } from '#angular/core';
#Component({
selector: 'test-data',
templateUrl: 'App/Angular2/MyModule/testdata-list.component'.html'
})
export class TestDataListComponent{
}
The way I am linking to the Angular 2 component is in a main menu HTML page. The relevant code is the following:
<li id="main-menu" class="menu-top-level" ng-mouseover="cur = 'mymodule'">
<a ng-reflect-router-link="/Mymodule/testdata" href="#/Mymodule/testdata">
MyModule
<span>
<i class="fa fa-check-square fa-2x"></i>
</span>
</a>
</li>
The problem I am experiencing is that clicking on the link above takes me to a blank view, i.e. the component TestDataListComponent is not displayed. However if I replace the following line in my main Angular 2 module, i.e. Ng2AppModule:
RouterModule.forRoot([])
with:
RouterModule.forRoot([], { useHash: true, initialNavigation: false })
component TestDataListComponent shows up fine, but then when I attempt to navigate back to an Angular 1 component, by clicking on a link, the page is blank and what I noticed is that the URL in my browser window looks like this:
http://localhost:60813/#/
while it should looks like this:
http://localhost:60813/#/myclients
If I click on the link a second time, the URL is correct and the Angular 1 component is displayed fine.
The relevant portion of my Angular 1 module angular1App is:
angular.module("angular1App",
["ngRoute",
"myInterceptorService",
"ngStorage",
"ngAnimate",
"ui.bootstrap",
"ngResource",
"SignalR",
"ui.select",
"ngSanitize"])
.config([
"$routeProvider", "$httpProvider", function($routeProvider, $httpProvider) {
$httpProvider.interceptors.push("myInterceptorService");
$routeProvider
.when('/myclients',
{
title: "Client Data",
controller: "clientDataCtrl",
templateUrl: "/App/Common/Views/clientData.html"
})
What is the problem here? Why is either my Angular 2 or Angular 1 component now showing up?
Your problem with component appears to be here:
import { Component } from '#angular/core';
#Component({
selector: 'test-data',
templateUrl: 'App/Angular2/MyModule/testdata-list.component'.html'
})
export class TestDataListComponent{
}
You need to add correct templateUrl strin
The solution was provided by brandonroberts on the Angular github. For details, see this issue https://github.com/angular/angular/issues/18832
Excerpt from solution: "The way setupLocationSync provides the URL to the router for navigating doesn't work correctly with hash-based routing. What I did was tweak the source from the setupLocationSync to pass the URL from the hash to navigate with. This way the Router will handle navigating both scenarios correctly."
See the Plunker in the issue above for details.

Angular2 "No provider for Service!" error when provider is added #NgModule

I have an app module and single component application (made to demonstrate my problem), and getting following error:
Error in ./AppComponent class AppComponent_Host - inline template:0:0 caused by: No provider for UserService! ; Zone: <root> ; Task: Promise.then ; Value:
code for AppModule:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UserService } from './components/common/userservice';
#NgModule({
imports: [
BrowserModule,
],
declarations: [
AppComponent
],
providers: [UserService],
bootstrap: [AppComponent],
entryComponents: []
})
export class AppModule {
}
Code for my AppComponent:
import { Component} from '#angular/core';
import { UserService} from './userservice';
#Component({
selector: 'App',
template: `<h3>App component</h3>
user name: {{userName}}
`,
providers: []
})
export class AppComponent {
userName: string;
constructor(userService: UserService) {
this.userName = userService.userName;
}
}
My UserService Code:
import { Injectable, EventEmitter } from '#angular/core';
#Injectable()
export class UserService {
obs$: EventEmitter<any> = new EventEmitter<any>()
userName = 'Sherlock Holmes';
}
Now if i add UserService as provider to AppComponent, it will solve the issue. but i dont want to, because i want only one instance of my service in whole application. Even in subModules(feature modules).
according to my understanding, if i add service as provider on module level, then i can just inject it to any component under module.
here is am example i was watching.
Plunker
am using angular2 version: "2.0.0"
The import path is wrong: you use /Common in one and /common right below.
Visual Studio and WebStorm will not show IntelliSense errors for case-sensitivity of paths.
Furthermore, if using Angular 5's AoT template compilation, you can get a "This component is not part of a module" error, even though it is, because the import path is incorrect. Without AoT this will work, so you'll get a surprise when converting to AoT.
Remove your service: UserService from app.module.ts, then add in component:
#Component({
selector: 'App',
template: `<h3>App component</h3>
user name: {{userName}}
`,
providers: [UserService]
})
Hope this will help you.
An additional reason for getting this error - I duplicated a service to a different directory and was updating each file individually. Even though the first file still existed I got this error until I updated app.module.ts.

Categories

Resources