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

Angular Universal Roadmap for Angular 4.0 #13822

Closed
vikerman opened this issue Jan 7, 2017 · 80 comments

Comments

Projects
None yet
@vikerman
Copy link
Contributor

commented Jan 7, 2017

Migrate Angular Universal API to @angular/platform-server

High Level Plan

Angular Universal consists of 1)API and 2)tools to do server side rendering of Angular apps. Currently both the Angular Universal API and tools live in angular/universal. The plan is to migrate the API part of Angular Universal to the core Angular project in angular/angular for the Angular 4.0 release and beyond.

We will design the API from ground up to be a core part of Angular and also refactor internals of Angular to better support Angular Universal. The aim is to build a strong foundation for the future development of Angular Universal. This also puts Angular Universal as a core part of Angular and will follow the same quality and release processes as the rest of Angular.

We will keep the API compatible with the current Express wrapper for Universal and make corresponding changes to it as needed. (We haven't scoped out the support for other Universal tools outside of the Express wrapper at this point)

There will be API docs and Dev Guide for Angular Universal. There will be a migration doc to help with migration from the current angular2-universal to the new Angular Universal API.

Use cases

The main use cases we are considering for this iteration of Universal are

  • Initial rendering of Angular application on server for perceived faster loading of application
  • SEO: Content in Angular application is indexed by crawlers
  • Link Preview - Generate a preview of the angular application for link previews on different websites

API

Instead of having a separate Angular platform to support the Universal API we aim to augment the platformServer in @angular/platform-server which already provides the basic support for running Angular in Node.js environment.

Very roughly the usage of this API would look like -

import {platformServer} from '@angular/platform-server';
import {AppServerModuleNgFactory} from './server.ngfactory';
…
// Per Request to avoid cross-talk.
const platform = platformServer([
  {provide: INITIAL_CONFIG, useValue: {
    url: '...',
    document: '...',
  }}
]);

// Long-form rendering
platform
  .bootstrapModuleFactory(MyAppModuleFactory)
  .then((ref: ApplicationRef) => ref.isStable.filter(v => v).toPromise())
  .then(() => platform.injector.get(PlatformState).renderToString())
  .then(html => ...)
  .then(() => platform.destroy());

The two main options to be passed in are the document string which represents the whole document from what would be the index.html on the client side app and the current app URL. These are specified through the INITIAL_CONFIG provider on the platform.

There will be a convenient API for the most common use case of bootstrapping and rendering a single application -

Import {renderModuleFactory, renderModule} from '@angular/platform-server'

// Short-form rendering
renderModuleFactory(MyAppModuleFactory, {url: '...', document: '...', providers: [..]}).then(html => …);

The other main addition will be the ServerModule in @angular/platform-server that corresponds to the BrowserModule in the client side. We recommend that you have a separate top-level App Browser Module and an App Server Module where you specify your platform specific providers including BrowserModule and ServerModule respectively.

Any extra data to be passed in to the server application will be done through providers.

Implementation details

  • Keep the current implementation of the renderer in platformServer and make changes to it as needed to support Universal use cases.

  • Refactor Http module to be platform independent and move the platform dependent parts into platformBrowser and platformServer.

  • Provide a server side Location service.

  • Use the current Metatags service for any met tags manipulation required for link previews.

  • We will make the server implementation of Http(most probably based on XHR2) on the server platform to be Zone aware. We will provide guidelines on how to make other asynchronous Node libraries Zone aware - so that the server side application knows when the rendering is actually complete.

  • Provide a way to test Universal ready Components and Application. We already run all internal Angular tests in the Node environment. We will provide helper utilities to make it easier to test your Universal ready Components on both Node and browser.

  • Update the Universal starter to use the new API

Thanks,
Vikram Subramanian and Alex Rickabaugh

@vikerman vikerman added this to the 4.x milestone Jan 7, 2017

@MarkPieszak

This comment has been minimized.

Copy link
Member

commented Jan 7, 2017

Just wanted to make sure @gdi2290's notes on the Universal integration were in as well from #12495 (comment)

One thing to note when dealing with ASP.NET Core environment for example, we typically won't be given the entire html document, rather just a fragment such as the root <app></app> for example since .NETs MVC handles the (.cshtml file, which in turn becomes the .html file during rendering). This might create some issues when trying to integrate the Metatags service.

Just wanted to make sure it's on the radar, here's an example of .NET & Universal in action:

/* ... imports ... */
const platform = platformNodeDynamic();

export default createServerRenderer(params => {
    return new Promise<RenderResult>((resolve, reject) => {
        const requestZone = Zone.current.fork({
            name: 'angular-universal request',
            properties: {
                baseUrl: '/',
                requestUrl: params.url,
                originUrl: params.origin,
                preboot: false,
                document: '<app></app>'     // <------- ** 
            },
            onHandleError: (parentZone, currentZone, targetZone, error) => {
                // If any error occurs while rendering the module, reject the whole operation
                reject(error);
                return true;
            }
        });

        return requestZone.run<Promise<string>>(() => platform.serializeModule(AppModule)).then(html => {
            resolve({ html: html });
        }, reject);
    });
});

Open issue: Do we provide a way to pass in the request data to the server side Angular application in the first iteration?

@vikerman Are you talking about the req object from Node? (https://github.com/angular/universal-starter/blob/master/src/server.ts#L72-L82)

Also would love to help with the integration in any way 📦

@FrozenPandaz

This comment has been minimized.

Copy link
Contributor

commented Jan 7, 2017

We are already using a copy of the Metatags service in our Universal App and it is already agnostic, we just had to transfer state by querying the DOM onInit on client side.

Also as a compliment to this, what is the roadmap for CLI integration for Universal? Maybe the Universal Starter should be rebased to an Angular CLI / Universal CLI generated app?

I would also like to help in anyway possible.

@vikerman

This comment has been minimized.

Copy link
Contributor Author

commented Jan 7, 2017

@MarkPieszak - Thanks for the pointers. We are working with Patrick on this. Alex and I did discuss about having separate document and fragment fields in the platformServer options to support other use cases where you don't control the whole document. I will put up the proper proposed signatures of each of these APIs to the bug soon. We are still in the process of reviewing the API with rest of the team.

@FrozenPandaz - If the Metatags service is already working in a platform agnostic way then it's just a matter of moving it to a place that reflects that. CLI needs to have the required extension mechanism to support Universal and is definitely in our radar post-4.0.

The aim of this is to provide a solid API foundation for Universal. We can definitely use your help in getting feedback on use cases and integrations with the current set of tools around Universal.

@MarkPieszak MarkPieszak referenced this issue Jan 7, 2017

Open

Angular 2 #1869

2 of 3 tasks complete
@FrozenPandaz

This comment has been minimized.

Copy link
Contributor

commented Jan 7, 2017

@vikerman would I be able to assist with moving it? I gave it a shot and the bigger underlying issue is that it relies on the DomAdapter which is currently native to @angular/platform-browser.. Should that be moved to @angular/common as well? That seems to require more refactoring.

This was referenced Jan 8, 2017

@MarkPieszak

This comment has been minimized.

Copy link
Member

commented Mar 17, 2017

@sneridagh @dguisinger I'll be trying to make a little write-up to showcase some of the differences & paths to upgrading for those using the previous angular2-universal package, but luckily when the new express-engine gets released out to npm it won't be very difficult ! (There will be a similar asp.net core engine as well).

Most of the major changes are including BrowserModule.withServerTransition() & ServerModule instead of UniversalModule.
Removing any references to angular2-universal & angular2-universal/polyfills and 2.1.1+ workarounds.
@Inject()'ing your own isBrowser/isNode.

Other than that, it should be relatively painless if you were on the 2.1.0-rc1 of angular-universal.

@damirkusar

This comment has been minimized.

Copy link

commented Mar 18, 2017

Hi Guys,

first of all, good work. My transistion from Angular 4.Beta.8 to RC.4 was almost painless, except two things :) hopefully somebody of you sees where I missed some things..

This is my project, here you see also the changes I made for those who are interested for the transition: https://github.com/damirkusar/AngularMeetsNetCore/tree/AngularRc4

First problem is when i am updating a file and hot module replacement kicks in, it has a problem refreshing the page. When doing it manually, it works. See for that this Stack overflow link:

https://stackoverflow.com/questions/42873427/angular-4-rc-4-net-core-the-selector-app-did-not-match-any-elements

Second problem is with server side rendering, it tells me that Cannot find module '@angular/animations/browser'. Where do I have to include that? :)
See for that again Stackoverflow link: https://stackoverflow.com/questions/42873506/angualr-4-rc-4-and-net-core-server-side-prerendering

Would be cool if you can take a look and point me where I missed things..

Thank you guys

@shawnsz

This comment has been minimized.

Copy link

commented Mar 18, 2017

@damirkusar About you second problem. you need to install the animations package @angular/animations and
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
Animations was pulled out from the core.

@damirkusar

This comment has been minimized.

@kukjevov

This comment has been minimized.

Copy link
Contributor

commented Mar 22, 2017

Hi all :).

I have question. What about authentication of XHR requests from SSR?

Let`s say i have application which uses Windows Authentication. When i access server, IIS returns header requiring NTLM auth i put in data and then browser automatically sends auth header with request, or i have cookie authentication. Everything is set with first request but any subsequential requests are handled by browser.

How is this going to work from SSR XhrRequests?

First what happens on server side when i receive response 401 with request headers indicating that authentication is required. What happens and what should happen?

@MarkPieszak

This comment has been minimized.

Copy link
Member

commented Mar 22, 2017

@kukjevov
I'll just added a few notes on the .NET integration for that, but basically you'll be starting the process from your MVC Controller Action, and in there you can pass CustomDataParameters that will be serialized and passed down to your boot-server. You'll just have to use dependency injection to provide a ServerCookieService that grabs those tokens from the server (that were taken from here), or browser (like normal).

This is the basic interface you'll see in the HomeController, and that 2nd to last Parameter is where you'd pass in any Cookies / tokens / random data you want given to Angular for consumption.

You can see the WIP aspnetcore-engine module readme here that explains where the data gets sent from .NET and how to take it in, and pass it into your providers for your Angular app.

@ozknemoy

This comment has been minimized.

Copy link

commented Mar 25, 2017

how can angular universal read cookie from node now? before we used Zone.current.get('req')

@ozknemoy

This comment has been minimized.

Copy link

commented Mar 25, 2017

for learning purpose :
from ngExpressEngine
const providers: Provider[] = [ { provide: 'REQUEST', useValue: req } ]; if (res) { providers.push({ provide: 'RESPONSE', useValue: res }); } return providers;

@kukjevov

This comment has been minimized.

Copy link
Contributor

commented Mar 28, 2017

@MarkPieszak
can you please help me :)? I am trying to set Cookie header on SSR, to be sent with XHR request. Reason for this is that, i have some Asp.Net Core data stored in Cookies, so i don`t have to retrieve them from database every time also information about current localization setting.

But for XHR requests made from Angular SSR i cant get them to server. I tried to set header named 'Cookie' but it failed with following error Refused to set unsafe header "Cookie". How can i pass Cookies to Xhr Requests from Angular SSR.

Thank you

@fulls1z3

This comment has been minimized.

Copy link

commented Mar 28, 2017

There's another example repo at https://github.com/nglibs/universal-example-app showcasing both Universal and lean Angular on a single project - using node.js + express.

This application uses platform-server delivered with Angular 4.0.0, and custom implementations of ng-express-engine and transfer-state (both packaged on npm under nglibs scope) until they got officially published on npm.

Furthermore, it covers most of the features of angular2-webpack-starter by AngularClass such as async/lazy routes, SCSS compilation (both inline and external), dev/prod modes, AoT compilation via @ngtools/webpack, tests, TsLint/Codelyzer, @types and maybe more.

I know it's not perfect, might contain issues, and I'm not suggesting that it's a starter/seed project. However, it might help with the use of platform-server. Meantime, any help and contributions are welcome :)

@NKjoep

This comment has been minimized.

Copy link

commented Mar 28, 2017

@fulls1z3 thank you for the link. That repo contains a very good example of how to configure everything for a full angular 4 app. 👍

@shawnsz

This comment has been minimized.

Copy link

commented Mar 28, 2017

Hi all. Is there any document/sample code showing how to do pre-rendering by calling renderModuleFactory to get the HTML pages? Angular 4 is out but still no enough document.

@somombo

This comment has been minimized.

Copy link

commented Mar 29, 2017

I am interested in how all this affects the Mobile-toolkit Project.

Correct me if I'm wrong, but it seems that the mobile-toolkit was stagnated, in part, by dependency on angular-cli (--mobile flag removed) and angular-universal (used for app-shell).

So now that @angular/platform-server merged the angular-universal core.. I am curious about the future of the mobile-toolkit.

I haven't seen any new commits or updates to the angular-universal repo since the platform-server update. Is the angular-universal repo/project discontinued? If so, does that mean the mobile-toolkit is as well?

Thanks

cc: @MarkPieszak @gdi2290 @alxhub @jeffbcross @hansl @filipesilva @robwormald

@linusbrolin

This comment has been minimized.

Copy link

commented Apr 6, 2017

Hi, I've tried to find how to make localStorage work when using Universal/platform-server?
I get an error message that localStorage is not defined when I try running my app.
I've checked #7438 angular/universal#309 angular/universal#247 angular/universal#272 and angular/universal-starter#80 but nowhere does it say if a Storage Service has been implemented, how to use it, or how to avoid the problem.

Does anyone know?

@ozknemoy

This comment has been minimized.

Copy link

commented Apr 6, 2017

try this

`export class LocalStorage {
storageKeyPrefix = 'ngStorage-';
storageType = 'localstorage';
constructor(@Inject('isBrowser') public isBrowser ) {}

get(key) {
    if (this.isBrowser) {
        return (window.localStorage.getItem(this.storageKeyPrefix + key));
    } else {
        return this[key]
    }
};

set(key, value) {
    if (this.isBrowser) {
        window.localStorage.setItem(this.storageKeyPrefix + key, value)
    } else {
        this[key] = value
    }
};

}`

@fulls1z3

This comment has been minimized.

Copy link

commented Apr 26, 2017

Just released a cache utility for Angular working on both browser and server platforms, and explained the local storage on angular/universal#272 and on ng-seed/universal#5.

Writing the documentation now, but you can have a look at ng-seed/universal for its usage.

Check the implementation: just updated this repo as well as publishing 4 separate packages for cache operations both on server and browser platforms. Everything is handled via the @Cached(...) decorator on the method/loader/etc which you want it cached. Also, there's the possibility to use caching methods (has, get, set) using the caching API.

You might ask why to keep the structure so complex and consume 4 different packages? The answer is, not all the use cases depend on caching on the server platform. Furthermore, this structure allows to integrate any kind of custom caching with the server or browser platform (ex: session, api, etc).

Currently there's no documentation available for these packages, because these days I'm kinda overloaded with many different tasks/projects. I think in a week or two I'll try to provide a good documentation as well as unit tests for these packages.

However, try to have a look on this repo - about the use of CacheService and its instruments. I think it won't be difficult to understand the concepts, as I've kept the code style as DRY as possible.

@MuhammadShaban

This comment has been minimized.

Copy link

commented May 3, 2017

@fulls1z3
good job, just need watch & serve options

@jerbes

This comment has been minimized.

Copy link

commented May 3, 2017

Is there a good place to get official current benchmarks?
We are currently using angular 1 and would like to make the case for angular 4.

@megamindbrian

This comment has been minimized.

Copy link

commented Jul 16, 2017

I know you mentioned it isn't a goal to render Angular outside of express, but I got this working using a mash of code from this thread and looking at the source of ngExpressEngine.
I wanted to render a ton of components providing different default states kind of like Airbnb's design framework stack. Here it is attached to a jupter cell:
screen shot 2017-07-16 at 1 04 47 pm

@SystemDisc

This comment has been minimized.

Copy link

commented Sep 19, 2017

How do we get the "req" object in a component during SSR? How do we get cookies sent from the user's browser to NodeJS (ExpressJS) during SSR? Is there a way to passthrough cookies/headers when making HTTP requests during SSR?

I feel like if a browser requests a page, SSR should load the cookies/headers passed, and pass them to HttpClient and whatnot.

Edit: nevermind, I figured it out: #15730 (comment)

@Toxicable

This comment has been minimized.

Copy link
Contributor

commented Sep 19, 2017

@SystemDisc If you're using ngExpress Engine then you can simply inject the request like so https://github.com/angular/universal/tree/master/modules/ng-express-engine#using-the-request-and-response

@SystemDisc

This comment has been minimized.

Copy link

commented Sep 19, 2017

@Toxicable Thanks! I'm not using ngExpress Engine, but I figured it out using extraProviders: #15730 (comment)

The comment I linked to is also relevant to @kukjevov and other users trying to pass cookies/auth during SSR

@ranjitnadar

This comment has been minimized.

@LoveenDyall

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.