Skip to content
This repository was archived by the owner on Jul 30, 2018. It is now read-only.

Support Injector instances in Registry and add inject decorator #668

Merged
merged 8 commits into from
Sep 12, 2017

Conversation

agubler
Copy link
Member

@agubler agubler commented Sep 10, 2017

Type: feature

The following has been addressed in the PR:

  • There is a related issue
  • All code matches the style guide
  • Unit or Functional tests are included in the PR

Description:

Adds inject decorator to support side loading properties from a Injector instance registered in the Registry.

Includes:

  • Adds additional API for define, get and has that deal with Injector instances (defineInjector, getInjector, hasInjector)
  • Add inject decorator that registers a function that will be called with the injector instances context and the widget properties in the beforeProperties phase.
  • Updates Container & Themeable to use mixin pattern for adding inject decorator
  • Removes BaseInjector that extends WidgetBase the Injector HOC
  • Renames WidgetBase.getRegistries() to WidgetBase.registries

Resolves #648

@dylans dylans added this to the 2017.09 milestone Sep 10, 2017
src/Injector.ts Outdated
private _context: T;

constructor(context: T = <T> {}) {
constructor(context: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the reason/motivation for the context being untyped? Not challenging, just wondering.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a challenge with the typing, I'll try it again and let you know (can't remember off the top of my head).

Also wasn't sure what the benefit really was, seen as it's injected at run time there's it didn't really give much to the consumer. The important typing is really on the getProperties function that gets passed the context, which the user can type as needed.

Copy link
Member Author

@agubler agubler Sep 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was imagining the challenge it seems, updated to accept a generic type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do get the point about the value of it...

@@ -29,31 +30,55 @@ export interface RegistryEvents extends BaseEventedEvents {
/**
* Widget Registry Interface
*/
export interface Registry {
export interface RegistryInterface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have traditionally not mentioned Interface in our interfaces. Why the change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it have something to do with name merging? Could this be a use case for an abstract class and having an AbstractRegistry as an export?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason was, that without a different name for the interface TS didn't actually error if the class didn't implement it! We've used a suffix of Interface in some of the interfaces exported from interfaces.d.ts which was the motivation.

I don't think Abstract works because the registry isn't actually abstract?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Thanks.

getProperties: GetProperties;
}

export function inject({ name, getProperties }: InjectConfig) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some basic JSDoc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops yup!

src/Registry.ts Outdated
public getInjector<T extends Injector>(label: RegistryLabel): T | null {
if (!this.hasInjector(label)) {
if (this.has(label)) {
console.warn(`Unable to find injector '${label.toString()}', did you mean to get a widget?`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not convinced we need these warns, because the api is separate and returns discreet types (that extend Injector), the user thus shouldn't be able to get this wrong at design time.

export function inject({ name, getProperties }: InjectConfig) {
return handleDecorator((target, propertyKey) => {
beforeProperties(function(this: WidgetBase, properties: any) {
const context = this.registries.getInjector(name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be called injector, over context?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup good shout

* used to map the injected properties.
*/
export interface GetProperties<T = any> {
(context: any, properties: T): T;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this context?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the context from the Injector

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is a context? can we get a better name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am receptive to better names.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't participate in suggesting a name without knowing what it is 😄

Copy link
Member Author

@agubler agubler Sep 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want it to be payload, I don't have any strong attachment to context or objections to payload - they both work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason i don't like context is the baggage that comes from react with it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup I understand that. Let me update that for you - I think both are okay and if using context could lead to confusing that is it enough reason to change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value? Since you are injecting a value of some sort that can be any. Do we actually expect it to be any or is it an object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye it can be a any, just as the payload of an Injector can be.

return class extends WidgetBase<any> {
@inject({ name, getProperties })
class WidgetContainer extends WidgetBase<Partial<W['properties']>> {
public __setProperties__(properties: Partial<W['properties']>): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have we thought how we can better support this in the future? this is the equivalent of shouldComponentUpdate set to true in react?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have thought about it, shall raise an enhancement issue - for now this is the only way I could support it.

@@ -706,7 +706,7 @@ export class WidgetBase<P = WidgetProperties, C extends DNode = DNode> extends E
let child: WidgetBaseInterface<WidgetProperties>;

if (!isWidgetBaseConstructor(widgetConstructor)) {
const item = this.getRegistries().get(widgetConstructor);
const item = this.registries.get(widgetConstructor);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the only place the registries are accessed? just noticed that we've renamed it back now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think everywhere else actually accesses the private (_registries) in WidgetBase, I'll update this to do that.

@agubler agubler merged commit 6779ca9 into dojo:master Sep 12, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants