如何将 asp.net mvc 视图渲染为 angular 2?

How to render asp.net mvc view into angular 2?

我正在尝试将 asp.net mvc 与 angular 2 应用程序集成。我知道这并不理想,但我被要求将一些现有的 Mvc 功能(想想大型遗留应用程序)集成到一个全新的 Angular 2 spa 中。

我希望能够做的是拥有一个包含 angular 组件以及纯 mvc 内容的 cshtml 视图...

<side-bar></side-bar>
<action-bar></action-bar>

@{
    Html.RenderPartial("_SuperLegacyPartialView");   
}

我正在努力寻找任何方法来做到这一点。此博客 post 看起来很有前途 - http://www.centare.com/tutorial-angular2-mvc-6-asp-net-5/. It used a templateUrl value that pointed to a path rendered by Mvc, as well as AsyncRoute, but none of that works anymore in Angular 2. This post looked promising as well - http://gbataille.github.io/2016/02/16/Angular2-Webpack-AsyncRoute.html,但它也使用 AsyncRoute,但已弃用。

这在 Angular 中曾经非常简单 1. 我们过去要么手动 bootstrap angular 到 Razor 视图中,要么将部分视图呈现为 [= 的 templateUrl =22=]。在使用 Webpack 的最新 Angular 2 中执行此操作的最佳方法是什么?

使用 Systemjs: https://github.com/VahidN/MVC5Angular2 使用网页包: http://blog.stevensanderson.com/2016/10/04/angular2-template-for-visual-studio/

我是这样做的

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

"/Template/Index"是你的MVC Controller中的URL,然后是方法。

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

我的问题是我不知道每次刷新视图以调用控制器方法是如何加载的。

我想出了一个解决方案,满足了我当时的需求。我将 angular-cli 与 WebPack 一起使用,这可以满足我的需要。我不明白我看到的所有示例都说要使用 "templateUrl: '/Template/Index'",其中路径是 MVC 视图的路径。这根本行不通,因为在 WebPack 创建的任何捆绑视图中都找不到该路径。也许那些人没有使用 angular-cli 和 WebPack。

这个 Whosebug 答案 - 对创建以下指令非常有帮助。该指令将获取 mvc 局部视图的输出并对其进行编译。它允许发生 Razor/server 逻辑,并且还可以编译一些 angular 。虽然,实际上在这个 MVC 部分中包含其他组件是有问题的。如果你成功了,请告诉我你做了什么。就我而言,我只需要进行服务器渲染并将其准确放置在 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(假设您的 mvc 应用与您的 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>

StuffsController.cs

public PartialViewResult MvcStuff() => PartialView();

我需要在我的 angular 4 应用程序中使用 MVC PartialView html,由 HttpClient .get 方法调用。

我用了

将我的部分视图转换为 html 字符串。我在容器 json 对象中返回了它,并将其设置为一个变量,该变量在我的页面上设置了 div 的 html ..因此:

    ...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;
   }

...在服务器上

  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);
      }
}

请注意:这只适用于不需要事件侦听器的静态 html。我无法使用 renderer2 将点击事件添加到已加载的 html,尽管我不是专家,但有可能。

您需要创建 ViewRender class 并将注入指令添加到 startup.cs 文件中,如 post

所示

对于 Angular 7 的那些人,您需要稍微更改已接受的答案才能使其生效。

MvcPartialDirective中:

将 Http 更新为 HttpClient,使其显示为:

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

在 ngOnInit() 中,指定响应类型:

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

管道更新:

.pipe(map(res => res.toString()))(注意 toString() 而不是 .text())

可选地使用 app 指令规范前缀:

@Directive({ selector: 'appActionResult' })

最终结果:

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();
    }
  }
}