Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for angular2-universal #140

Open
AnderssonPeter opened this issue Oct 17, 2016 · 29 comments · May be fixed by #427
Open

add support for angular2-universal #140

AnderssonPeter opened this issue Oct 17, 2016 · 29 comments · May be fixed by #427

Comments

@AnderssonPeter
Copy link

It seems this does not work with angular2-universal as it gives a error about the window object not exist.

Are there any plans to add support for it?

@MarkPieszak
Copy link

MarkPieszak commented Oct 17, 2016

From looking at it at a glance, it looks like this library requires jQuery/hammerJS and has a few small references to window (all which don't exist on the server-side).

Unless these requirements were removed, I'd imagine this would be more in our ballpark on the Angular Universal side to at least (attempt) to support some of these features, which would slow down server rendering, or at least ignore them somehow (which means they just won't get rendered on the server-side). You can see the issue here (angular/universal#534), but it's nothing something I imagine we'll be able to implement soon as it's a huge overhaul. :(

@AnderssonPeter
Copy link
Author

@MarkPieszak Ok thanks i guess ill have to look for a different CSS framework, do you know if there is a list with compatible css frameworks? (the only one i know is bootstrap).

@MarkPieszak
Copy link

ng2-bootstrap
https://github.com/valor-software/ng2-bootstrap

ng-bootstrap (think they have one issue with Carousel remaining to work perfectly with Universal)
https://github.com/ng-bootstrap/ng-bootstrap

Both work very well with Universal, they have no jQuery dependencies and have fixed any window/document references/etc.

Not sure why both teams didn't just combine their efforts :(
@AnderssonPeter

@rubyboy
Copy link
Contributor

rubyboy commented Apr 30, 2017

After moving to the Angular CLI this is now part of the library.

@rubyboy rubyboy closed this as completed Apr 30, 2017
@bisubus
Copy link

bisubus commented May 3, 2017

@rubyboy How so? The library still relies on browser globals in entry point and custom-event-polyfill.

@rubyboy
Copy link
Contributor

rubyboy commented May 4, 2017

@bisubus is there anything we can do in the library to support angular universal? I thought we'd need support in MaterializeCSS itself.

@bisubus
Copy link

bisubus commented May 4, 2017

@rubyboy Definitely yes. I tried to go Universal before, and the ng2-materialize was one of the few show-stoppers.

The app will fail to bootstrap if MaterializeModule is included on server side, and it will fail if it's not (at least because materialize directive is used in templates).

Should we assume that materialize directive doesn't do any DOM changes that would be crucial to pre-render and it's safe to make the whole module a noop on server side by default?

@rubyboy
Copy link
Contributor

rubyboy commented May 5, 2017

@bisubus I'd actually assume that Materialize does do DOM changes. Worth checking in the original repo.
Do you have a suggestion how to add support for universal in the angular2-materialize library? Happy for a PR or work with you together on getting that implemented.
Thanks!

@bisubus
Copy link

bisubus commented May 5, 2017

@rubyboy Sure, we'll see how things go.

I've checked how it's going in Angular Material, and it looks like it still isn't ready for Universal, see angular/components#308, but there's supposed to be a separate entry point for server side, like angular2-materialize/server import. Makes sense.

The good thing is that in Materialize most visuals are implemented with CSS, while JS does interaction with user. Until JS part will be rewritten to native Angular DOM manipulation like @MarkPieszak suggested, I guess that stubbing materialize directive and ignoring window stuff for server side is the only reasonable way.

Another direction I see is that browser part (angular2-materialize import) has to be refactored a bit to make it more suitable for server side if a user wants to render it with jsdom or something (not even sure if jsdom can handle jQuery and Waves and other complex DOM stuff). Currently mocking global.window will result in incorrect framework behaviour, angular/angular#16545 . Since there's no built-in window service, I guess it makes sense to introduce something like MATERIALIZE_WINDOW provider that defaults to window global and can be easily substituted with jsdom. I guess in this case window.document from MATERIALIZE_WINDOW should be preferred over built-in DOCUMENT provider, too, since implementations will be different on server side.

What do you think?

@rubyboy rubyboy reopened this May 6, 2017
@rubyboy
Copy link
Contributor

rubyboy commented May 6, 2017

I've reopened this issue, to keep it in the correct state. @bisubus as you said, we do need to put some effort in supporting universal.
Your suggestions make sense. I've never tried using universal, so it's hard for me to advocate on the correct approach. If we can keep the changes minimal, another option would be to have 2 entry points, to allow different imports (import 'angular2-materialize' vs. import 'angular2-materialize-universal', or something like that?). That would make the maintenance much easier, if the shared code inside the directly and accompanying classes is the same.

@susheelbanyal
Copy link

Hi there, It would be great help if anyone can tell me, how to remove the window is not defined issue in angular2-materialize with universal

@bisubus
Copy link

bisubus commented Aug 9, 2017

@susheelbanyal To my knowledge, it's currently better to not include angular2-materialize module on server side at all and stub its directives instead.

@susheelbanyal
Copy link

How can we do that? any code sample?

@bisubus
Copy link

bisubus commented Aug 9, 2017

Nope, no code, sorry. Create a dummy MaterializeDirective that has same inputs as real directive and include it instead of angular2-materialize. This is same thing that's usually done in unit tests.

@bisak
Copy link

bisak commented Sep 17, 2017

I'm also banging my head against the wall trying to implement Angular Universal with materialize. The error I get is the following...

C:\Development\Tests\node_modules\angular2-materialize\dist\materialize-module.js:1
(function (exports, require, module, __filename, __dirname) { import { NgModule } from '@angular/core';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:74:10)
    at Object.runInThisContext (vm.js:116:10)
    at Module._compile (module.js:588:28)
    at Object.Module._extensions..js (module.js:635:10)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)
    at Module.require (module.js:568:17)
    at require (internal/module.js:11:18)
    at Object.JbiC (C:\Development\Tests\dist\dist-server\main.bundle.js:1:7632)

That's a major bummer. Had I known I'd run into such issues I wouldn't have started a project with materialize. Had no idea at the time that I'd need to use Angular Universal :/

@bisubus
Copy link

bisubus commented Sep 17, 2017

@biskazz The issue doesn't have to do anything with Universal or this package but is specific to your build process. You may also try allowJS option.

@ssatz
Copy link

ssatz commented Dec 22, 2017

I have successfully used angular2 materialize in angular universal project.
All you need to do is install domino.
and add below code to server files

const domino = require('domino');
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'), 'utf8').toString();
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
global['$'] = require('jQuery');
global['jQuery '] = require('jQuery');
global['Materialize'] = win.Materialize;

@mpunit2530
Copy link

@ssatz can you share the code here. I am facing the same issue. I have added the above code but failing at Cannot set property 'default' of undefined @ exports.default = Materialize.

@ssatz
Copy link

ssatz commented Mar 1, 2018

@karunya2530 : find the below gist link for server.ts file
https://gist.github.com/ssatz/9e894070af27a7b9233ff25eb6f8cc2d
I think this is something to do with webpack bundle import. if the above implementation is not working then share your tsconfig file

EDIT: Ignore the above gist file, it is for latest alpha version materialize css without jquery.
Here is the version with jquery

import 'zone.js/dist/zone-node';
import 'reflect-metadata';
const domino = require('domino');
import { enableProdMode } from '@angular/core';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();
const compression = require('compression');
const PORT = process.env.PORT || 3000;
const DIST_FOLDER = join(process.cwd(), 'dist');

// Our index.html we'll use as our template
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'), 'utf8').toString();
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
global['$'] = require('jQuery');
global['jQuery '] = global['$'];
global['Materialize'] = win.Materialize;
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');

const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
app.use(compression());

app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
        provideModuleMap(LAZY_MODULE_MAP)
    ]
}));


app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));

// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), { maxAge: '1y' }));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
    res.render('index', {
        req: req,
        res: res
    });
});

// Start up the Node server
app.listen(PORT, () => {
    console.log(`Node server listening on http://localhost:${PORT}`);
});

@mpunit2530
Copy link

mpunit2530 commented Mar 9, 2018 via email

@ssatz
Copy link

ssatz commented Mar 9, 2018

@karunya2530: here is the sample repo
https://github.com/ssatz/Angular-SSR-Service-Worker

@mpunit2530
Copy link

Hi Satish,

I did followed the sample repo but yet i am facing this error
factory(jQuery, Hammer);
^

ReferenceError: Hammer is not defined

Do you have any idea to resolve this ??

@ssatz
Copy link

ssatz commented Mar 13, 2018 via email

@mpunit2530
Copy link

mpunit2530 commented Mar 13, 2018

Hi,
I have achieved though, but i am getting now this error..

app\node_modules\domino\lib\utils.js:41
throw new Error("NotYetImplemented");
Error: NotYetImplemented
at HTMLCanvasElement.exports.nyi (app\node_modules\domino\lib\utils.js:41:9)

@mpunit2530
Copy link

I think i am using chart.js and this is causing the server.js to fail to execute. Do you have any idea on this ??

@ssatz
Copy link

ssatz commented Mar 13, 2018

checkout the link here
https://github.com/angular/universal

import { PLATFORM_ID } from '@angular/core';
 import { isPlatformBrowser, isPlatformServer } from '@angular/common';
 
 constructor(@Inject(PLATFORM_ID) private platformId: Object) { ... }
 
 ngOnInit() {
   if (isPlatformBrowser(this.platformId)) {
      // Client only code.
      ...
   }
   if (isPlatformServer(this.platformId)) {
     // Server only code.
     ...
   }
 }

note : mail me sathish.thi[@]gmail.com . I won't reply here

@joscmw95
Copy link
Collaborator

joscmw95 commented May 6, 2018

Initially I followed @ssatz's way to circumvent the problem of not having the window object during SSR.
However, after some time I realize, because domino wasn't able to provide proper DOM API, the directive has been suspiciously causing issues with Node's memory usage -- memory leak due to GC not able to clean up the problematic directives.

Since these Directives aren't very much useful in SSR, it is better to gracefully disable them via platform checking.

@ssatz
Copy link

ssatz commented May 6, 2018

@joscmw95 👍 Seems to be awesome.
Memory leakage would be by jQeury & jQuery dependencies.

@tuffant21
Copy link

As far as I am aware, materialize-css version 1.0.0-rc.2 no longer requires jquery. So technically, this could be closed if angular2-materialize were to support that version.

Maybe? @rubyboy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants