是否可以将差异服务实例传递给类似于React Context的子组件

时间:2019-03-23 03:23:25

标签: angular

例如有一项这项服务

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中做类似的事情?

1 个答案:

答案 0 :(得分:1)

是的,可以这样做。技巧是在将使用该服务的组件的providers数组中声明该服务,而不是在Module中声明该服务。

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

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

在此处查看docs以获得更多信息。

使用一个组件的@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)。

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

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>

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