简体   繁体   English

Angular2依赖项注入:创建对最初未定义属性的引用

[英]Angular2 dependency injection: creating a reference to a property that is initially undefined

I'm trying to use Angular2 dependency injection to get a reference to a "Webgl2RenderingContext", which is undefined until I call the createContext function. 我正在尝试使用Angular2依赖项注入来获取对“ Webgl2RenderingContext”的引用,该引用在我调用createContext函数之前是不确定的。 Is there way to inject the reference into a several different service classes and then set the value after the HTMLCanvasElement is available? 有没有办法将引用注入到几个不同的服务类中,然后在HTMLCanvasElement可用之后设置值?

@Injectable()
export class RenderContext {   
    get context() { return this.render_context; };

    constructor() { };

    createContext(canvas: HTMLCanvasElement) {
        this.render_context = canvas.getContext("webgl2");
    };

    private render_context: WebGL2RenderingContext;
}

let get_context = () => {
    return (render_context: RenderContext) => {
        return render_context.context;
    }
}

export const webgl2 = new OpaqueToken("webgl2");

export const webgl2_providers = [
    RenderContext,
    {
        provide: webgl2,
        useFactory: get_context(),
        deps: [RenderContext]
    }
];

I think this is a good case for Observables, and probably in your case a Subject from RxJS, since it allows sending to multiple observers through one emit call. 我认为这对于Observables来说是一个很好的案例,对您而言,这可能是RxJS的Subject,因为它允许通过一个generate调用发送给多个观察者。 In the below code I use a ReplaySubject because it lets you subscribe to the subject after the context has been sent and still get the most resent value (ie you don't have to make sure all your subscriptions are in place before creating the context, and you don't have to change code on if context has already been created or not). 在下面的代码中,我使用了ReplaySubject,因为它使您可以在发送上下文后订阅主题,并且仍然获得最多的重发值(即,在创建上下文之前,不必确保所有订阅都到位,并且您不必更改是否已创建上下文的代码)。

@Injectable()
export class RenderContext {  
    //import from 'rxjs/ReplaySubject'  Provide Observable functionality to
    //multiple observers, and will replay last-set value so those that subscribe
    //after context is set still get the context.
    private __contextSubject : ReplaySubject = new ReplaySubject(1);

    private render_context: WebGL2RenderingContext;

    constructor() { };

    //not using property getter now since returning Observable, not actual value
    getContext() : Observable {
         return this.__contextSubject;
    };

    //send value to subscribers
    private emitContext() {
        this.__contextSubject.next(this.render_context);
    }

    //after storing context, also emit it to subscribers
    createContext(canvas: HTMLCanvasElement) {
        this.render_context = canvas.getContext("webgl2");
        this.emitContext();
    };

}

Then you would get the context by injecting the RenderContext directly (instead of going through a factory): 然后,您可以通过直接注入RenderContext来获得上下文(而不是通过工厂):

class OtherComponent {
    constructor(renderContext: RenderContext) {
        renderContext.getContext().subscribe(
            (context: WebGL2RenderingContext) => {
                //do what you need to do with the context...
            }
        )
    }
}

One solution I found was creating a ReflectiveInjector as a property of the MainCanvas component, adding a single provider for the initialized value of "WebGL2RenderingContext" after it is created, then creating a child injector with all the other classes that depend on the context: 我发现的一个解决方案是创建一个ReflectiveInjector作为MainCanvas组件的属性,在创建“ WebGL2RenderingContext”的初始化值后为其添加单个提供程序,然后使用依赖于上下文的所有其他类创建一个子注入器:

export class MainCanvas {
    @ViewChild("canvas") canvas_ref: ElementRef;

    private gl: WebGL2RenderingContext;
    private context_injector: ReflectiveInjector;
    private scene_renderer: SceneRenderer;

    constructor(private render_context: RenderContext) {};

    ngAfterViewInit() {
        this.render_context.createContext((<HTMLCanvasElement>this.canvas_ref.nativeElement));
        this.gl = this.render_context.context;

        if (this.gl) {

            let gl_provider = [{ provide: webgl2, useValue: this.gl }];

            this.context_injector = ReflectiveInjector.resolveAndCreate(gl_provider)
                .resolveAndCreateChild([SceneRenderer, cube_providers, shader_providers]);

            this.scene_renderer = this.context_injector.get(SceneRenderer);
        }
    }
};

Edit: It also works without making a child injector: 编辑:它也可以不使用子注入器而工作:

this.context_injector = ReflectiveInjector.resolveAndCreate([gl_provider, SceneRenderer, cube_provider, shader_providers]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM