-
Notifications
You must be signed in to change notification settings - Fork 519
Conversation
Thanks so much for putting this together! Unfortunately, when I run the install I get the following error:
I tried running npm install manually in my project folder, but that didn't make any difference. If I try to run the project I get this error: fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0] Any idea what the issue is? |
I hit the same error as @danobri. It seems that running |
@danobri @astegmaier Thanks for pointing that out! I've applied a fix and have pushed an update to |
Thank you for your work, I just ran the install and everything went good. I'm still experiencing an issue I was trying to get rid of thanks this version in angular 4. Indeed, openlayers tries to access DOM before it's rendered... I tried to implement the test The only solution I found is to get rid of the Thanks for your help |
@SteveSandersonMS - I've confirmed the core-js/shim issue is fixed - thanks! Now I can run the project, but not only does HMR not seem to be running, if I stop the project and make a change to one of the html files, and then re-run, I don't see the change. Rebuilding the project doesn't seem to make any difference. I'm using VS2017 if that matters. |
@epitomz I don't think your issue is specific to this Angular 4 version of the template. In general, you can't access @danobri I don't think your issue is specific to this Angular 4 version of the template either. It sounds like you're just not running in development mode. |
@SteveSandersonMS I closed the solution and re-opened, and now everything is working as expected, including HMR. |
@SteveSandersonMS Apologies for the delay Steve I meant to email you a week or so ago, but just went through a large cross-country move so I've been a little MIA! Would it be easier to setup some time on Skype/etc to talk through everything?
You could catch the error directly from the subscription, as I'm doing here in the aspnetcore-engine ? You're right though, the timing is a little different now, as isStable looks at more than just the Zone to check for App stability. Also wondering if the missing
Yes ideally you want it to be a As for platformServerDynamic, the reason it isn't used is that there are no benefits to doing JIT compilation during server renders, so we only recommended AoT compilation through platformServer. Inside the aspnetcore-engine there is actually a compiler function that can take regular NgModules and compile them async to AoT'd ngModuleFactories, so you can just use the regular platformServer. Anyway, I'd love to sync with you sometime, so we can craft the aspnetcore-engine & whatever else we need here to help aid ASPNET developers! 👍 |
bootstrap: sharedConfig.bootstrap, | ||
declarations: sharedConfig.declarations, | ||
imports: [ | ||
BrowserModule, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You want to make sure you have .withServerTransition({})
as this is how guids (such as those for components and styles) are synced between the server & client.
BrowserModule.withServerTransition({
appId: 'my-app-id' // make sure this matches with your Server NgModule
}),
bootstrap: sharedConfig.bootstrap, | ||
declarations: sharedConfig.declarations, | ||
imports: [ | ||
ServerModule, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You want to make sure you have BrowserModule with serverTransition here as well (with the same appId)
BrowserModule.withServerTransition({
appId: 'my-app-id' // make sure this matches with your Server NgModule
}),
@@ -1,29 +1,22 @@ | |||
import 'angular2-universal-polyfills/browser'; | |||
import 'reflect-metadata'; | |||
import 'zone.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could just have (I believe it's a bit smaller) import 'zone.js/dist/zone-node';
here instead, just an idea!
}); | ||
moduleRef.destroy(); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could catch errors here in the subscription
I tried that, but it didn't work. Try throwing an exception from a component constructor and see if catches the error properly.
I tried that too, but it didn't change anything in this case. Also since I'm listening to
Yes, we definitely do want to get to that eventually, but for this first phase I'm just trying to preserve existing functionality, which doesn't include any server->client state transition. So hopefully we can add that later.
Is that for the server only (based on the name
Again, that doesn't seem to work as a way of getting for example exceptions thrown in component constructors. Though if you can confirm some way of doing it that does work, please let me know! |
import { BrowserModule } from '@angular/platform-browser'; | ||
import { FormsModule } from '@angular/forms'; | ||
import { HttpModule } from '@angular/http'; | ||
import { sharedConfig } from './app.module'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make more sense to have sharedConfig be an NgModule
itself that they import within imports[]
, since most Apps will have lots of NgModules lazy-loaded etc ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. How does that then work in terms of overriding the config in each of the server/client app modules? If you can post a code sample we can definitely consider this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW I tried to do this, but hit a roadblock. If the shared NgModule
includes the routing config (which of course you would want it to), then it has to be able to compile all the templates for all the components. But to be able to compile them, it needs to reference either BrowserModule
or ServerModule
(otherwise it fails with errors like ngIf is unknown
). But it can't reference either of those, because it needs to work with both server and client.
So is there any reasonable way to do this? Please let me know if there is. It doesn't seem like a compiled NgModule
can possibly be portable across both server and client, which is an unfortunate limitation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry just seeing this now! @SteveSandersonMS
Did you have boot-client & boot-server using browser-app.module & server-app.module ?
To get rid of the ngIf unknown errors, you just import CommonModule into your base app.module NgModule (https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/app/app.module.ts#L51)
You can see in there I have the app.module importing the Routes, and all the base declarations etc that can be shared. Browser & Server-app.module's both import the app.module and they each declare the bootstrap: [ AppComponent ]
themselves. You want to make sure app.module doesn't declare a bootstrap Component.
{ provide: 'ORIGIN_URL', useValue: params.origin } | ||
]; | ||
|
||
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The team has stressed to never use dynamicServer as JIT compilation just slows down server renders tremendously as you'd really only want ahead of time compiled ones with platformServer
.
Just worried we might have Devs use this phase 1 template (and never update it) and get stuck using a non-ideal bootstrapModule serializer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point as for where we're ultimately heading, but right now people are already using the equivalent to dynamicServer
with their Angular 2 sites. So this pull request is not making things any slower.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Just making sure :)
You're right, I have it spilling out the errors in the debug console of VSCode etc, but the prerendering Times out. I'll investigate this one further! Are you getting the same result?
Investing this one, I'll get back to you. That's quite strange behavior though, you're right.
Sounds good, it doesn't do too much other than make sure they use the same guid.
Ah sorry I mean using the
What kind of exceptions are you throwing, |
Any unhandled exception in a constructor causes this (so We need to catch and handle all the plausible ways that developers will experience errors during server-side rendering, including:
Hopefully there's no special code needed to cover all these cases. The Zone's |
This is now merged to |
@SteveSandersonMS Thank you for you work! When the next version (that includes the Angular 4 support) will be released? |
@bm-software It's published to NuGet now. The new version number is 0.9.2. You might have to wait another 10-15 mins before NuGet refreshes and acknowledges that the new package exists. |
Nice! |
Please don't laugh at me but... How do I actually update to the latest template package? |
Option 1: go through the commits, and apply changes manually Option 2:
|
I have already updated my project to ng4 but I want to update the template package as well so I get the latest templates for |
There's probably a cleaner way to upgrade, but what I've been doing is just deleting the If anyone knows what the official way to update Maybe it's just as simple as running |
That worked for me 👍 |
Same here, |
So the issue of passing .net core data to server side pre-rendering appears to have gotten more acute with this release and with @MarkPieszak 's code. Before it was relatively easy to just pass the values through. But in the new code all of that seems to have been removed from the server pipeline. The reason why this is critical is environment variables etc. You want to be able to tell your SPA for instance where your API is located in the different environments that it gets published to without having to hack a source file to do it. An example of how to pass server side values to pre-render (and hopefully the same end result on client side) would be greatly appreciated. |
Thanks! @SteveSandersonMS @MarkPieszak |
Would very much appreciate to see the same. |
I'd also love to see data caching so that a page can load on the server side and then pass the cached data to the clientside so that there aren't duplicate calls constantly. |
@JohnGalt1717 Inside my Repo, you pass in whatever custom data you want within TransferData in the HomeController, then you pass it further down with main.server (there should be comments in there explaining the rest). As for not having Http calls reused you need to either setup a Redux that repopulate the store from the data passed down through the window, or you can use the provided TransferHttp (here). That automatically handles all of that, and doesn't rerun Gets on the client. Coming soon we are working on adding an easier way to pass data withServerTransition, so that'll help at least from the Angular perspective. I'll spend some time trying to improve the process, at the moment it's a little difficult 🙈 |
@MarkPieszak Thanks for the info. I've already tried all of that and I can't get the data out that is being set in the HomeController. I CAN get it out on the client side with window.TRANSFER_CACHE. but I cannot figure out how to get it out on node server side pre-render because obviously window.xx doesn't exist. If you can just shoot me an example or set me on the right path to getting it on the server side then I can get this going. On the TransferHttp is there an example on it's usage somewhere in the code? All I saw was linked to the internationalization stuff. We have a client library that is automatically generated from our WebAPI code so I need to adapt what you have to work in that context. Thanks! Sorry for the move, must be exhausting! |
So really all you want to do it create a Class/service that you add to providers in the main.server file (these are depedencies injected only for the server rendered portion), provide that Class the properties / static properties (whatever you need) here, and during browser renders, have it pluck window.TRANSFER_CACHE when those properties exist there. That way you'll be using it in both 👍 I should probably add some demos of this concept! |
Except window.XXX doesn't exist on the server side because it isn't in nodejs. It only works (and it works fine) on the browser side. I've added the following to my main.server.ts: const setupOptions: IEngineOptions = {
}; and return ngAspnetCoreEngine(setupOptions).then(response => {
}); The transfer script correctly sets window.xxx on the client side but if you try and access it on a server side pre-render it fails saying that window is undefined. Further if I try and import "APP_SETTINGS in my app.module and pass that in to a function (for static provision of our api client) like this: import { ORIGIN_URL, APP_SETTINGS, IAppSettings } from './shared/constants/baseurl.constants'; ... providers: [ and then in initializeConfig I add a param for it, the object is initialized but there is nothing in it. Alternatively if I use an InjectionToken as a string and change the above provider creation to just a string value the injectiontoken is always null in the app.module.ts. Hence why I keep banging away at this topic. It just isn't working as you describe. It did work in the javascriptservices Angular 2 version because it forked the Zone and you could add the values as properties on the zone and then access it with Zone.current.get("XXX") but that doesn't work now. I even tried adding it back and as far as I could tell it never inserted them. (and it's very hard to tell because it's virtually impossible to set break points and inspect this stuff which is a major problem that needs to be resolved. Vs code should be setup so that it will debug the .net, the browser and the nodejs execution with a compound debugging session but I can't get the node debugging to actually work properly and set break points so I haven't been successful in this either which is causing major wastes of time in our team because you just get generic (mostly noise) errors in the debug console in VS.net or VS Code without being able to see what's really going on inside of node and the code that's being executed as soon as you have server side pre-rendering enabled. I'm at 3 days trying to get this to work. The angular 4 documentation is truly awful in this regard and the opacity of JavaScriptServices on top of node makes this an exercise in frustration. |
@MarkPieszak @SteveSandersonMS What's the possibility of using this template and targeting the full .NET framework? I tried passing in the -F switch, but looks like the template generator does not support full framework versions. Been working with the template on a project that uses Windows Auth, and just discovered that I need functionality from the System.DirectoryServices library, which unfortunately has not yet been ported to core yet - see CoreFx #2089. If it's possible to target the full framework with this template, would it be practical to convert my existing project? Any information / insight you can provide would be greatly appreciated. Thanks again for all your work on this template! |
I just changed the in my csproj to net461, and everything seems to be working fine. So apparently it's not a problem... Is that would you would expect? |
As per this comment, this is a first step towards updating to Angular 4. It's an attempt at the minimal changes needed to get Angular 4 working correctly on both client and server.
I've pushed a prerelease version of
generator-aspnetcore-spa
to NPM so that people can try this out (details below). I'd like to get a pretty substantial level of feedback that this works properly in all the existing scenarios before we merge this intodev
and ship it as the released template package.How to try it
Install the prerelease version of the Yeoman generator:
(having already installed Yeoman using
npm install -g yo
if you don't already have it)... and then create a new project:
Please tell us how you get on with it.
Notes
There are a couple of implementation details I'm not yet totally happy with:
boot-server.ts
, the error handling is messy. Because Angular firesonStable
before it firesonError
, I've had to use a nastysetImmediate
hack to wait for possible errors before rendering the page. This doesn't feel like it should be the right solution. However, I've looked for examples of people usingplatformDynamicServer
across the web, and it seems like virtually nobody is using it yet, there are no official samples, and the few examples I can find don't even make any attempt at error handling at all. So, @MarkPieszak, is the event ordering a bug in Angular, or is there some other way we're meant to catch errors?ORIGIN_URL
token is just a string right now. It would be better if we had an actual class likeRequestContext
so you could have component constructor parameters likecontext: RequestContext
without having to use the@Inject
decorator at all. But when I tried to do that, it didn't work because the code evaluates in the wrong order, and you have to use the hideousforwardRef
(which I don't consider acceptable at all) to work around that. If someone can find a better way to do this, that would be great. The problem might go away on its own if we end up getting anORIGIN_URL
from a third-party package (e.g., Mark'saspnetcore-engine
).