I don't know Angular1 well therefore I can't tell what require
does exactly or for what purposes it is used.
To the bullets mentioned in your question:
- Normally you use template binding to wire up parent and children
parent template
<child [childInput]="parentValue" (childOutput)="doSomethingInParent()">
- Services are singletons per provider. The same provider will always return the same instance but you can provide the same service multiple times and then it's not a real singleton anymore. Therefore, where you provide a service defines the scope of where it is treated as singleton. When you provide it at a component, this component instance and all child components will get the same instance (as long as not a child provides the same type).
This DI behavior prevents conflicts like you mentioned in your question.
- template variables are rather used to refer to siblings
<child1 [child1Input]="child2.child2Prop"
(child1Output)="child2doSomethingInChild2()">
<child2 #child2></child2>
- If you know the type of the parent component, you can require it to be injected to the constructor of the child component
constructor(@Host() private parent:ParentComponent) {}
This might especially be handy in recursive components (like tree) where it's know what the parent is. In this case additional decorators might be necessary
constructor(@Optional() @SkipSelf() @Host() private parent:ParentComponent) {}
Where
@Optional()
is for the root component to avoid an exception because there is no parent of the same type to be injected
@SkipSelf()
avoids the component itself to get injected when it's the same type as the parent it actually wants to get injected. DI always starts on the component itself to look up providers.
See also Inject parent component of the same type as child component