首页 > 解决方案 > 是否可以将不同的服务实例传递给类似于 React Context 的子组件

问题描述

例如有这个服务

class Service {
  constructor(public name:string){}
}

我想将它的不同实例传递给子组件

@Component({
  selector: 'child-component',
  template: `
    <div>{{service.name}}</div>
  `,
})
export class ChildComponent {
  constructor(public service: Service){}
}

@Component({
  selector: 'parent-component',
  template: `
    <child-component></child-component>
    <child-component></child-component>
  `,
})
export class ParentComponent {
  constructor(public service: Service){}
}

在 React 中,它可以通过使用上下文来完成。像这样的东西

const ServiceContext = React.createContext<Service>({} as any);

function ChildComponent(){
    const service = useContext(ServiceContext)

    return <div>{service.name}</div>
}
export function ContextTest(){

    const service1 = new Service("foo")
    const service2 = new Service("bar")

    return <div>
        <ServiceContext.Provider value={service1}>
            <ChildComponent/>
        </ServiceContext.Provider>
        <ServiceContext.Provider value={service2}>
            <ChildComponent/>
        </ServiceContext.Provider>
    </div>

}

是否可以在 Angular 中做类似的事情?

标签: angular

解决方案


是的,这是可以做到的。诀窍是在providers将使用它的组件数组中声明服务,而不是在Module.

当您在该级别“提供”服务时Module,单个实例可用于 DI 中的每个组件Module

但是,如果您在组件级别“提供”它,那么该组件每次都会获得一个全新的服务实例。

请查看此处的文档以获取更多信息。

@Component使用您的组件装饰器之一的基本示例:

@Component({
  selector: 'child-component',
  template: `<div>{{service.name}}</div>`,
  providers:  [Service]
})

对每个需要它自己的实例的组件执行此操作,并再次确保您没有将服务放入Module's providers数组中。

编辑:

如果您希望父组件知道服务(及其值),那么它可以手动创建它们并通过@Input()绑定传递它们。

举一个简单的例子,您的组件将具有如下属性:

@Input() service: Service;

然后在你父母的 HTML 中:

<child-component [service]="new Service('foo')"></child-component>
<child-component [service]="new Service('bar')"></child-component>

或者,在父级上创建字段,例如:

fooService = new Service('foo');
barService = new Service('bar');

然后在 HTML 中:

<child-component [service]="fooService"></child-component>
<child-component [service]="barService"></child-component>

编辑2:

虽然我不相信这可以使用 Angular 的 DI 开箱即用来实现,但我认为可能有一种方法可以实现所需的结果(并且仍然使用构造函数 DI)。

首先,创建一个“配置”类型,例如:

export interface ServiceConfig {
    name: string;
}

现在,像这样修改您的服务:

export class Service {
    private config: ServiceConfig;

    constructor() {
    }

    registerConfig(config: ServiceConfig) {
        this.config = config;
    }

    get name(): string {
        return this.config.name;
    }
}

然后在您的组件中:

@Component({
  selector: 'child-component',
  template: `<div>{{service.name}}</div>`,
  providers: [Service]
})
export class ChildComponent implements OnInit {
   @Input() serviceConfig: ServiceConfig;

   constructor(private service: Service) {
   }

   ngOnInit(): void {
      this.service.registerConfig(this.serviceConfig);
   }
}

最后一件事是将独特的配置连接到父模板中的每个子组件:

<child-component [serviceConfig]="{ name: 'fooService' }"></child-component>
<child-component [serviceConfig]="{ name: 'barService' }"></child-component>

这并不完全是一个优雅的解决方案,但它满足了您(我认为非常奇怪)的要求。


推荐阅读