Refresh a Select Dropdown After Saving Modal Form

98 views Asked by At

I' ve got a modal form "AddProductComponent" which is called inside "AddServiceRecordsComponent".

export class AddProductComponent implements OnInit {

id!: string;
isAddMode: boolean = false;

constructor(private fb: FormBuilder, private productService: ProductService,
            private route: ActivatedRoute, private alertify: AlertifyService,
            private router: Router, private activeModal: NgbActiveModal) {
}

productForm = this.fb.group({
    name: ['', Validators.required]
});

ngOnInit(): void {
    this.id = this.route.snapshot.params['id'];
    this.isAddMode = !this.id;
    if (!this.isAddMode) {
        this.productService.getById(this.id).subscribe(product => {
            this.productForm.patchValue(product);
        })
    }
}

save() {
    const product: any = {};
    Object.assign(product, this.productForm.value);
    this.id = this.route.snapshot.params['id'];
    this.isAddMode = !this.id;
    if (!this.isAddMode) {
        this.productService.updateProduct(this.id, product).subscribe(product => {
                this.router.navigate(["products"]);               
        });
    } else {
        this.productService.saveProduct(product).subscribe(product => {
            if (product != null) {
                 this.activeModal.close(); 
                // I need to call a refresh products in the select box of AddRecordComponent                 
            } else {
                // etc....
            }
        });
    }
}

}

This is my main form component which is calling modal form.

export class AddServiceRecordsComponent implements OnInit {

isAddMode: boolean = false;

id!: string;
serviceRecord!: ServiceRecord;
clients: Client[] = [];
products: Product[] = [];
brands: Brand[] = [];
deliveryTypes: Delivery[] = [];
productStatusses: ProductStatus[] = [];
serviceRecordForm: FormGroup;
serviceProcess!: ServiceProcess;

constructor(private fb: FormBuilder, private serviceRecordService: ServiceRecordService,
            private clientService: ClientService, private productService: ProductService,
            private deliveryService: DeliveryService, private brandService: BrandService,
            private productStatusService: ProductStatusService, private alertify: AlertifyService,
            private route: ActivatedRoute, private router: Router, private modalService: NgbModal) {

    this.serviceRecordForm = this.fb.group({
        client: [],
        serviceRecordItem: this.fb.group({
            productStatus: [''],
            password: [''],
            hasBackup: [''],
            delivery: [''],
            product: [''],
            brand: [''],
            serialNumber: [''],
            defectDetail: [''],
        }),
        accessory: ['']
    });
}

ngOnInit(): void {

    this.id = this.route.snapshot.params['id'];
    this.isAddMode = !this.id;
    if (!this.isAddMode) {
        this.serviceRecordService.getById(this.id).subscribe(serviceRecord => {
            this.serviceRecordForm.patchValue(serviceRecord);
        })
    }

    this.clientService.getClients().subscribe(clients => {
        this.clients = clients;
    })

    this.productService.getProducts().subscribe(products => {
        this.products = products;
        products.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
    })

    this.brandService.getBrands().subscribe(brands => {
        this.brands = brands;
        brands.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
    })

    this.deliveryService.getDeliveries().subscribe(deliveries => {
        this.deliveryTypes = deliveries;
    })
    this.productStatusService.getProductStatuss().subscribe(statusses => {
        this.productStatusses = statusses;
    })
}

save() {
    const serviceRecord: any = {};
    Object.assign(serviceRecord, this.serviceRecordForm.value);
    this.id = this.route.snapshot.params['id'];
    this.isAddMode = !this.id;
    if (!this.isAddMode) {
        this.serviceRecordService.getById(this.id).subscribe(serviceRecord2 => {
            this.serviceProcess = serviceRecord2.serviceProcess;
            serviceRecord.serviceProcess = this.serviceProcess;
            this.serviceRecordService.updateServiceRecordService(this.id, serviceRecord).subscribe(() => {
                this.router.navigate(["service-records"]);
            });
        })
    } else {
        this.serviceRecordService.saveServiceRecordService(serviceRecord).subscribe(() => {
                parent.location.reload();
        });
    }
}

// Some helper methods

openModalAddProduct() {
    const modalRef = this.modalService.open(AddProductComponent);
}

openModalAddBrand() {
    const modalRef = this.modalService.open(AddBrandsComponent);
}

}

openModal methods calling another component and htlm has a code snippet like this to open this modal.

<div class="row">
    <div class="col-md-10">
        <select class="form-select" formControlName="product" id="product"
                [compareWith]="compareObjectProduct">
            <option [ngValue]="product" *ngFor="let product of products">
                {{product.name}}</option>
        </select>
    </div>
    <div class="col-md-2">
        <button type="button" class="btn btn-outline-danger" (click)="openModalAddProduct()">+</button>
    </div>
</div>

Now what i want to do is close that modal after saving product (done) and refresh product dropdown options without refreshing whole page.

1

There are 1 answers

2
traynor On BEST ANSWER

You need to refresh products variable, and then the list will populate itself. In short, you need to share data between components via productService.

You could use observables and store products in the service and share them via BehaviorSubject, and then subscribe from the components, but I think it would require a lot of refactoring, because I guess productService methods are mostly HTTP calls...

A quick-and-dirty way that doesn't require much refactoring could be to add an event emitter in the productService, subscribe to it from the AddServiceRecordsComponent, and then after you save the product in the service, emit from there back to the component to signal to fetch fresh products (moved to a separate method).

Try this:

productService:

@Output() updateProducts = new EventEmitter<any>();


saveProduct() {

    //... save the product

    // emit to component to get new products
    this.updateProducts.emit();
}

AddServiceRecordsComponent:

ngOnInit() {
  
    // move to the method
    this.getProducts();

    // subscribe to the event
    this.productService.updateProducts.subscribe((res:any) => {
        
        this.getProducts();
    });

}


getProducts() {

   this.productService.getProducts().subscribe(products => {
        this.products = products;
        products.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
    })

}

Observable approach:

productService:

// produts shared as observable
products = new BehaviorSubject<Product[]>([null]);
products$ = this.products.asObservable();


// assign products
// probably from this.getProducts
setProducts() {
    this.getProducts().subscribe(products => {

        this.products.next(products);
    });
}


// update products from saveProduct
saveProduct() {

    //... save the product

    // update products
    this.setProducts();
}

AddServiceRecordsComponent:

this.productService.products$.subscribe(products => {
    
    this.products = products;
});

edit

or, as Zlatko pointed out, if you're using ng-bootstrap, you can utilize result promise: when the promise resolves, call separate method which will update products:

AddServiceRecordsComponent:

openModalAddProduct() {

    const modalRef = this.modalService.open(AddProductComponent);

    modalRef.result.then(result => this.getProducts() );
}




getProducts() {

   this.productService.getProducts().subscribe(products => {
        this.products = products;
    });

}