I have an Angular 9 app hosted in a domain subfolder. This is reflected in the index.html base tag, which looks like this:
<base href="/subfolder/">
The app behaves perfectly when run in local, without base subfolder:
<base href="/">
When the routing is accessed directly, the specific subpage loads correctly, like so:
http://localhost:4200/user/user-1
But that is not the case in production, where when trying to access the subpage directly, will result in a 404.
My routing module looks like this:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { ExhibitorMainViewComponent } from './exhibitor-main-view/exhibitor-main-view.component';
import { ExhibitorDetailsComponent } from './exhibitor-details/exhibitor-details.component';
const routes: Routes = [
{
path: '',
component: ExhibitorMainViewComponent,
},
{
path: 'user/:userId',
component: ExhibitorDetailsComponent
},
];
#NgModule({
imports: [RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled'
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
I'm pretty sure is related to the subfolder, but I couldn't debug it yet.
Any pointers?
Try to specify the path of your subfolder when launching a build
ng build --prod --deploy-url /subfolder/ --base-href /subfolder/
Related
I was just trying to build a module/component and serve it as a JS bundle. it builds:
#Component({
selector: 'app-component-overview',
template: '<button><ng-content></ng-content></button>',
})
export class ButtonComponent {}
#NgModule({
declarations: [ButtonComponent],
})
export class AppModule {}
the issue is that after building it to a javascript bundle. when I try to import it in another angular project. I get:
//ERROR in ... error TS2306 '...' is not a module.
loadChildren: () => import('...').then(m=>m.ButtonModule)
maybe I am missing the point here and things are different in angular, having webpack control makes it a lot easier but I want to avoid bringing custom builders and fiddling with the settings as little as possible.
Question is, is there a well documented way to bundle a module or even a standalone component to be used outside my project as a JS bundle? I could not find anything useful other than high level explanation of reusing and lazyloading thing already inside the same project.
It seems you are building this module as part of an application which is supposed to run in a browser. But what you are looking for is to build this module as part of a library which can be re-used in other projects.
See this official Angular guide on how to create a library.
After a few hours browsing around. I figured it out.
The answer that #json-derulo gave is part of the solution but there are more steps to make this work.
follow the guide: https://angular.io/guide/creating-libraries
here is the tricky part if you import the lib inside the workspace it will work. but that does not make much sense. You likely have another repository with a angular app that you want to consume the lib.
now to be able to import the this component as a lazy loaded route you will need to add "#angular/router" to the lib peerDependecies and run npm install again
now create a routing module and add the empty path to point to the component in the lib.
//my-lib-routing.module.ts
import { NgModule} from "#angular/core";
import { RouterModule } from "#angular/router";
import { MyLibComponent } from "./my-lib.component";
const routes= [
{ path: '', component: MyLibComponent },
];
#NgModule({
imports: [
RouterModule.forChild(
routes,
)
// other imports here
],
// providers: [RouterModule],
exports: [RouterModule]
})
export class myLibRoutingModule { }
change the libmodule to import the routing module
//
import { NgModule } from '#angular/core';
import { myLibRoutingModule } from './my-lib-routing.module';
import { MyLibComponent } from './my-lib.component';
#NgModule({
declarations: [MyLibComponent],
imports: [
myLibRoutingModule
],
})
export class MyLibModule { }
on the lib project root run 'npm run build'.
create a separate repo and "ng new app"
add the lib to the package.json of the main app, something like "my-lib": "../my-lib-ws/dist/my-lib".
open angular.json and add "preserveSymlinks": true to the build options.
run npm i to link the lib as a dependency.
add the libmoudle as a lazy loaded route
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
const routes: Routes = [
{path: 'test', loadChildren: () => import('my-lib').then(m=>m.MyLibModule)}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
run npm run start. and try to access the 'test' route. you should see the component loaded in the main app router outled
Now, this is more of learning exercise the bundles almost gets doubled. and you would profit more using module federation. But it is a use case we have. Anyhow the steps are here if anyone can't reproduce let me know.
Current behavior
I declared those dynamic components as entry components in the module where I also want to render them. With JIT it works fine.
Following structure has the part of my app I want to render them: app -> home (lazy) -> contracts (lazy) -> search.
So I added those components to the module I use for the search component/route. When I'm compiling with AOT, everytime I visit the search route, the app tells me there is no component factory. Of course I searched google and found some results:
I tried adding them to the ANALYZE_FOR_ENTRY_COMPONENTS provider, I tried to import a ModuleWithProviders with .forRoot() in my app.module and I also tried simply importing and declaring my dynamic and all of its dependant components in the root module (app.module). Everything resulting in the same error.
I declare my dynamic components as entry components like so:
#NgModule({
imports: [SharedComponentsModule, FinoSchemaFormsModule, TabGroupModule, FinoInputModule],
declarations: [EnergySearchSettingsComponent, DslSearchSettingsComponent, MobileSearchSettingsComponent, ComparisonDetailSectionComponent],
entryComponents: [EnergySearchSettingsComponent, DslSearchSettingsComponent, MobileSearchSettingsComponent],
exports: [EnergySearchSettingsComponent, DslSearchSettingsComponent, MobileSearchSettingsComponent, ComparisonDetailSectionComponent],
providers: [CategoryMappingProvider]
})
export class ComparisonComponentsModule { }
This module gets imported in the SearchModule, where also my SearchComponent is declared. In this component I want to render those components dynamically using the ComponentFactoryResolver I inject in the SearchComponent.
ngOnInit() {
this.searchSettingsComponent = this.comparisonService.getSearchComponent(); // returns either EnergySearchSettingsComponent, DslSearchSettingsComponent or MobileSearchSettingsComponent
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(searchSettingsComponent);
this.searchSettingsComponent = this.searchContainer.createComponent(componentFactory);
this.searchSettingsComponent.instance.comparisonSettings = comparisonSettings;
this.searchSettingsComponent.instance.onSave.subscribe(settings => this.saveSearchSettings(settings));
}
The SearchComponent is the routing component of the search route, which is a child route of my contract route, which gets lazy loaded. This again is a child route of my home route (also lazy loaded) and this belongs to the main route.
Environment
Angular version: 5.2.4
For Tooling issues:
- Node version: 8.11.3
- Platform: Mac
It must be pretty simple. Just create the SharedModule and put all reusable dynamic component in it, export those components from SharedModule and import this Module in all required. Lazy loaded Modules.
Since it is imported direct to the Module, it must be available while creating the Dynamic Component.
Have you tried updating angular to latest 6.1.10? With version 5 I had issues with lazy loaded modules.
I had a similar task, and it worked fine under 6.1.4.
I've created a working example for you under 7.0.1
I've created both cases
Dynamic component is declared in the module which will create the dynamic component
Dynamic component is declared in a shared module and imported in the lazy-loaded module which will create dynamic components. You can create a shared module for every dynamic component, so you import only one component in a lazy loaded module
I don't feel as though there is enough information in your question to give you the exact answer to the problem you are facing.
I was able to create a solution with, what I feel is a similar setup to yours that you could use to solve your problem or to ask a more pointed question.
TLDR: Full GitHub repo here
I created an app structure as follows:
app/
app.module
app.component
/dynamic-provider --contains component that is dynamically loading other components
--module is lazy loaded by dynamic-one module
dynamic-loader.module
slot.component
/dynamic-one --contains lazy loaded module
--module is lazy loaded by app module
dynamic-one.module
/dynamic-loader --contains a component to be dynamically loaded
dynamic-provider.module
one.component
provider.service
app.module looks as follows
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
RouterModule.forRoot([
{ path: 'dynamic-loader', loadChildren: './dynamic-one/dynamic-one.module#DynamicOneModule' },
{ path: '', component: AppComponent }
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
dynamic-one.module looks as follows
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
#NgModule({
imports: [
RouterModule.forChild([
{ path: '', loadChildren: '../dynamic-loader/dynamic-loader.module#DynamicLoaderModule' }
])
]
})
export class DynamicOneModule {
constructor() {
console.log('one');
}
}
dynamic-loader.module looks as follows
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { DynamicProviderModule } from '../dynamic-provider/dynamic-provider.module';
import { SlotComponent } from './slot.component';
#NgModule({
declarations: [ SlotComponent ],
imports: [
DynamicProviderModule,
RouterModule.forChild([
{ path: '', component: SlotComponent }
])
]
})
export class DynamicLoaderModule { }
dynamic-provider.module looks as follows
import { NgModule } from '#angular/core';
import { OneComponent } from './one.component';
import { ProviderService } from './provider.service';
#NgModule({
declarations: [ OneComponent ],
entryComponents: [ OneComponent ],
exports: [ OneComponent ],
providers: [ ProviderService ]
})
export class DynamicProviderModule { }
As you state, your dynamic creation of components is working when the module isn't loaded, so I haven't included that code here(though it is in the repo for completeness). As can be seen here though, the app module lazy loads the dynamic-one module which in turn lazy loads the dynamic-loader module. The dynamic-loader module dynamically creates components from the dynamic-provider module.
How this differs from your implementation is very hard to tell as you have provided only a small amount of information. I hope this helps you find the missing piece you are looking for though!
Creating shared modules allows you to organize and streamline your
code. You can put commonly used directives, pipes, and components into
one module and then import just that module wherever you need it in
other parts of your app.
By re-exporting CommonModule and FormsModule, any other module that imports this SharedModule, gets access to directives like NgIf and NgFor from CommonModule and can bind to component properties with [(ngModel)], a directive in the FormsModule.
EX:
import { CommonModule } from '#angular/common';
import { NgModule } from '#angular/core';
import { ReactiveFormsModule } from '#angular/forms';
import { SharedModule } from '../../shared/shared.module';
import { EntryModalComponent } from './entry-modal.component';
#NgModule({
imports: [
CommonModule,
SharedModule,
ReactiveFormsModule
],
declarations: [ EntryModalComponent ],
entryComponents: [ EntryModalComponent ],
exports: [ EntryModalComponent ]
})
export class EntryModalModule { }
Now you can use this EntryModalComponent for dynamic loading in some other component after importing the module where it's defined.
In the latest versions Angular has updated a lot of staff about lazy loaded modules. And with high probability this trouble is fixed now.
I'm trying to create a package using
Angular library starter
Everything works fine until I add RouterModule.
The module that causes an issue
import { NgModule, ModuleWithProviders } from '#angular/core';
import { CommonModule } from '#angular/common';
import { ClientMenuComponent } from './client-menu/client-menu.component';
import { ClientMenuItemComponent } from './client-menu-item/client-menu-item.component';
import { RouterModule, Router, Routes } from '#angular/router';
#NgModule({
imports: [
CommonModule,
RouterModule.forRoot([]) <---- Error: AoT compilation failed
],
declarations: [
ClientMenuComponent,
ClientMenuItemComponent,
],
exports: [
ClientMenuComponent,
ClientMenuItemComponent
]
})
export class ClientMenuModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: ClientMenuModule,
providers: []
};
}
static forChild(): ModuleWithProviders {
return {
ngModule: ClientMenuModule,
providers: []
};
}
}
This is the full error I get.
Error during template compile of 'ClientMenuModule'
Function calls are not supported in decorators but 'RouterModule' was called.
Error: AoT compilation failed
The environment that i'm using is
Angular CLI: 6.0.3
Node: 8.11.1
OS: darwin x64
Angular: 6.0.0
Pull out the function call:
export const MyModule = RouterModule.forRoot([]);
#NgModule({
imports: [
CommonModule,
MyModule
],
declarations: [
ClientMenuComponent,
ClientMenuItemComponent,
],
exports: [
ClientMenuComponent,
ClientMenuItemComponent
]
})
If this is a library or a module that will be imported into other libraries/modules you SHOULD NOT call the .forRoot() function when you pass it into the #NgModule.
RouterModule declares and exports some directives, e.g. router-outlet, routerLink, routerLinkActive etc. Also, it provides some services e.g. Router, ActivatedRoute etc. To avoid having multiple instances of services, RouterModule defines two methods, "forRoot" and "forChild". As the name suggests, "forRoot" method should be called only by root module, i.e. app.module, and forChild should be called by other feature modules. This way, you still get to use directives, components, pipes exported by this module and don't get new instances of services.
While this doesn't specifically get the root of your issue, it will likely resolve your issue. (Also this issue may help)
I have the following setup:
in app-routing module:
export const routes: Routes = [
{ path: '', component: AppComponent },
{ path: 'Role', component: RoleComponent }
];
In app.module.ts, I have:
imports: [
RouterModule.forRoot(routes)
],
In app.component.html, I have
Test
view above is an iframe. However, it doesn't find the role component to go to role.component.html page and open it in iframe.
BTW, role component files are in a folder called role under app directory where the app.component.html is.
app > app.component.html, ts, css
app > role > role.component.html, ts, css
And obviously everything compiles fine in VS Code.
Any ideas whats missing?
I have a simple angular routing module that looks like this:
import { NgModule, ModuleWithProviders } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { HomeComponent } from './../../home/home.component';
import { LoginComponent } from './../../login/login.component';
const routes: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'login',
component: LoginComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
The HomeComponent is loaded at first whenever I open the page. everything works fine until I switch to the login page. Both of them have certain parts that work with javascript. I added those in the .angular-cli.json file.
If I switch to the login page using the following button:
<li class="active"><a routerLink="">Home</a></li>
none of those functions seem to work, also whenever I switch back to the homepage component, the functions that did work before won't work here either.
So far i found out that the scripts are loaded once since the main application is loaded using "eager loading" and the compontens use "lazy loading".
Is there a way to load the scripts again or any other way to fix this?
I managed to fix the problem by using the solution given on the following page:
how to load js on component level in angular 4. I don't want to load all js file at app startup
given by Milad.