Asp.netmvcをangular 2アプリケーションと統合しようとしています。これは理想的ではないことは理解していますが、既存のMvc機能を統合するように求められています(大きなレガシーアプリを考えてください)真新しいAngular 2スパ。
私ができるようにしたいのは、angularコンポーネントと純粋なmvcのものを含むcshtmlビューを持つことです...
<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。では非常に簡単でした。手動でbootstrap angular Razor Viewに入れるか、またはコンポーネント/ディレクティブのtemplateUrlとして部分ビューをレンダリングします。Webpackを使用する最新のAngular 2でこれを行うための最良の方法は何ですか?
当時の自分のニーズを満たすソリューションを思いつきました。私はWebPackでangular-cliを使用していますが、これは私のニーズに合っていました。パスがMVCビューへのパスである、「templateUrl: '/ Template/Index'」を使用するという、私が見たすべての例を理解していません。 WebPackが作成するバンドルされたビューのいずれにもパスが見つからないため、これは単に機能しません。たぶんそれらの人々はangular-cliとWebPackを使用していません。
このstackoverflowの回答-- 動的テンプレートを使用/作成してAngular 2.0? で動的コンポーネントをコンパイルするにはどうすればよいですか?)==は、次のディレクティブの作成に非常に役立ちました。このディレクティブは、 mvcパーシャルビューの出力とコンパイル。これにより、Razor /サーバーロジックが実行され、一部のangularもコンパイルされます。ただし、実際には、このMVCパーシャル内に他のコンポーネントが含まれていました。問題があります。問題が発生した場合は、何をしたかをお知らせください。私の場合は、サーバーレンダリングを実行し、それを必要な場所に正確に配置する必要がありましたAngular 2スパ。
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();
}
}
}
insome-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>
inStuffsController.cs
public PartialViewResult MvcStuff() => PartialView();
こんな感じでした。
@Component({
templateUrl: '/Template/Index'
})
export class TemplateComponent {}
「/ Template/Index」は、MVCコントローラーのURLであり、次にメソッドです。
public IActionResult Index()
{
return PartialView();
}
私の問題は、ロードされるたびにビューを更新してコントローラーメソッドを呼び出す方法がわからないことです。
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()))
(。text()の代わりにtoString()に注意してください)
オプションで、ディレクティブ指定に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();
}
}
}
_
HttpClient.getメソッドによって呼び出されるangular 4アプリケーションでMVCPartialViewhtmlを使用する必要がありました。
私は使用しました AMDの投稿
部分ビューを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にクリックイベントを追加できませんでした。
AMDs postに示すように、ViewRenderクラスを作成し、startup.csファイルにインジェクション命令を追加する必要があります。