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 new Angular CLI-based template #1288

Closed
SteveSandersonMS opened this Issue Sep 19, 2017 · 148 comments

Comments

Projects
None yet
@SteveSandersonMS
Member

SteveSandersonMS commented Sep 19, 2017

The Angular template shipped with .NET Core 2.0 originated well before @angular/cli came on to the scene. Even when @angular/cli was first shipped, it was unclear whether it would become the de facto standard way to build real-world Angular apps. But now it seems the Angular community largely sees the CLI as the preferred approach, and it has stabilised and matured into something you actually can use in real-world development scenarios.

So, even though the ASP.NET Core Angular template has pretty much all the same features as @angular/cli such as HMR, AoT on publish, Karma tests, etc. (and in fact even some features that the CLI lacks, such as out-of-the-box server-side prerendering), it would be advantageous if our template achieved that stuff using @angular/cli rather than having its own implementation. The key benefit is that developers will have an even more typical project setup, so basically all online tutorials, StackOverflow posts, etc., will apply without any modification.

Approach

The idea is for an updated dotnet new angular template to use a new SpaServices feature called Angular CLI middleware instead of the existing Webpack middleware. So it will still be a single ASP.NET Core project with the Angular part in a subdirectory called ClientApp, but now when the browser makes requests for content under /dist, we will pass through the request to an instance of @angular/cli's ng serve process. As such, the output and behaviour will be identical to what you'd get if you ran ng serve directly, and all the same config and features apply. If you wanted, you could even take your ClientApp directory out of your project and run it manually with ng serve, since it's literally just a standard @angular/cli project. Likewise you can ng eject it if you don't want to use the Angular CLI for some reason (and then use Webpack middleware like before if you want).

Of course, because you then have a standard Angular CLI project, you can use all other CLI features such as ng generate, ng lint, etc., without anything being different just because you're also using ASP.NET.

We would also integrate with Angular CLI when you publish your app. That is, a single dotnet publish command (or the Visual Studio equivalent option) would not only publish the .NET part but would also automate the process of using ng build -prod, putting the output in the right places to be served in production. Likewise, server-side prerendering would also be achieved by a call into Angular CLI.

Note: This would not be a breaking change. The SpaServices package will continue to work exactly the same with existing applications created using the .NET Core 2.0.0 template - this scenario remains fully supported.

Tasks

  • Add BuildServerSideRenderer flag in csproj
  • Add new API for supplying custom data to prerenderer
  • Support "proxy only" mode for AngularCliMiddleware, so you can have an ng serve process running separately and not restarted on each C# code change. Ideally, decouple the proxying from the Angular CLI-specific bits so it can be reused with other SPA frameworks.
  • Consider whether it's practical to proxy the HMR endpoint. If not, this will be difficult to use from inside a Docker container (or at least requires extra configuration). Update: it does now proxy, so should work fine in Docker.
  • Try to get CLI's progress display reflected in the console.
  • Ensure that any errors occurring in angular-cli-middleware.js are passed back upstream. This also includes the CLI writing to stderr.
  • Stop hard-coding CLI options in angular-cli-middleware.js. Either have a .NET API for configuring them, or better, call a script in package.json (in which case, make the task name appear explicitly in the C# source so people can discover what it does).
  • Make sure the npm child processes aren't left behind on exit, including when using dotnet watch, including on Mac/Linux
  • Get SSR working with publish by default. Waiting on angular/angular-cli#7903
  • Consider whether it's desirable to have package.json and node_modules at project root (is this necessary for VS to do auto-restore?). If so, need to support finding node_modules in ancestor directories using require-from-string-type approach (#154), since Angular app will be in subdirectory. Update: Not doing this - it's impractical. Angular CLI is hardcoded to assume node_modules is in the project root (e.g., see getBrowserConfig in models\webpack-configs\browser.js line 25), so we'd need to have the .NET project root as the Angular CLI project root, which means merging the folder structures (super ugly, and violates the intended separation), or having an extremely nonstandard Angular CLI config to have it fetch all sorts of things from a special subdirectory (violates the idea of it being a standard Angular CLI project). Instead, we'll rely on the .csproj containing instructions to do the applicable npm install in the ClientApp subdir during build when necessary.
  • Figure out how the APIs would look if someone wanted index.html to be a server-rendered cshtml page instead. Update: Not supporting this. Doesn't really make sense.
  • See if it's possible to detect efficiently whether the <base href> matches the PathBase of the incoming request, and if not, fail with a clear message. Alternatives include rewriting index.html dynamically to inject a <base href> based on the per-request PathBase, or just rely on people reading docs to know they have to configure this manually before publishing.
  • Ensure the right set of files are appearing in VS's Solution Explorer (e.g., package.json should be there)
  • Add shrinkwrap
  • Fix the bootstrap nav button in the Angular templates (and check it works in the others)
  • Ship preview

Pre-preview (try it)

You can now try this out. For instructions, please see #1288 (comment)

@amivit

This comment has been minimized.

amivit commented Sep 19, 2017

Totally agree! I've been quite annoyed lately, fighting some CORS-related issues, I still cannot find a solution to (due to having both back- and front-end separated).
I would have loved to have them combined, for server-side-rendering too, but it was an intentional decision (which may seem detrimental now), to utilize the "mainstream" @angular/cli for boilerplate generation.

My "risk assessment" on trying to predict the future, says @angular/cli is likely a safer dependency in the long run.

I think the perfect scenario would be if dotnet new angular relied on the @angular/cli, as suggested, in such a way that you could at any time extract only the Angular project and continue using it only with @angular/cli. Is that feasible?

Google's Angular already relies on Microsoft's TypeScript - can the teams coordinate it so @angular/cli won't make unnecessary breaking changes so it's safe-ish to be embedded in JavaScriptServices :-) ? Or does it not work like that, lol. In any case, please take @SteveSandersonMS post very serious!

Of course, because you then have a standard Angular CLI project, you can use all other CLI features such as ng generate, ng lint, etc., without anything being different just because you're also using ASP.NET.

This would simply be amazing...

@peterdobson

This comment has been minimized.

peterdobson commented Sep 19, 2017

@amivit I don't have my back- and front-end in separate projects anymore but when I did, if I recall it was just a case of having:

  services.AddCors(options => options.AddPolicy("MyPolicy", p => p.AllowAnyOrigin()
                                                                      .AllowAnyMethod()
                                                                      .AllowAnyHeader()
                                                                    ));

.. in the right place in ConfigureServices() and:

app.UseCors("MyPolicy");

...in Configure()

@MarkPieszak

This comment has been minimized.

Contributor

MarkPieszak commented Sep 19, 2017

@SteveSandersonMS We also have Universal support in the CLI now, but I'm wondering if we will be able to pipe that into aspnet somehow as well? Let me know if we can meet up again when you have some time I'd love to help anyway I can to merge the two processes, or help add any hooks Aspnet would need from the CLI.

This would be ideal in letting the CLI community handle all upcoming Angular aspects of the template as well, improvements, etc 👍

Exciting times!

@amivit

This comment has been minimized.

amivit commented Sep 20, 2017

@peterdobson that works for anything except POST. POST is special since the Access-Control-Allow-Origin NEEDS an explicit reference to the requests Origin. So I made an explicit Origin which works fine for everything, except POST.
The issue occurs because browsers by design, strip the initial OPTIONS request of all headers (such as authorize).
For some stupid reason, my app when deployed to IIS, expects the Authorize even on the OPTIONS, and returns 401 unauthorized. If I enable anonymous access globally on the ng frontend, the problem goes away. Which is fine for this internal tool, when the API is still secure.
I guess it's more of an IIS issue, and I don't want to hijack this thread, which I'm already in the process of doing :/

aspnetcore-angular2-universal seems cool, but way too bleeding edge for me to dare use for anything production. I'd prefer relying on @angular/cli with asp.net/JavaScriptServices

@wangkanai

This comment has been minimized.

wangkanai commented Sep 21, 2017

@SteveSandersonMS Is this a plan feature or already release? Because choosing the angular template today would get the webpack middleware, which has many bug. Upgrading to bootstrap 4 took me the whole afternoon without success. Npm will not install bootstrap 4 in visual studio 2017 enterprise. So went to command line and did it, then SpaService would not webpack the bootstrap 4 into the bundle.
Just gave up in the end.

@jrmcdona

This comment has been minimized.

jrmcdona commented Sep 24, 2017

@wangkanai it is not already released

@chencingyen

This comment has been minimized.

chencingyen commented Sep 27, 2017

@SteveSandersonMS this is that i've been waiting for :)
nice 👍

@spboyer

This comment has been minimized.

spboyer commented Sep 28, 2017

This also solves questions around using environment variables/settings for client-side code.
https://github.com/angular/angular-cli/wiki/stories-application-environments

@SteveSandersonMS

This comment has been minimized.

Member

SteveSandersonMS commented Sep 28, 2017

In case anyone wants to see, I'm working on this here: https://github.com/aspnet/templating/tree/stevesa/angular-cli-template. Don't ask for support with getting or using it yet though - it's not ready 😄

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Sep 29, 2017

This would be awesome! The production builds through angular cli are also very well optimized and also includes features such as hash file name.

@jrmcdona

This comment has been minimized.

jrmcdona commented Sep 29, 2017

@spboyer I could use that too. Had trouble getting it working well using the Angular CLI built in enviornments configs.

@smoses2

This comment has been minimized.

smoses2 commented Oct 11, 2017

Fantastic news. For the last year, I've been avoiding javascript services since as Steve notes at top of this issue, many of us prefer the angular cli. I have been building my angular 2/4 with angular CLI in its own project and writing to an asp.net core wwwroot directory for production. But this missed all the tooling you've developed around javascript services, made integrated testing slow and split the angular app from my development process for the Api/MVC and IdentityServer projects.

Looking forward to seeing what you do with exposing angular cli, while maintaining other javascript services tooling (outside webpack),and config for the enclosing asp.net core project. My greatest concern would be to not interfere with the angular upgrade process - i.e. when I update the package.json with angular 5..6..7, that the coupling to asp.net core and javascript services stays loose enough, that the breaks I need to fix are angular specific.

@Alex-Torres

This comment has been minimized.

Alex-Torres commented Oct 13, 2017

Just saw a demo first look of this @AngularMix and it was simply awesome!! This is would definitely deliver great value to many developers who want to get into Angular using Asp.net Core but often struggle with the complicated processes of getting both technologies to coordinate. Thank you @SteveSanderson and Asp.net Team!

@Alex-Torres

This comment has been minimized.

Alex-Torres commented Oct 13, 2017

@SteveSandersonMS where can I find a sample project? Looks like the URL feed you provided above is no longer available: Link

@Meligy

This comment has been minimized.

Meligy commented Oct 13, 2017

It seemed to have moved here:
https://github.com/aspnet/templating/tree/feature/stevesa/angular-cli-template

@SteveSandersonMS is ALWAYS amazing, and this feature is going to be SO BIG!

@Alex-Torres

This comment has been minimized.

Alex-Torres commented Oct 13, 2017

Thank you @Meligy.

@IshamMohamed

This comment has been minimized.

IshamMohamed commented Oct 23, 2017

Agreed. Recently blogged about how to make existing .NET Core SPA templates to support Angular-cli https://programmium.wordpress.com/2017/10/02/ng-add-angular-cli-to-net-core-angular-template/ but this project is going to be a great benefit..

@mpalmer-sps

This comment has been minimized.

mpalmer-sps commented Oct 26, 2017

Thanks @SteveSandersonMS for the heads up! Digging into this now.

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Oct 26, 2017

@SteveSandersonMS please target Angular 5 and aspnetcore-engine 5.
Angular 5 is currently in Rc6 and aspnetcore-engine 5 is currently in 5.0.0-beta.4 hopefully by the time this template is released Angular 5 and aspnetcore-engine 5 would be production ready.

@asadsahi

This comment has been minimized.

asadsahi commented Oct 31, 2017

Really excited to hear about Angular-cli integration.
image

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Nov 2, 2017

@SteveSandersonMS Angular CLI now has support for Lazy Loading routes for Server-side rendering, I hope its support can also bee added to the template.

@mpalmer-sps

This comment has been minimized.

mpalmer-sps commented Nov 4, 2017

@SteveSandersonMS -- Shawn Wildermuth posted a blog about doing this with ASP .Net Core, Entity Framework Core and the Angular CLI [https://wildermuth.com/2017/07/10/ASP-NET-Core-2-VS2017-Angular-CLI ]. There is also a corresponding PluralSight course for this as well.

@secondcircle

This comment has been minimized.

secondcircle commented Dec 7, 2017

Is there a way to have certain routes/modules not get put into the bundle that is created for Server Side Rendering? I'm using TinyMCE and getting errors because it tries to access a dom element.

@mac2000

This comment has been minimized.

mac2000 commented Dec 7, 2017

@secondcircle you might check if you are rendering inside browser or not before instantiating TinyMCE, e.g.:

ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      this.cities = this.service.getCities()
    }
}

so from backend you will receive page without tinymce, but after application is being bootstraped ngOnInit will still run and will go insade if check and instantiate TinyMCE

hope that helps

@mac2000

This comment has been minimized.

mac2000 commented Dec 7, 2017

Here is what I were talking about, CLI 1.6.0 released - from now on there is a "right" way to create universal apps, e.g.:

ng new my
cd my
ng g universal ssr
npm i
ng b -prod
ng b -prod --app ssr

but still you are required to provide your simple server.js file which will serve generated dist files

it will be never ending race condition, will be nice if there will be instructions how to setup dotnet core or may be template without app itself, e.g. generated dotnet core app is expecting that there is dist-server folder and serves app from it

@NazirTemirov

This comment has been minimized.

NazirTemirov commented Dec 7, 2017

Has anyone tried to translate an old project into a new template? Can you provide a detailed description of how to do this?

Thanks.

@svoychik

This comment has been minimized.

svoychik commented Dec 7, 2017

@NazirTemirov I've tried to upgrade old project into a new template (and also from ng 4 to ng 5). After few unsuccessful attempts to migrate, I've already understood that the best way to upgrade project is to move each of your components separately (especially if you project is really big and has a lot of dependencies). Maybe this approach is not the best and very fast, but it helps you to better understand what is wrong with your code and easily fix it.

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Dec 7, 2017

I have been able to migrate a major portion of a relatively large project to this template without any major issues.

If you are migrating an Angular CLI based project, I believe the migration should be more smooth than if you are migrating an old .Net Core Angular template based project.

For issue @secondcircle is having (or even for few of the other issues as well) take a look at
angular/angular-cli#8749 , these issues are not due to this template rather its how Angular CLI works.

Two major issues, which I think still exists in this template are:

Lazy Loading doesn't with with SSR and I already have an open issue about it #1413.

Secondly, when we publish it also deploys node_modules folder. See #1288 (comment)

@SteveSandersonMS since Angular CLI 1.6 is released, can you please update the template and also take a look at the above two issues since with these issue, its difficult to use a project based on this template in production.

@NazirTemirov

This comment has been minimized.

NazirTemirov commented Dec 7, 2017

Thanks @svoychik and @naveedahmed1. I migrated the old .Net Core Angular template based project. It seems everything works well!

@secondcircle

This comment has been minimized.

secondcircle commented Dec 7, 2017

@naveedahmed1 @mac2000 Thanks for the advice but unfortunately it doesn't fix the issue.

I'm not receiving an exception when I try to load that specific component, It's actually throwing an exception when the first request is sent to the server, even on a page that works fine with server-side rendering. Because the TinyMCE JS file that references a dom element is bundled in the file that gets generated when you compile the server side rendered bundle. I think, I'm still pretty new at this.
Again this only happens when you publish the app, using webpack on the development side everything works as expected with no errors.

I basically have a front end of the site that I need to work for SEO and a back-end admin side where there is a login and some admin related stuff that I don't need SEO for, and I seem unable to find a way to set this admin section to not even bother with anything related to SSR because it is such a hassle.

In this specific example, I have only added the Module and Import statement, I'm not even using the module anywhere in the project and it still throws the exception.

I've attached the error log.

Error.txt

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Dec 7, 2017

@secondcircle I understood your issue at first place since I also faced this.

As I suggested, the solution is in the discussion at angular/angular-cli#8749 . And there isn't anything wrong in this template that should cause this issue.

You need to add exclude array to tsconfig.server.json which include files that breaks SSR. Like angular/angular-cli#8749 (comment)

@secondcircle

This comment has been minimized.

secondcircle commented Dec 7, 2017

I see what you're saying, I just can't seem to make that work. I tried excluding every single file I could find related to TinyMCE and I still get the error. I may just end up splitting it up into two angular apps.

@SteveSandersonMS

This comment has been minimized.

Member

SteveSandersonMS commented Dec 8, 2017

The preview1 version of this new template is now published. For installation instructions, please see #1288 (comment). Full docs on this will appear on https://docs.microsoft.com/en-us/aspnet/core/index soon - hopefully in the next day or so. We aim to release the final released version of the new SPA templates in January, depending on feedback.

Now seems like a good time to mark this giant issue as closed. For any further issues discovered with the new templates, please post separate issues. Thanks for all the feedback so far!

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Dec 8, 2017

@SteveSandersonMS that's awesome. Any update on node_modules folder that is copied to deployment folder when we published?

@SteveSandersonMS

This comment has been minimized.

Member

SteveSandersonMS commented Dec 8, 2017

@naveedahmed1 Please see angular/angular-cli#8616 for any updates on that. If this is important to you, please consider helping the Angular CLI team find a fix for that and submit a PR to that repo.

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Dec 8, 2017

@ridicoulous

This comment has been minimized.

ridicoulous commented Dec 8, 2017

Hello everyone. How about to add StaticFileOptions to UseSpaStaticFiles() for response caching? May be, I did it wrong, but this approach does not working and I think. problem is in UseSpaStaticFiles method:
app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = (context) => { var headers = context.Context.Response.GetTypedHeaders(); headers.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue { Public = true, MaxAge = TimeSpan.FromDays(365) }; } });

@naveedahmed1

This comment has been minimized.

naveedahmed1 commented Dec 8, 2017

@ridicoulous can you please open a new issue at https://github.com/aspnet/JavaScriptServices/issues
its a giant thread and already marked as closed after release of preview1.

@andrewjsaid

This comment has been minimized.

andrewjsaid commented Dec 12, 2017

I may have found a regression with the newest template Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-preview1-final.

In older templates, the Program class had a method IWebHost BuildWebHost which has been replaced with IWebHostBuilder CreateWebHostBuilder.

The problem occurs when attempting to run EF migrations dotnet ef migrations add Migration1. The new template causes the following error to appear

Unable to create an object of type 'DbContext'. Add an implementation of 'IDesignTimeDbContextFactory<DbContext>' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.

Some digging about shows that BuildWebHost is a convention expected by Entity Framework Core Design Tools. see https://github.com/aspnet/Hosting/blob/cda9ec6fe416eaa4fdc8f72276c7b16941d9761c/shared/Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources/WebHostFactoryResolver.cs

There is a solution - which is to expose the IDesignTimeDbContextFactory<DbContext> as suggested in the error message - but it would be easier for everybody if EF Design tools work out of the box.

As general feedback, the template works well and feels more natural to use than previous webpack-based one. However the requirement to copy node_modules folder when using SSR is kind of a big drawback as has been mentioned by others.

Also, for my use case which had dependencies on ng-bootstrap, I ran into problems where node could not compile their .js files with ES6 import statements. This was not a problem with the previous template where webpack would remove these statements.

@Meligy

This comment has been minimized.

Meligy commented Dec 12, 2017

@andrewjsaid it's super awesome that you tracked down the root cause for this. Can you please open a new issue for it? This is the suggested action a few comments back.

As much as I hate saying this, but it might be time to lock / limit this contributors of this thread in favor of new issues.

@SteveSandersonMS

This comment has been minimized.

Member

SteveSandersonMS commented Dec 13, 2017

OK, thanks for the suggestion @Meligy. I will lock this thread now, only so that people will post any further issues as separate issues and we don't keep spamming everyone who's ever commented here :)

@aspnet aspnet locked and limited conversation to collaborators Dec 13, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.