0

I have a status box which is it’s own component. On my app there are multiple instances of this status box component as you can see from the image below:

enter image description here

The details of each status box are stored in the browsers local storage and are created when a form is submitted with the passed in form values being saved to the browsers local storage and displayed on the status box component.

The details of each separate status box component instance are treated as a map data structure. When the app.component first initialises, a map is created for each of the details stored in the browsers local storage and they are pushed onto an array called myConfigs:

export class AppComponent implements OnInit, OnDestroy
{
  myConfigs: any[] = [];
  localStorageKeys: string[] = ['Test']
  constructor() {}
    
  ngOnInit(): void 
  {
    for (var key of this.localStorageKeys)
      for(let i = 1; i < 5; i++)
        if (localStorage.getItem(key+i) != null)
          this.myConfigs.push(new Map(JSON.parse(localStorage.getItem(key+i)!)))
        else
          console.log(key+i + ' currently not available');
  }
}

Then in the app.component template we loop through each map item in the myConfigs array as a single config, then pass that to a directive (appDynamicStatusBox).

<app-header></app-header>
<div class='status-box-container'>
    <ng-container *ngFor="let config of myConfigs; let i=index" appDynamicStatusBox [config]="config"></ng-container>
</div>

In the directive we create an instance of each status box component. The config item from the myConfigs is an @Input in this directive, and from there we can create each status box component via a resolveComponentFactory then set each value in that component instance with the passed in config variable:

@Directive({selector: '[appDynamicStatusBox]'})

export class DynamicStatusBoxDirective implements OnInit
{
  @Input() config: any;
  componentRef!: ComponentRef<StatusBoxComponent>;

  constructor(private resolver: ComponentFactoryResolver, public viewContainerRef: ViewContainerRef) {}

  ngOnInit(): void 
  {
    const factory = this.resolver.resolveComponentFactory(StatusBoxComponent); // Retrieves the factory object that creates a component of the given type
    this.viewContainerRef.clear(); // clears the previous view
    this.componentRef = this.viewContainerRef.createComponent(factory); // Instantiates a single component of type StatusBoxComponent and inserts its host view into this container
    this.componentRef.instance.name = this.config.get('name')
    this.componentRef.instance.deviceId = this.config.get('device')
    this.componentRef.instance.cpuTemp = this.config.get('cpuTemp')
    this.componentRef.instance.diskUsage = this.config.get('diskSpace')
    this.componentRef.instance.systemUptime = this.config.get('uptime')
    this.componentRef.instance.systemTime = this.config.get('time')
    this.componentRef.instance.number = this.config.get('num')
    this.componentRef.instance.device = this.config.get('device')
    this.componentRef.instance.value = this.config.get('value')
  }
}

What I would like to do is to be able to retrieve the this.componentRef.instance.deviceId which is the key stored in the local storage that holds the data for a particular status box component and be able to press the delete button to delete the local storage and thus delete the component instance.

But I’m not sure how I could get the this.componentRef.instance.deviceId value by clicking / selecting the red delete button of the particular status box component.

If anyone could help, it would be appreciated.

SneakyShrike
  • 723
  • 1
  • 10
  • 31
  • The deviceId is an @input on the actual component I guess..? So if the button is part of the component, that id should be in scope… right..? – MikeOne Sep 03 '21 at 12:34

1 Answers1

0

I would do like this :

  • Create a StatusService
  • Inject the StatusService in each component when created (take care to bind the injectors of the parent and of the children)
  • Declare a
private deleteSubject = new Subject<boolean>();
public delete$ = this.deleteSubject.asObservable()

public delete(checkboxId: string){
    this.deleteSubject.next(checkboxId);
}
  • From the component you just have to call delete
  • From the parent, you just have to listen to delete$ to know which checkbox to delete.

To go further, you can entirely manage the checkboxes in the service, and make you parent component a lot more dumb :)


Other way is to create an Output() on your directive. And bind this to the exact same Output() in your Checkbox

this.componentRef.instance.delete = this.delete

marcant0
  • 189
  • 8
  • I'm sort of new to angular, so there's a few things I don't understand. I'm not really aware if I'm using a parent child component relationship. From my understanding, I simply have multiple instances of the same component created via a directive. I'm also not sure what the $ means in your code example for a Service. I can sort of see how you're logic works but i'd need some more code examples to fully grasp this idea as I'm not really sure what do to. Thanks. – SneakyShrike Sep 10 '21 at 13:11