Inversifyjs: Complex contextual resolution (multiple tags/tag and name)

Created on 16 Jul 2019  路  4Comments  路  Source: inversify/InversifyJS

In the system I'm building, we have ViewControllers and ViewControllerComponents. Each ViewController type has a name, like form_view, map_view, etc. Each ViewController has specific ViewControllerComponents that correspond to it, like TextArea and Numeric for form_view, and Point and Polygon for map_view.

Expected Behavior

ViewControllers are easy:

context.bind(VIEW_TYPES.ViewController).to(FormViewController).whenTargetNamed("form_view")

And then for the factory I do:

bind<ViewControllerFactory<any>>(VIEW_TYPES.ViewControllerFactory).toFactory<View<any>>(
    context => name => context.container.getNamed(VIEW_TYPES.ViewController, name),
);

Now what I want to do is the following for ViewControllerComponents:

context.bind(VIEW_TYPES.ViewControllerComponent).to(TextArea).whenTargetNamed("text_area").whenTargetTagged("view_type", "form_view")
...
bind<ViewControllerComponentFactory<any>>(VIEW_TYPES.ViewControllerComponentFactory).toFactory<ViewComponent<any>>(
    context => view_type => component_name => context.container.getMultiTagged(VIEW_TYPES.ViewControllerComponent,{[TAGS.NAME_TAG]: component_name, "view_type": view_name})
);

There are two problems: the first is that you can't chain bindingToSyntax (so .whenTargetNamed().whenTargetTagged() won't work). I solved this with the following complex constraint:

    const constraint: interfaces.ConstraintFunction =  (request: interfaces.Request | null) =>
        request !== null && request.target !== null && (
            request.target.matchesTag("view_type")(view_type)
        ) && (
            names.map(name => request.target.matchesNamedTag(name)).some(i=>i) // check that any array element is truthy
        );

But I wasn't able to figure out how to craft a request that can satisfy this request.

Current Behavior

I dug deeper here and found that inside of the plan function in planner.ts, a Target is created, but you're only allowed to specify a single key for its Metadata (despite Metadata being an array).

Possible Solution

If we were somehow allowed to append to this array of Metadatas, then what I'm trying to do would be possible.

Am I missing something, or approaching this the wrong way? There is still a lot about Inversify that I haven't learned yet.

Most helpful comment

I also want getMultiTagged, this is a great feature, thank you for your contribution. But since this is not merged, can you give me some advice on how to implement a workaround? I don't want to create a fork

All 4 comments

The root Request from Container getNamed/getAllNamed/getTagged/getAllTagged allows specifying a single tag metadata entry, whereas dependency requests have the tag metadata provided by the named decorator and the tagged decorator and multiple metadata can be created. The metadata entries in Target.metadata have to have unique keys so you can only supply one name but if you wanted multiple 'names' supply the names as an array for the metadata value.

constructor( @tagged(multiNameTag,['One','Two']) public namedDependeny: INamedDependency){}

const oneOfNamedConstraintFactory=function(names:string[]){
            return (request:interfaces.Request)=>{
                let matched=false;
                const customTags=request.target.getCustomTags();

                if(customTags){
                    const multiNamedTag=customTags.find(t=>t.key===multiNameTag);
                    if(multiNamedTag){
                        matched= (multiNamedTag.value as string[]).some(n=>names.some(nm=>{
                            return n===nm
                        }))
                    }
                }
                return matched;
            }
        }

container.bind(NamedDependency).toSelf().when(oneOfNamedConstraintFactory(['Zero','Two']))

Thanks for getting back to me! I know I can do @tagged(multiNameTag,['One','Two']), but what I really want is getMultiTagged, which is what it seems you're writing in #1134. I'm excited for that to be done! Thank you :)

I also want getMultiTagged, this is a great feature, thank you for your contribution. But since this is not merged, can you give me some advice on how to implement a workaround? I don't want to create a fork

@JSilversun It has been six months since I looked at the source code and I do not remember how it functions ! If I get a chance I will have a look but it might be a few days.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rashtao picture rashtao  路  3Comments

khorvat picture khorvat  路  5Comments

matthewjh picture matthewjh  路  3Comments

remojansen picture remojansen  路  4Comments

remojansen picture remojansen  路  4Comments