0

I want to add and remove component dynamically on selecting the checkbox . I'm able to add the component and remove it on clicking close button . But if i try to change the component selection from the checkbox , the previously selected component still remains, it is not getting deleted.

Here is the working demo of it Demo

app.component.ts

  import { Component,ViewChild,ViewContainerRef ,ComponentFactoryResolver , AfterContentInit,OnInit,ComponentRef} from '@angular/core';
import { BoxOneComponent } from './boxOne.component';
import { BoxTwoComponent } from './boxTwo.component';
import { BoxThreeComponent } from './boxThree.component';
import { BoxFourComponent } from './boxFour.component';
import { SharedService } from './shared.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent {

  @ViewChild('parent',{read:ViewContainerRef}) parent;
  @ViewChild('widget') widget ;
  public resolve;
  public totalBoxLength:number = 0;

  constructor(private cfr:ComponentFactoryResolver,private sharedService : SharedService){

  }


  passedValue(data){
    data.forEach(element => {
      this.addWidget(element.name,element.length)
    });
  }

  addWidget(val,boxLength){
    this.totalBoxLength += boxLength;
    if(this.totalBoxLength<=8){
        let data;
        switch(val){
          case "BoxOneComponent":
            data = BoxOneComponent;
            break;
          case "BoxTwoComponent":
            data = BoxTwoComponent;
            break;
          case "BoxThreeComponent":
            data = BoxThreeComponent;
            break;
          case "BoxFourComponent":
            data = BoxFourComponent;
            break;
          case "BoxFiveComponent":
            data = BoxFiveComponent;
            break;
          case "BoxSixComponent":
            data = BoxSixComponent;
            break;
          case "BoxSevenComponent":
            data = BoxSevenComponent;
            break;
          case "BoxEightComponent":
            data = BoxEightComponent;
            break;
          case "BoxNineComponent":
            data = BoxNineComponent;
            break;
        }
        this.resolve = this.cfr.resolveComponentFactory(data);
        let cmp: ComponentRef<any> = this.parent.createComponent(this.resolve);
        cmp.instance.boxLength$.subscribe((value)=>this.totalBoxLength -= value)
        cmp.instance.close$.take(1).subscribe(() => cmp.destroy());
    }else{
        this.totalBoxLength -= boxLength;
    }

  }


}

BoxOne.component.ts

    import { Component, Input } from '@angular/core';
import { Subject } from 'rxjs';
import { SharedService } from './shared.service';

@Component({
  selector: 'box-one',
  template: `
    <div>
      <button (click)="close({'name':'BoxOneComponent','length':2})">Close</button>
    </div>
  `,
  styles: [`
    div{
      height:100px;
      width:40%;
      background-color:yellow;
      display:inline-block;
      margin:5px;
    }
    button{
      margin-left:48px;
    }
  `]
})
export class BoxOneComponent  {

  constructor(private sharedServ : SharedService){}

  public close$ = new Subject<void>();
  public boxLength$ = new Subject<number>();

  close(comps){
    this.sharedServ.sendComponentName(comps);
    this.close$.next(null);
    this.boxLength$.next(2);
  }

}

There are another 3 box component like this .

widgetlist.html

<div class="modal fade" bsModal #widgetList="bs-modal" [config]="{backdrop: 'static'}" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-sm widget-width">
        <div class="modal-content widget-content">
            <div class="modal-header widget-header">
                <div class="modal-title">
                    <span class="widget-heading">List of all available widgets</span>
                    <span (click)="hideWidgetList()" class="close-span">Close</span>
                </div>
            </div>
            <div class="modal-body widget-body">
                <div><input type="checkbox" #BoxOneComponent name="Box One" (click)="selectWidget({'name':'BoxOneComponent','length':2})"> Box One </div>
                <div><input type="checkbox" #BoxTwoComponent name="Box Two" (click)="selectWidget({'name':'BoxTwoComponent','length':1})"> Box Two </div>
                <div><input type="checkbox" #BoxThreeComponent name="Box Three" (click)="selectWidget({'name':'BoxThreeComponent','length':1})"> Box Three </div>
                <div><input type="checkbox" #BoxFourComponent name="Box Four" (click)="selectWidget({'name':'BoxFourComponent','length':2})"> Box Four </div>
            </div>
            <div class="modal-footer widget-footer ">
                <button class="cancel-button" (click)="hideWidgetList()">Cancel</button>
                <button class="update-button " (click)="updateWidgetList();hideWidgetList()">Update</button>
            </div>
        </div>
    </div>
</div>

widgetlist.ts

import { Component,ViewChild ,ElementRef , ViewRef , Output , EventEmitter} from '@angular/core';
import { ModalDirective } from 'ngx-bootstrap';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-widget-list',
  templateUrl: './widget-list.component.html',
  styles: [
`
    .close-span{
    cursor: pointer;
    float: right;
    border: 1px solid black;
  }

.widget-body div{
  display:inline-block;
  width:45%;
}

`
  ]
})
export class WidgetList  {

  @ViewChild('widgetList') widgetList : ModalDirective ;
  @ViewChild('BoxOneComponent') boxOne;
  @ViewChild('BoxTwoComponent') boxTwo;
  @ViewChild('BoxThreeComponent') boxThree;
  @ViewChild('BoxFourComponent') boxFour;
  @Output() selected = new EventEmitter();
  constructor( private elemRef : ElementRef,private sharedService:SharedService) { 
    this.sharedService.shareComponentName$.subscribe(val=>{
      this.selectWidget(val);
      this.triggerChecked(val);
    });
  }
  public widgetSelected : Array<string> =[];
  public widgetSelectedName : Array<string> = [];
  public widgetAlreadyInList : Array<any>;

  showWidgetList():void{
    this.widgetList.show();
  }

  hideWidgetList():void{
    this.widgetList.hide();
  }

public pushedArray =[];
  selectWidget(widget){
    let indexOfSelectedWidget = this.widgetSelectedName.indexOf(widget.name);
      if(indexOfSelectedWidget === -1){
          this.widgetSelected.push(widget);
          this.widgetSelectedName.push(widget['name']);
      }else{
          this.widgetSelected.splice(indexOfSelectedWidget,1);
          this.widgetSelectedName.splice(indexOfSelectedWidget,1);
      }      
  }

  updateWidgetList(){
      console.log(this.widgetSelected);
      sessionStorage.setItem("existingWidgets",JSON.stringify(this.widgetSelected))
      this.selected.emit(this.widgetSelected);
  }

  triggerChecked(component){

    switch(component.name){
      case "BoxOneComponent":
        this.boxOne.nativeElement.checked = false;
        break;
      case "BoxTwoComponent":
        this.boxTwo.nativeElement.checked = false;
        break;
      case "BoxThreeComponent":
        this.boxThree.nativeElement.checked = false;
        break;
      case "BoxFourComponent":
        this.boxFour.nativeElement.checked = false;
        break;
    }

  }

}
Sampath1504
  • 131
  • 1
  • 13
  • I think the approach with `*ngFor` and `` (or `NgComponentOutlet` demonstrated in https://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468 results in simpler code. – Günter Zöchbauer Sep 04 '17 at 16:14
  • what does [type] means in that approach? – Sampath1504 Sep 04 '17 at 17:38
  • That's the class of the component. `` or `NgComponentOutlet` create a component of that instance and add it. This allows to have an array of component types and for each entry a component of that type will be added. – Günter Zöchbauer Sep 04 '17 at 17:45
  • Thanks !! and can you please tell me the difference between viewContainerRef and ElementRef ? – Sampath1504 Sep 04 '17 at 17:55
  • `ViewContainerRef` allows you to stamp templates and add components. `ElementRef` allows you to access the DOM element (using `ElementRef.nativeElement`) for example to call methods on it, like `focus()` or `getAttribute()` in a way you would do it in a plain JS project. – Günter Zöchbauer Sep 04 '17 at 17:58
  • Thanks @GünterZöchbauer – Sampath1504 Sep 04 '17 at 18:01
  • Hey @GünterZöchbauer . I have a doubt . Your approach worked for me . – Sampath1504 Sep 09 '17 at 18:54
  • https://stackblitz.com/edit/add-remove-components . Here is the demo of it . I have a close button on each element , if close it , my component is getting deleted , but why i am not able to add the same component again ? – Sampath1504 Sep 09 '17 at 18:55
  • If i close component 1 and try to add it again it's not working . – Sampath1504 Sep 09 '17 at 18:57
  • It would be helpful if you could create a Plunker that demonstrates the issue. Plunker provides a ready-to-use Angular 4 template to start from. – Günter Zöchbauer Sep 10 '17 at 15:01
  • https://stackblitz.com/edit/add-remove-components . Here is the demo of it . – Sampath1504 Sep 10 '17 at 16:31
  • I think , ngOnChange is not firing . – Sampath1504 Sep 10 '17 at 16:32
  • stackblitz looks great (haven't seen it before). "If i close component 1 and try to add it again it's not working" I can't reproduce. I have to uncheck the checkbox and check it again to get the component re-added, but otherwise I don't know what the issue is. – Günter Zöchbauer Sep 10 '17 at 16:48
  • Is there a way to find the index of the component with respective to parent which i am deleting? – Sampath1504 Sep 10 '17 at 17:03
  • I'm not exactly sure what you're after. I'd just manipulate the `tabs` array (add/remove). Remove for example `this.cmpRef.instance.close$.take(1).subscribe(() => this.cmpRef.destroy());`. If you remove an entry from the `tabs` array, Angular will destroy `ParentContainer` and this will call `this.cmpRef.destroy()`. You can pass the index to the component from `*ngFor` like `
    ` and then for example add an '@Output() delete:EventEmitter ...;` that notifies
    – Günter Zöchbauer Sep 10 '17 at 17:10
  • `AppComponent` to remove the item at the emitted index. – Günter Zöchbauer Sep 10 '17 at 17:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/154074/discussion-between-sampath1504-and-gunter-zochbauer). – Sampath1504 Sep 10 '17 at 17:27

0 Answers0