-1

I have a interface export interface Tree {}

The base class in implements this interface:

export class TreeBase implements Tree {}

There are some concrete classes extends the TreeBase:

export class TreeLayers extends TreeBase {}

export class TreeSearch extends TreeBase {}

Inside TreeComponent I try to inject type Tree, so component should't know about concrete impementation, just abstraction private tree: Tree.

export class TreeComponent implements OnInit, OnDestroy {
   constructor(private tree: Tree) {}
}

Why I get this error:

Can't resolve all parameters for TreeComponent in src/app/components/tree/tree.component.ts: (?)

I provide concrete class using this:

  providers: [
    {
      provide: Tree,
      useClass: TreeLayers,
      deps: [MapLibraryService],
    },
  ],
  • 3
    check this link: https://stackoverflow.com/questions/37002522/is-it-possible-to-inject-interface-with-angular2 – JustLearning Feb 01 '21 at 18:40
  • Thank you, so if I got this, I mus create token string instead concrete interface –  Feb 01 '21 at 18:46
  • And if you got me right, component `TreeComponent` is universal. So it renders tree. There is a problem what if I need to display checkbox near each node if I pass Tree as `TreeLayers` realization? How to do that? Depends concrete implementaton to show DOM element in default tree as checkboxes? –  Feb 01 '21 at 18:48
  • 1
    not sure why you want to inject the tree then you should be rather nesting your tree component inside which component needs the tree funcitonality, use input, outpu parameters to manupilate the data or services for inter component communication – JustLearning Feb 01 '21 at 18:50
  • Could you share an exmaple, I did not get you –  Feb 01 '21 at 18:52
  • 1
    first check component communication on angular site – JustLearning Feb 01 '21 at 18:53
  • 1
    will post an example in 30 min ;) – JustLearning Feb 01 '21 at 18:53
  • Thank you, I know about communication between components. Problem is there are lot of cases when I need to show, hide checkboxes in tree nodes, or case when I need input nearby or something else. So I think it is not good to create a lot of `@Input() `parameters to manage all cases. Or pass only one as @Input() `type: NodeType`. And having several cases it pollutes the component, because for example when I work with concrete Tree type as TreeLayers I have to manage checkbox. So component should have `checkCheckbox() { this.treeService.checkCheckbox()}` and the others methods for other cases. –  Feb 01 '21 at 18:55
  • So depends concrete implementation passed to component the component should render specific DOM and be able contsol this through component. –  Feb 01 '21 at 18:59
  • I think `export class TreeBase implements Tree {}` should contain `public treeType: TreeType`. Depends this I can render additional DOM element in TreeComponent, getting this type from service. Right? But I dont want to pollute component by several methods for different types of tree. –  Feb 01 '21 at 19:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228120/discussion-between-justlearning-and-asad). – JustLearning Feb 01 '21 at 19:23
  • this is where services can help you i think, so in a service you can create an observable to hepl you render your viewmodel, and in the tree component you subscribe to the observable – JustLearning Feb 01 '21 at 19:24

1 Answers1

0

I think the following implementation can work for you. Define a Model for the tree . You can get away with a just having a TreeNodeModel ->

export class TreeNodeModel{
   public Id:number; 
   public Disabled:boolean;
   public Checked:boolean // keeps the state
   public Text:string;   // optional description or input for description
   public Title: string; // title of the node for display
    //  collection of all the child nodes and their children  etc
    public Nodes: TreeNodeModel[]=[];
}

So you can further complicate the model depending on your requirement:

Then create a TreeNodeComponent and as an Input pass it an empty TreeModel or populated one (depending on your requirements):

    @component({selector:'app-tree-node',templateUrl:'tree.component.html'})
    export class TreeComponent{
   constructor(private treeStateService: TreeStateService){}
    
    @Input() Nodes: TreeNodeModel[]=[];

    public NodeToggled(node: TreeNodeModel){
// notify any other component you need that node is selected
    if(node.Checked){
    this.treeStateService.notifyNodeChecked(node);
}
}
    }

Then your tree component simply needs to display the checkboxes and any other relevant display/input elements:

<div *ngFor="let node of Model?.Nodes">
 <input type="checkbox" [(ngModel)]="node.Checked" [disabled]=node.Disabled>
<label>{{node.Title}}</label>
<!--Display the child checkboxes-->
<app-tree-node [Nodes]="node?.Nodes"></app-tree-node>
</div>

So now you are able to display all your parent nodes and their children. You can further more create a tree.state.service

@Injectable({provideIn:'root'})
export class TreeStateService{
 private nodeCheked = new Subject<TreeNodeModel>();
public nodeChecked$ = this.nodeChecked.asObservable();

public notifyNodeChecked(node: TreeNodeModel){
 this.nodeChecked.next(node);
}
}

Then in which ever component that you host the tree pass in the nodes and subscribe to the service:

@Component({selector:'app-parent'})
export class ParentComponent implement OnInit{
 constructor(private treeStateService: TreeStateService){}

ngOnInit(){
this.treeStateService.nodeChecked$.subscribe((selectedNode)=>{
 // do what you need with the node
});
}
}

Please note i did not compile this code. Also as described in the angular site, unsubscribe from non http subscriptions in ngOnDestroy .

Dharman
  • 30,962
  • 25
  • 85
  • 135
JustLearning
  • 3,164
  • 3
  • 35
  • 52
  • Thank you I will elaborate your answer –  Feb 02 '21 at 10:51
  • The question is how to manage if I dont need checkbox but need input. And where is logic to handle input or checkbox? –  Feb 02 '21 at 10:56
  • the answer was supposed to give you an idea of how you can structure your components and use component interaction to handle relatively complex scenarios, thus avoiding injection of of the Tree interface, which i think in your case is the wrong approach to solve your problem – JustLearning Feb 02 '21 at 11:09
  • we can continue in the chat to try figure out how to solve your problem, join the one created on the question comments – JustLearning Feb 02 '21 at 11:10
  • Use the TreeNodeModel to define whether to show input or checkboxes, per node. If you need it to be per whole tree, you can pass a boolean to the tree-node-component. The model you pass in the tree node component should be update via ngModel so in the parent comp you can have access to the updated model at any time – JustLearning Feb 02 '21 at 11:20
  • Thank you, but where to place logic for click checkbox? –  Feb 02 '21 at 11:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228151/discussion-between-justlearning-and-asad). – JustLearning Feb 02 '21 at 11:33