Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-SPA with angular2

Tags:

angular

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:

  1. 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?

  2. 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>
like image 291
ilana917 Avatar asked Mar 08 '16 09:03

ilana917


2 Answers

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/'
        }
    });
like image 89
ilana917 Avatar answered Nov 10 '22 01:11

ilana917


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

like image 24
Günter Zöchbauer Avatar answered Nov 09 '22 23:11

Günter Zöchbauer