【问题标题】:How to render asp.net mvc view into angular 2?如何将 asp.net mvc 视图呈现为 Angular 2?
【发布时间】:2017-01-04 21:27:30
【问题描述】:

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

我想做的是有一个 cshtml 视图,其中包含角度组件,以及纯 mvc 的东西......

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

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

我正在努力寻找任何方法来做到这一点。这篇博文看起来很有希望 - http://www.centare.com/tutorial-angular2-mvc-6-asp-net-5/。它使用了一个 templateUrl 值,该值指向由 Mvc 渲染的路径,以及 AsyncRoute,但在 Angular 2 中这些都不再适用。这篇文章看起来也很有希望 - http://gbataille.github.io/2016/02/16/Angular2-Webpack-AsyncRoute.html,但它也使用了 AsyncRoute,它已被弃用。

这在 Angular 1 中曾经非常简单。我们过去要么手动将 Angular 引导到 Razor 视图中,要么将部分视图渲染为组件/指令的 templateUrl。在使用 Webpack 的最新 Angular 2 中,最好的方法是什么?

【问题讨论】:

    标签: asp.net-mvc angular webpack asp.net-mvc-partialview


    【解决方案1】:

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

    这个 stackoverflow 答案 - How can I use/create dynamic template to compile dynamic Component with Angular 2.0? 对创建以下指令非常有帮助。该指令将获取 mvc 部分视图的输出并对其进行编译。它允许发生 Razor/服务器逻辑,并且还可以编译一些角度。虽然,实际上在这个 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();
        }
      }
    }
    

    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 模板是有意义的,因此您可以轻松地渲染 UserName 或在那里执行其他用户特定的业务逻辑(权限等)。
    • 只有这样才能实现?
    • 收到'undefined' is not assignable to type 'ComponentFactory&lt;any&gt;' 错误
    • 我已经有一段时间没有接触过这段代码了。我的猜测是角度的变化可能会导致这种情况。如果我有时间,我会尝试重现它。
    • 我刚刚看到您关于此的其他问题。听起来 TypeScript 抱怨 createComponentFactory 可能返回未定义。是否将 createComponentFactory 的返回类型更改为 Promise> |承诺?
    【解决方案2】:

    我是这样做的。

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

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

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

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

    【讨论】:

      【解决方案3】:

      对于使用 Angular 7 的用户,您需要稍微更改已接受的答案以使其正常工作。

      MvcPartialDirective 中:

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

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

      在ngOnInit()中,指定responseType:

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

      管道更新:

      .pipe(map(res =&gt; 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();
          }
        }
      }
      

      【讨论】:

        【解决方案4】:

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

        我用AMD's post

        将我的部分视图转换为 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 类并将注入指令添加到 startup.cs 文件中,如 AMDs 帖子所示

        【讨论】:

          【解决方案5】:

          【讨论】:

          • 感谢您的回复,但以上教程都没有解决我的问题,这就是为什么很难找到答案的原因。与网络上的所有其他教程一样,这些教程只是描述了如何在 MVC 中设置 Angular spa。他们没有解决如何在同一页面上呈现 MVC 部分和角度组件。
          猜你喜欢
          • 2011-01-28
          • 2016-10-10
          • 1970-01-01
          • 2012-03-03
          • 2010-11-22
          相关资源
          最近更新 更多