How to display asp.net mvc view in angular 2?

I am trying to integrate asp.net mvc with an angular 2 app. I understand that this is not ideal, but I will be asked to integrate some existing Mvc features (I think a great legacy) into the new angular 2 spa.

What I would like to do is to have a cshtml view containing angular components in it, as well as pure mvc stuff ...

<side-bar></side-bar> <action-bar></action-bar> @{ Html.RenderPartial("_SuperLegacyPartialView"); } 

I'm struggling to find a way to do this. This blog post looked promising - http://www.centare.com/tutorial-angular2-mvc-6-asp-net-5/ . He used a templateUrl value indicating the path displayed by Mvc, as well as AsyncRoute, but none of this works in angular 2 anymore. This post also looked promising - http://gbataille.imtqy.com/2016/02/16/Angular2 -Webpack-AsyncRoute.html , but also uses AsyncRoute, which is deprecated.

It was very easy in angular 1. We used either manually loading angular in Razor View, or displaying a partial view as templateUrl of the component / directive. What is the best way to do this in the latest angular 2 that Webpack uses?

+7
source share
5 answers

I came up with a solution that met my needs at that time. I am using angular-cli with WebPack and it worked for my needs. I do not understand all the examples that I saw that say to use "templateUrl: / Template / Index", where the path is the path to the MVC view. It just doesn't work, because the path cannot be found inside any of the associated views created by WebPack. Perhaps these people do not use angular-cli and WebPack.

This stackoverflow answer - How can I use / create a dynamic template to compile a dynamic component with Angular 2.0? , helped a lot in creating the next directive. This directive will output a partial view of mvc and compile it. It allows you to execute Razor / server logic, as well as compiling some Angular. Although, in fact, including other components inside this part of MVC, it was problematic. If you earn, let me know what you did. In my case, I just needed to render the server to place it exactly where I wanted it in the Angular 2 spa.

MvcPartialDirective

 import { Component, Directive, NgModule, Input, ViewContainerRef, Compiler, ComponentFactory, ModuleWithComponentFactories, ComponentRef, ReflectiveInjector, OnInit, OnDestroy } from '@angular/core'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import {Http} from "@angular/http"; import 'rxjs/add/operator/map'; export function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any>> { const cmpClass = class DynamicComponent {}; const decoratedCmp = Component(metadata)(cmpClass); @NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp] }) class DynamicHtmlModule { } return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule) .then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) => { return moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp); }); } @Directive({ selector: 'mvc-partial' }) export class MvcPartialDirective implements OnInit, OnDestroy { html: string = '<p></p>'; @Input() url: string; cmpRef: ComponentRef<any>; constructor(private vcRef: ViewContainerRef, private compiler: Compiler, private http: Http) { } ngOnInit() { this.http.get(this.url) .map(res => res.text()) .subscribe( (html) => { this.html = html; if (!html) return; if(this.cmpRef) { this.cmpRef.destroy(); } const compMetadata = new Component({ selector: 'dynamic-html', template: this.html, }); createComponentFactory(this.compiler, compMetadata) .then(factory => { const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); this.cmpRef = this.vcRef.createComponent(factory, 0, injector, []); }); }, err => console.log(err), () => console.log('MvcPartial complete') ); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } } 

in some-component.html (if your mvc application shares the domain with your spa)

 <mvc-partial [url]="'/stuffs/mvcstuff'"></mvc-partial> 

Mvcstuff.cshtml

 @{ ViewBag.Title = "This is some MVC stuff!!!"; } <div> <h2>MVC Stuff:</h2> <h4>@ViewBag.Title</h4> <h2>Angular Stuff:</h2> <h4>{{1 + 1}}</h4> </div> 

in stuffscontroller.cs

 public PartialViewResult MvcStuff() => PartialView(); 
+11
source

I did it like that.

 @Component({ templateUrl: '/Template/Index' }) export class TemplateComponent {} 

"/ Template / Index" is the URL of your MVC controller, and then the method.

 public IActionResult Index() { return PartialView(); } 

My problem is that I do not know how the update of the method of invoking the controller call is loaded every time.

+1
source

I needed to use the MVC PartialView html in my angular 4 application called by the HttpClient.get method.

I used the AMD post

to convert my partial view to html string. I returned this to the container json object and set it to a variable that sets the html div on my page..thus:

  ...in the template <div class="files" [innerHtml]="myTemplate"> </div> ... in the component .ts file export interface htmldata { html: string; } ... inside component getDivHtml(path: string): Promise<htmldata> { return this.http .get<htmldata>(`${this.baseUrl}/MVC/Index?path=` + path , { withCredentials: true }) .toPromise(); } ngOnInit() { this.getDivHtml('').then( data => { this.loadData(data); }, ).catch( error => { console.log(error); }); } loadData(data: htmldata) { this.myTemplate = data.html; } 

... on server

  public class HtmlReturn { public string html { get; set; } } [Produces("application/json")] [Route("api/MVC/[action]")] public class MVCController : Controller { private readonly ViewRender view; public MVCController(ViewRender view) { this.view = view; } public IActionResult Index(string path) { data.html = this.view.Render("viewpath", viewModel); return Json(data); } } 

Please note: this only works with static html, which does not require event listeners. I was not able to add click events to the loaded html using renderer2, although I am not an expert, and this is possible.

You will need to create a ViewRender class and add the instruction to be inserted into the startup.cs file, as shown in the published article.

0
source

For those working on Angular 7 , you need to modify the accepted answer a bit to make it work.

In MvcPartialDirective :

Update Http to HttpClient so that it reads:

import { HttpClient } from '@angular/common/http';

In ngOnInit (), specify responseType:

this.http.get(this.url, {responseType: "text"})...

Update to pipe:

.pipe(map(res => res.toString())) (note toString () instead of .text ())

Optionally use the app prefix to specify the directive:

@Directive({ selector: 'appActionResult' })

Final result:

 import { Component, Directive, NgModule, Input, ViewContainerRef, Compiler, ComponentFactory, ModuleWithComponentFactories, ComponentRef, ReflectiveInjector, OnInit, OnDestroy } from '@angular/core'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; export function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any>> { const cmpClass = class DynamicComponent { }; const decoratedCmp = Component(metadata)(cmpClass); @NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp], schemas: [NO_ERRORS_SCHEMA] }) class DynamicHtmlModule { } return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule) .then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) => { return moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp); }); } @Directive({ selector: 'appActionResult' }) export class ActionResultDirective implements OnInit, OnDestroy { html = '<p></p>'; @Input() url: string; cmpRef: ComponentRef<any>; constructor(private vcRef: ViewContainerRef, private compiler: Compiler, private http: HttpClient) {} ngOnInit() { this.http .get(this.url, {responseType: "text"}) .pipe(map(res => res.toString())) .subscribe( (html) => { this.html = html; if (!html) { return; } if (this.cmpRef) { this.cmpRef.destroy(); } const compMetadata = new Component({ selector: 'dynamic-html', template: this.html, }); createComponentFactory(this.compiler, compMetadata) .then(factory => { const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); this.cmpRef = this.vcRef.createComponent(factory, 0, injector, []); }); }, err => console.log(err), () => console.log('MvcPartial complete') ); } ngOnDestroy() { if (this.cmpRef) { this.cmpRef.destroy(); } } } 
0
source

Source: https://habr.com/ru/post/1013764/


All Articles