您可能需要其他选项,所以我在第2段中编写了它:
- 无需通信的Angular-ReactJS
- 具有双向通信的Angular-ReactJS
下面的所有代码都是最少的,无法在提出的步骤中显示问题。在GitHub上,您拥有完整的代码来解决问题,以下示例并不总是1:1,因为此代码已扩展。
无需通信的Angular-ReactJS
要将ReactJS应用添加到现有的Angular应用中,您需要安装5个npm依赖项:
react,
react-dom:
npm install --save reactnpm install --save react-domnpm install --save-dev @types/reactnpm install --save-dev @types/react-domnpm install --save-dev @types/react-select
下一步-我们应该允许
jsx在
.tsx文件中使用模板,因此我们应该进行编辑
tsconfig.json并添加:
{ ... "compilerOptions": { … "jsx": "react"}如果使用WebStorm,则应重新启动项目,因为tslint会显示错误,直到重新启动。
为了保持结构清晰,我创建了以下目录结构:
angular / ng-hero.component.ts // Component in Angular react-renderer.component.ts // ReactJS renderer without communicationreact / react-application.tsx // React init application react-hero.tsx // React hero componentapp.component.htmlapp.component.ts
现在,您需要在Angular中创建特殊组件,该组件将负责嵌入ReactJS应用程序。我将调用此组件
ReactRendererComponent。这个组件非常简单,它只有一行模板行,带有的构造函数
importInjector和一行
ngOnInit:
@Component({ selector: 'app-react-renderer', template: `<div id="react-renderer"></div>`})export class ReactRendererComponent implements onInit { constructor(public injector: Injector) { } ngonInit() { ReactApplication.initialize('react-renderer', this.injector); }}现在我们需要
ReactApplication在其中初始化ReactJS应用程序的组件:
interface IReactApplication { injector: Injector;}class ReactApp extends React.Component<IReactApplication, any> { constructor(props) { super(props); } render() { return ( <div className={'renderer'}> <h2>ReactJS component: </h2> <br/> <ReactHero/> </div> ); }}export class ReactApplication { static initialize( containerId: string, injector: Injector ) { ReactDOM.render( <ReactApp injector={injector}/>, document.getElementById(containerId) ); }}我们需要
ReactHero以下示例中使用的组件:
class ReactHero extends React.Component<any, any> { constructor(props) { super(props); } render() { return ( <span> <span>react-hero works!</span><br/> <span>Don't have any data</span> </span> ); }}export default ReactHero;在Angular App中,我们应该使用
ReactRenderercomponent,所以我们使用:
App.component data:<hr><h2>This is Angular</h2><img width="100" alt="Angular Logo" ><hr><!-- Without data binding --><app-react-renderer></app-react-renderer>
目前,我们有带有嵌入式ReactJS应用程序的Angular应用程序,但是没有任何通信。对您来说足够了吗?如果是的话,就这些了。如果您需要在两个应用程序之间进行任何形式的通信,请在下面为您介绍RxJS选项。
具有双向通信的Angular-ReactJS
在此示例中,RxJS支持双向数据绑定。您可以获取此数据,并在ReactJS应用和Angular应用中使用它们以查看所有更改。这对于许多项目来说已经足够了,但是您可以使用不同的选项来获得这种双向通信,例如,可以对它们使用Redux。
为了清楚起见,下面我将提供此部分的完整目录结构:
angular / hero.service.ts ng-hero.component.ts // Component in Angular react-bidirectional-renderer.component.ts // ReactJS renderer with bidirectional communicationmodel / hero.ts // interface for Hero objectreact-bidirectional react-bidirectional-application.tsx // React init application with bidirectional communication react-bidirectional-hero.tsx // React hero component with RxJS supportapp.component.htmlapp.component.ts
首先,我们创建
IHero带有数据的接口:
/model/hero.ts
export interface IHero { name: string; age: number;}在下一步中,我们创建
angular/hero.service.ts服务,以在应用程序的Angular部分中使用它:
@Injectable({ providedIn: 'root'})export class HeroService { private heroes$: BehaviorSubject<IHero[]> = new BehaviorSubject([]); constructor() { } addHeroes(hero: IHero) { // To add new hero const actualHero = this.heroes$.value; actualHero.push(hero); this.heroes$.next(actualHero); } updateHeroAge(heroId: number, age: number) { // To update age of selected hero const actualHero = this.heroes$.value; actualHero[heroId].age = age; this.heroes$.next(actualHero); } getHeroes$(): BehaviorSubject<IHero[]> { // To get BehaviorSubject and pass it into ReactJS return this.heroes$; }}然后
app.component.ts我们用数据初始化(Zeus和Poseidon):
@Component({ selector: 'app-root', templateUrl: './app.component.html'})export class AppComponent implements onInit { public heroesObj$: BehaviorSubject<IHero[]>; public heroes: IHero[]; constructor(private heroService: HeroService) {} ngonInit(): void { this.heroService.getHeroes$().subscribe((res: IHero[]) => { this.heroes = res; }); this.heroesObj$ = this.heroService.getHeroes$(); this.initHeroes(); } initHeroes() { this.heroService.addHeroes({name: 'Zeus', age: 88}); this.heroService.addHeroes({name: 'Poseidon', age: 46}); }}在下一步中,我们应该准备应用程序的ReacJS部分,因此我们创建
react-bidirectional/react-bidirectional-application.tsx文件:
interface IReactBidirectionalApp { injector: Injector; heroes$: BehaviorSubject<IHero[]>; // We use this interface to grab RxJS object}class ReactBidirectionalApp extends React.Component<IReactBidirectionalApp, any> { constructor(props) { super(props); this.state = { heroes$: this.props.heroes$ // and we pass this data into ReactBidirectionalHero component }; } render() { return ( <div className={'renderer'}> <h2>ReactJS component (bidirectional data binding): </h2> <ReactBidirectionalHero heroes$={this.state.heroes$}/> </div> ); }}export class ReactBidirectionalApplication { static initialize( containerId: string, injector: Injector, heroes$: BehaviorSubject<IHero[]>, // This is necessary to get RxJS object ) { ReactDOM.render( <ReactBidirectionalApp injector={injector} heroes$={heroes$}/>, document.getElementById(containerId) ); }}在下一步中,我们需要
ReactBidirectionalHero组件,因此我们将其创建:
interface IReactBidirectionalHero { heroes$: BehaviorSubject<IHero[]>;}class ReactBidirectionalHero extends React.Component<IReactBidirectionalHero, any> { constructor(props) { super(props); this.state = { heroes: [] }; this.addAge = this.addAge.bind(this); // Register function to bump age this.addHero = this.addHero.bind(this); // Register function to add new Hero } componentDidMount(): void { // In componentDidMount we subscribe heroes$ object this.props.heroes$.subscribe((res: IHero[]) => { // and we pass this data into React State object this.setState({heroes: res}); }); } addAge(i: number) { const temp = this.state.heroes; temp[i].age = temp[i].age + 1; // In this way we update RxJS object this.props.heroes$.next( temp); } addHero() { const temp = this.state.heroes; temp.push({name: 'Atena', age: 31}); // In this way we update RxJS object this.props.heroes$.next(temp); } render() { // Hire we render RxJS part of application with addAge button and ADD ATENA button below const heroes = this.state.heroes.map((hero: IHero, i) => { return <span key={i}>{hero.name} - {hero.age} <button onClick={() => this.addAge(i)}>Add {hero.name} age</button><br/></span>; }); return ( <span> <span>react-hero works!</span><br/> {heroes} <br/> <button onClick={this.addHero}>ADD ATENA</button> </span> ); }}export default ReactBidirectionalHero;现在我们需要在Angular应用程序中初始化ReactJS应用程序,因此我们可以创建
angular/react-bidirectional-renderer.component.ts它-非常简单,与版本相比只有一个更改,而无需通信:
@Component({ selector: 'app-react-owc-renderer', template: `<div id="react-owc-renderer"></div>`})export class ReactBidirectionalRendererComponent implements onInit { // Hire we get data from parent component, but of course we can also subscribe this data directly form HeroService if we prefer this way @Input() heroes$: BehaviorSubject<IHero[]>; constructor(public injector: Injector) { } ngonInit() { // We add only one parameter into initialize function ReactBidirectionalApplication.initialize('react-owc-renderer', this.injector, this.heroes$); }}现在,我们应该进行一些更改
ng-hero.component.ts以查看所有效果:
@Component({ selector: 'app-ng-hero', template: ` <div> <span>ng-hero works!</span><br/> <span *ngFor="let hero of heroes; let i = index;">{{hero.name}} - {{hero.age}} - <button (click)="addAge(i)">Add {{hero.name}} age</button><br/></span> <br/> <button (click)="addHero()">ADD AFRODITA</button> </div> `})export class NgHeroComponent implements onInit { public heroes: IHero[]; constructor(private heroService: HeroService) { } ngonInit() { this.heroService.getHeroes$().subscribe((res: IHero[]) => { this.heroes = res; }); } addAge(heroId: number) { this.heroService.updateHeroAge(heroId, this.heroes[heroId].age + 1); } addHero() { this.heroService.addHeroes({name: 'Afrodita', age: 23}); }}最后我们改变
app.component.html:
App.component data:<hr><h2>This is Angular component: </h2><app-ng-hero></app-ng-hero><hr><!-- With bidirectional data binding--><app-react-owc-renderer [heroes$]="heroesObj$"></app-react-owc-renderer><hr>
一切都应该工作。如果您有任何问题,请随时提问。
您可以在GitHub上找到此解决方案的完整存储库。
如果您需要演示,请点击雇用。



