Closed eXaminator closed 8 years ago
I just thought about how "optional" parents could be handled (like require
in angular 1 allows) and came to the conclusion that I could drop the check on $injector
if there is a service of the same name and just do "components first". This would allow to manually add a service/value/constant that uses the component class as name which should then be used as fallback wenn a component requires another component but doesn't find it.
Example: I could provide an empty object as value for Parent
and if I have a Child
that requires a Parent
to be present but doesn't have one it would instead receive the empty Object as fallback. So the user could supply whatever fallback he would like.
Of course this doesn't work on a "per component" basis, but I'd say this could be pretty much how angular 2 would handle it.
What do you say? @timkindberg
Talked with @MikeRyan52 about this.
That's actually a CommonJS issue. I think ES6 has a notion of hot modules, which means the cyclic dependency would resolve naturally Though I understand the issue I think Angular 2 has a futureReference helper somewhere Look for forwardRef https://angular.io/docs/ts/latest/api/core/forwardRef-function.html
If it works, great! If not, see if it would be too hard to do a lightweight forwardRef implementation. If it's too crazy then we can limit this to transclusion scenarios only for now and deal with non-transcluded in the future.
Again thanks!!!!
Also let's squash all the commits before we merge it. Make the commit against feat(Inject): find and inject parent controllers as locals
per your comment: https://github.com/ngUpgraders/ng-forward/pull/112#issuecomment-162354595
I don't think we need to provide a way to provide a fallback. I'm not sure Angular 2 has that beyond what you can already do with using provide
to set up a service/component of the same name so it clobbers the original.
I downloaded the real Angular 2 so I could see how it behaves in various scenarios, here are the results:
null
. So a check is necessary if(tabs){}
. My constructor of the child looks like this: constructor(@Optional() tabs: Tabs) {}
.constructor(@Inject(forwardRef(() => Tabs)) tabs) {}
: successconstructor(@Optional() @Inject(forwardRef(() => Tabs)) tabs) {}
: successSo again I'm not sure how much farther you'd like to take this. What you've done so far has value and can stand on it's own if you'd like to just wrap it up and get this merged. But there's all the details if you want to keep going :)
I imagine we could make our @Optional decorator that just adds a flag to the class; would be pretty simple. Then we could use it like so @Inject(@Optional() Parent)
. You'd have to reflect that it is optional somehow in the inject string. That might be weird. I'm wondering if we should reconsider using require
instead of climbing the DOM looking for controllers... we could use the componentHooks.extendDDO method to add ddo.require
as needed. I'm not sure...
This one I'm not so sure about how to implement. @Inject would need to be extended to allow for function params. Those function params would still need to be able to be decorated by @Optional. This one seems like a larger change to me... like we might have to alter our injection code to allow for just-in-time resolution of the Type.
Sorry for the long post :/
I'll have a look at the refactorings tonight.
As for the other matters:
I have an idea for this: We could have @Inject()
check if it got a function as injectable and if it does, keep it intact in the $inject
array. We could then decorate (as in 'angular.js decorator') the $injector service and make it resolve functions before it runs its methods. We'd also have to update code where we manually work with the injects (like my above code). This is a very rough idea, so I'm not sure if that would actually work. We could limit forwardRef
to components and only update the above code, but I think that would be rather limiting.
I could give it a try if you'd like.
This one is hard. Your @Optional
proposal would only work in TypeScript, but as it is discussed in https://github.com/angular/angular/issues/5449 I might have to live with the thought that I have to either get rid of ESnext altogether (and work with ES5 syntax) or I upgrade to typescript (which is probably what I'll do). Never the less I'd rather not implement that right now as I don't feel I have enough knowledge about typescript right now to implement a completely new parameter decorator.
So I fixed the location where I add the callback, but I got into something of a dead end regarding the forwardRef as it wasn't as easy as I hoped it would be. @Inject
does more then I first expected which makes this approach harder. I'll think about it a while longer and see if I can come up with something.
Though I wonder if it's worth it if @Inject
might be changed to an attribute decorator anyway.
But another thought: Maybe we should start searching on the same element as long as we are not requesting the same instance? Do you know how angular 2 handles this?
I recommend cloning the ng2-play repo so you can try some things out. It's helped me quite a bit. If you don't have time I can check on that for you later.
I tried putting two directives (not components) on the same div and have one try to inject the other. It got the unknown provider error. Is this what you were wondering about?
<div tabs tab></div>
Also if I put one directive on a parent element it didn't work either (with the nested directive asking for the one on the parent):
<div tabs>
<div tab></div>
</div>
Mh, so I guess it's not possible to require directives on the same element at all? That makes a certain pattern I was thinking of impossible, but I guess there are other ways. I will check out the repo you mentioned, but I don't think I'll get to it any time soon.
And as the whole inject API might change I'd rather not invest more time right now into this matter. So maybe it should be merged. I wasn't able to find a simple solution for forwardRef anyway. I'll see if I can squash the commits tonight.
Yeah that's how it appears... kind of weird. But Pascal is saying that its possible in Angular 2 and the default in 2 is to look on the current element first and then travel up the DOM. He said there is another decorator (@SkipSelf) that makes DI start on the parent, but we don't need to do that just yet.
How about this: If you squash it I'll merge it
We'll deal with the other stuff later.
Oh we also need updates to the API.md file. Which I could do if you don't have time, but would love if you did them :)
Lastly, maybe we should just allow the same element directive injection, since Pascal is saying it is supposed to work. So can you start on the current element and write a test for that? If you just wanna be done I totally understand.
Ok, I changed the logic to start searching on the current element and updated the API docs. Please review and merge if everything is ok! :)
Looks good thanks @eXaminator !!!
This implements #98, but it only works for transcluded children. Direct children can't retrieve their parents this way because you get a circular dependency between the the parent (which needs the child for its
directives
option) and the child (which needs its parent for the@Inject
decorator).