2

I got an angular4 & Universal application with a app.module and a app.browser.module/app.server.module.

App.Module has all app configuration and bootstrap. app.browser.module call modules that are used in the browser, to avoid breaking the universal build.

Link for the project repo on Github

When I import an external lib (ng2-semantic-ui) into app.browser.module, the app does does not recognize ng2-semantic-ui directives, but when I import it into 'app.module' it does.

This is my modules structure:

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { SuiModule } from 'ng2-semantic-ui';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
  ],
  imports: [
    BrowserModule.withServerTransition({appId: 'app.id'}),
    SuiModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.browser.module.ts

import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';   
import { SuiModule } from 'ng2-semantic-ui';

@NgModule({
  imports: [
    BrowserAnimationsModule,
    AppModule,
    SuiModule,
  ],
  bootstrap: [AppComponent]
})
export class AppBrowserModule {}

When I import SuiModule inside app.module the app recognize its directives, but when I take it out to app.browser.module, it doesn't. Why?

The Error

compiler.es5.js:1690 Uncaught Error: Template parse errors:
'sui-sidebar' is not a known element:
1. If 'sui-sidebar' is an Angular component, then verify that it is part of this module.
2. If 'sui-sidebar' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("

P.S. BrowserAnimationsModule are recognized in the app when I import them inside app.browser.module.

neoswf
  • 4,269
  • 4
  • 35
  • 52
  • list the steps how you installed and configured? – Aravind Jul 23 '17 at 01:41
  • In my opinion,you should refactor your code structure.The AppModule should be root module And not imported by any others. – jackjoy Jul 23 '17 at 01:45
  • @Aravind & @jackjoy. Thank you for your comments. [I have constructed a github repo that has all the project code](https://github.com/neoswf/ngcli-universal-external-ui-libs-seed), and left comments in `AppModule`, `AppBrowserModule` & components html pages to understand better my problem. I cannot load AppModule as the root since I need to load AppBrowserModule & AppServerModule first in order to not break Universal. What u guys suggesting I'll do? – neoswf Jul 24 '17 at 01:44
  • What I don't get is why animations that I imported on AppBrowser working as expected but SUI doesn't... – neoswf Jul 24 '17 at 02:26
  • I manage to see the JS files of SemanticUI Module in the page, meaning I see them in my browser's inspector, but their directive is unrecognized.... – neoswf Jul 24 '17 at 13:01

2 Answers2

3

It doesn't work because you are trying to use sui-sidebar component within template of HomeComponent that has been declared in AppModule and this module doesn't have sui-sidebar component in its scope

That's how how encapsulation works in @NgModules. Directives, components and pipes are not published in the "global" scope.

If you want to use sui-sidebar component within HomeComponent then

1) find @NgModule where HomeComponent has been declared (AppModule).

2) look at declarations array of AppModule. Do you see sui-sidebar component there? If you do it will work otherwise

3) look at imports array. Do you see any module that is exporting sui-sidebar. If you do then it will work otherwise unfortunatelly you will get error like

ERROR in Template parse errors: 'sui-sidebar' is not a known element:

If your external library is not compatible with server rendering then prerendering won't work in any cases.

See also

yurzui
  • 171,085
  • 24
  • 365
  • 354
  • I think it was not clear in my original question. The app is a `universal` implementation. I cannot declare `sui-sidebar` in AppModule, cause by doing so, the Universal build will break. I need to place all external UI-Libs outside of AppModule so Universal build will pass. This is why I tried placing them in AppBrowserModule. – neoswf Jul 25 '17 at 18:24
  • Then move HomeComponent to AppBrowserModule or export SuiModule from AppBrowserModule. – yurzui Jul 25 '17 at 18:26
  • But than universal's build won't break, cause he won't be aware of HomeComponent, cause it got declared on AppBrowserModule only? – neoswf Jul 25 '17 at 18:29
  • 1
    You shouldn't compare BrowserAnimationModule with SuiModule. BrowserAnimationModule doesn't have any directives. If your external library is not compatible with server rendering then it won't work in any cases – yurzui Jul 25 '17 at 18:35
  • Creating different appModules for browser and server is the way to make the SSR build pass, and still have UI Directives working on the client. Thats why I opened this question, cause I wish to achieve that. The architecture I used comes from an example I found on the web. Regarding `BrowserAnimationModule` - its not loading directives, but it does get recognized on appBrowser, in my app architecture... I still don't get it... – neoswf Jul 25 '17 at 18:43
  • `the app does does not recognize ng2-semantic-ui directives` what do you mean? My and BogdanC answers explain why is it happening. You are getting error from aot compilation – yurzui Jul 25 '17 at 18:47
  • It means that when I import and declare SUI in AppBrowserModule, angular do not understand the directives in the HTML code. I did understand what u guys wrote me, thing is that I cannot load the components in AppBrowserModule case that AppServerModule will not recognize them & break. Why do u say that its an AOT error? I was not running it. – neoswf Jul 25 '17 at 18:52
  • How do you start your application? `npm start`? – yurzui Jul 25 '17 at 18:54
  • NGCLI app. `ng serve`. When I build the app for Universal, I run `ng run start`, which does not run aot, it runs `ng build --prod && ngc` and after runs the server `ts-node src/server.ts` https://github.com/neoswf/ngcli-universal-external-ui-libs-seed/blob/master/package.json – neoswf Jul 25 '17 at 18:56
  • `ng build --prod` runs AOT internally and ngc is `@angular/compiler-cli` the main project for building AOT https://github.com/angular/angular-cli/blob/master/docs/documentation/build.md#--dev-vs---prod-builds (aot true by default with `--prod` flag) – yurzui Jul 25 '17 at 18:58
  • When u speak about the error, u mean the error for my universal build or the ng app serving? Cause npm build run the universal. When I run its build, its passing but shouting about lack of support for UI features, which are caused by importing UI libs to its module. My problem is with importing UI Libs to the BrowserModule, so i won't break Universal. (Thats at least what I think I need to do....). Maybe the solution lies with creating a third module which both browser and server will use... A module with components imports... – neoswf Jul 25 '17 at 19:06
  • I was talking about universal build by running `npm start` That is what i found in your linked repo – yurzui Jul 25 '17 at 19:08
  • Yes. But I do not get an error of AOT. It does runs when building the app, but the error is a universal error, cause it complains of lack of support for browser features – neoswf Jul 25 '17 at 19:20
  • How can i reproduce your error? i installed your repo, uncommented `sui-sidebar` in `home-component.html` then ran `npm i` and `npm start` and i see aot error `ERROR in Template parse errors:` – yurzui Jul 25 '17 at 19:32
  • Did not know that this is a AOT erro. Sorry. To reproduce the Universal erro, uncomment the 2 lines in appModule. This will give - `ReferenceError: MouseEvent is not defined`. – neoswf Jul 25 '17 at 19:54
2

Your problem came from the module hierarchy of Angular. What you are trying is to build a building and on the second floor you add a room (your about component), then you add a window on the third floor (semantic ui) and you want it added on your room on the second floor. In your case, you declare the about.component to your app.module and you try to use on your about.components some semantic components that you import later on on your app.browser.module when you import also the app.module.

To fix it, you either have to move your component declarations to your app.browser.module:

import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';

import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';

/*Code that breaks the NG APP when importing SUI on app.browser.module, but works well when imported on app.module*/
import {SuiModule} from 'ng2-semantic-ui';

@NgModule({
  imports: [
    BrowserAnimationsModule,
    AppModule,
    SuiModule,
  ],
  declarations: [HomeComponent,
                AboutComponent],
  bootstrap: [ AppComponent ]
})
export class AppBrowserModule {}

or you do it on your app.module:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';

import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';

/*Code that breaks the NG APP when importing SUI on app.browser.module, but works well when imported on app.module*/
import {SuiModule} from 'ng2-semantic-ui';


@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent
  ],
  imports: [
    BrowserModule.withServerTransition({appId: 'ngcli-universal-seed-app'}),
    FormsModule,
    HttpModule,
    AppRoutingModule,
    SuiModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

You can use the architecture you want (although is a good thing to stick to the one @angular proposes) as long as the page that is consuming a component has that component imported in his module (directly or via another module).

Edit

Searching for a solution on this problem, I found an interesting page about Angular Universal Server Side Rendering here.

In the article I have found the link to a boilerplate that I found is better structured than the one you are using and up to date. You can find it here. I recommend you to use this one since is better structured and it works.

Last I added your semantic-ui code to about page of this boilerplate, imported the module into their about.module, compiled the universal app and run it, everything works fine.

I dropped for you the boilerplate with the working added code here

To conclude, the problem was coming either from the boilerplate structure you were using or some dependency conflict when building. Also, I find the Google documentation on universal apps not complete and outdated.

BogdanC
  • 2,368
  • 2
  • 21
  • 22
  • I think it wasn't clear in my original post. The app is a universal implementation. If I'll take out all components declarations to AppBrowser, AppServer wont be aware of them & will break (i guess). If I'll place SUI inside AppModule, Universal will break again. – neoswf Jul 25 '17 at 18:27
  • 1
    [@neoswf](https://stackoverflow.com/users/279591/neoswf) I've found where it was the problem. I will edit the answer. – BogdanC Jul 26 '17 at 17:32
  • thank you so much for taking all this time to build the repo and explanations. It seems to be working, thank you! I still don't get why my structure did not work (or what exactly is wrong there). --- New Line: Running the universal build, I see that visual components won't render. (the sidebar for example do not run on universal, making component unreadable by search engines). It makes me wonder how ready is it for production... – neoswf Jul 26 '17 at 20:45