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

时间:2017-01-04 21:27:31

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

我试图将asp.net mvc与angular 2应用程序集成。我知道这并不理想,但我被要求将一些现有的Mvc功能(想想大遗产应用程序)整合到一个全新的Angular 2水疗中心。

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

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

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

我很难找到办法做到这一点。这篇博文看起来很有希望 - http://www.centare.com/tutorial-angular2-mvc-6-asp-net-5/。它使用了一个指向由Mvc和AsyncRoute呈现的路径的templateUrl值,但在Angular 2中它们都不再有效。这篇文章看起来很有前途 - http://gbataille.github.io/2016/02/16/Angular2-Webpack-AsyncRoute.html,但它也使用了AsyncRoute,这是弃用。

这在Angular 1中非常容易。我们曾经手动将角度引导到Razor视图中,或者将局部视图渲染为组件/指令的templateUrl。在使用Webpack的最新Angular 2中执行此操作的最佳方法是什么?

5 个答案:

答案 0 :(得分:11)

我想出了一个满足我当时需求的解决方案。我正在使用带有WebPack的angular-cli,这符合我的需求。我不明白我见过的所有使用“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应用与您的水疗中心共享域名)

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

答案 1 :(得分:1)

我是这样做的。

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

&#34; /模板/索引&#34;是MVC控制器中的URL,然后是方法。

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

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

答案 2 :(得分:0)

我需要在我的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 post

所示

答案 3 :(得分:0)

对于使用Angular 7的用户,您将需要稍作更改才能使其正常工作。

MvcPartialDirective 中:

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

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

在ngOnInit()中,指定responseType:

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

答案 4 :(得分:-1)