I started using angular2 (with an express.js backend) with the intention of passing control of several sections of a page to angular2 components, but I eventually realized that the system is much more effective when angular2 controls an entire area of a page. However, my application is not a SPA and there are other server-rendered routes aside from the route that holds the selector of my angular2 root component.
I have 2 questions, followed by code snippets:
Whenever I load a server-controlled route whose server-side rendered template does not contain the selector of my root component, <main-comp></main-comp>
, angular2 basically pukes into the console saying it doesn't have anywhere to initialize. I put all of my angular2 initialization code in a shared header for my whole application. Is there a way to tell the angular2 root component not to render itself if its selector simply does not exist on the page?
The server side route that renders the angular2 root component is /writing
and the angular2 routing URL for a particular blog post would be something like /writing/2016/03/post
. However, whenever I browse straight to the URL, System.js tries to load app/boot relative to that location, i.e., /writing/2016/03/app/boot
, which obviously fails with a 404. I think this is happening because my server controls the /writing
route and angular loads relative to the base URL being requested to the server. Thus, the only solution I know of would be to turn my application into an SPA with a single server route being '/'
, which would require lots of rewriting to the front end. How can I get around this issue?
This is my root component:
@Component({
selector: 'main-comp',
template: '<router-outlet></router-outlet>',
providers: [TagService, PostService, BlogStateService],
directives: [ROUTER_DIRECTIVES, BlogComponent]
})
@RouteConfig([
{path: '/writing/...', name: 'Blog', component: BlogComponent }
])
export class AppComponent {
constructor(private _blogState : BlogStateService,
private _postService : PostService) {
}
}
And here is the <head>
element:
<head>
<script src="/js/jquery.min.js"></script>
<script src="/js/modernizr-2.6.2-respond-1.1.0.min.js"></script>
<!-- Angular Dependencies -->
<script src="/app/node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="/app/node_modules/es6-shim/es6-shim.js"></script>
<script src="/app/node_modules/systemjs/dist/system.src.js"></script>
<script src="/app/node_modules/moment/moment.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
},
moment: {
main: 'moment.js',
type: 'cjs',
defaultExtension: 'js'
}
},
map: {
moment: '/app/node_modules/moment/'
}
});
</script>
<script src="/app/node_modules/rxjs/bundles/Rx.js"></script>
<script src="/app/node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="/app/node_modules/angular2/bundles/http.dev.js"></script>
<script src="/app/node_modules/angular2/bundles/router.dev.js"></script>
<script>
System.import('app/js/boot').then(null, console.error.bind(console));
</script>
<base href="/">
</head>
For the first problem, I added a check for the existence of <main-comp>
on the page before calling the bootstrap method (Thanks @Gunter for the idea).
For my second problem from above, I added a path to my boot file in the system.js configuration, as follows, so that system.js isn't loading app/boot relative to the request URL.
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
},
moment: {
main: 'moment.js',
type: 'cjs',
defaultExtension: 'js'
}
},
map: {
app: '/app', //<----------added this line
moment: '/app/node_modules/moment/'
}
});
1) I guess you would need to manually check before the call to bootstrap()
and then skip bootstrap()
if the selector isn't found.
2) Setting the base URL using the <base>
tag or providing APP_BASE_HREF
might fix that. See also Angular 2 router no base href set
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With