Search code examples
angularng-modal

Refresh a Select Dropdown After Saving Modal Form


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.


Solution

  • 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;
        });
    
    }