cyberfly
12/19/2018 - 3:30 AM

Angular checkbox component

import { ReplaySubject } from 'rxjs/index';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { RCUSTATUSES } from 'Helpers/rcustatuses';
import { FormGroup, FormBuilder, FormArray } from '@angular/forms';
import { ModalDirective } from 'ngx-bootstrap/modal/modal.directive';
import { AuthUserService } from 'Services/auth-user.service';
import { finalize, distinctUntilChanged, debounceTime, } from 'rxjs/operators';
import { Component, OnInit, OnChanges, Input, ViewChild, SimpleChanges } from '@angular/core';
import { RelocationChecklistService } from 'AppService/service/relocation-checklist.service';
import { RelocationChecklistStatusService } from 'AppMscCompliance/services/relocation-checklist-status.service';

@Component({
    selector: 'v-relocation-internal-checklist',
    templateUrl: './relocation-internal-checklist.component.html',
    styleUrls: [ './relocation-internal-checklist.component.scss' ],
})
export class RelocationInternalChecklistComponent implements OnInit, OnChanges {

    modalRef: BsModalRef;
    @ViewChild('modal') modal: ModalDirective;

    @Input()
    rcu_case_id: number = null;

    @Input()
    status_name: string = '';

    @Input()
    existing_checklists: any[] = null;

    checklistForm: FormGroup;

    onChanges = new ReplaySubject<SimpleChanges>();

    checklist_items = [
        {
            id: 2,
            name: 'Renting: Stamp certified true copy of tenancy agreement.'
        },
        {
            id: 3,
            name: 'Own premise: Stamp certified true copy of Sales & Purchase Agreement'
        },
        {
            id: 4,
            name: 'Office Space in Sq.Ft'
        },
    ];

    state = {
        can_update: false,
    };

    meta = {
        is_loading: false
    };

    constructor(
        public authUser: AuthUserService,
        private relocationChecklistService: RelocationChecklistService,
        private relocationChecklistStatusService: RelocationChecklistStatusService,
        private fb: FormBuilder
    ) {

    }

    ngOnInit(): void {

        this.checklistForm = this.fb.group({
            checklists: this.buildChecklist()
        });

        // set existing item only when input existing_checklists data is ready

        this.onChanges.subscribe(data => {
            this.setExistingItem(this.existing_checklists);
        });

        // listen for checkbox item changes

        this.itemChanges();

    }

    ngOnChanges(changes: SimpleChanges) {

        if (changes['rcu_case_id'] && this.rcu_case_id) {

            console.log('checklist rcu_case_id', this.rcu_case_id);
        }

        if (changes['status_name'] && this.status_name) {

            // reset the state

            this.state = {
                can_update: false,
            };

            this.enableUpdateChecklist(this.status_name);

        }

        if (changes['existing_checklists'] && this.existing_checklists) {

            console.log('existing_checklists', this.existing_checklists);

            this.onChanges.next(changes);

        }
    }

    // first build of checkbox

    buildChecklist() {

        const controls = this.checklist_items.map(item => this.fb.control(false));

        return this.fb.array(controls);

    }

    // enable update checklist

    private enableUpdateChecklist(status_name: string) {

        if (
            status_name === RCUSTATUSES.IN_PROGRESS
            || status_name === RCUSTATUSES.RESUBMITTED
        ) {
            this.state.can_update = true;
        }

    }

    // getter

    get formData() { return <FormArray> this.checklistForm.get('checklists'); }

    // rebuild checkbox after received pre-checked data from API

    rebuildChecklist(selected_ids: number[]) {

        const controls = [];

        for(let i = 0; i < this.checklist_items.length; i++) {

            let checked = false;

            if (selected_ids.indexOf(this.checklist_items[i].id) !== -1) {
                checked = true;
            }

            const form_state = {
                value: checked,
                disabled: !this.state.can_update
            };

            controls.push(this.fb.control(form_state));
        }

        return this.fb.array(controls);

    }

    // when user check / uncheck item, auto store via API

    itemChanges(): void {

        this.checklistForm.valueChanges
            .pipe(
                debounceTime(400),
                distinctUntilChanged()
            )
            .subscribe(value => {

                const selectedItemIds = this.getSelectedItems();

                console.log(selectedItemIds);

                this.storeChecklist(selectedItemIds);

                this.enableApproveRelocation(selectedItemIds);

            });
    }

    // enable Approve if user checked all the items

    enableApproveRelocation(selectedItemIds) {

        const checklist_item_ids = this.checklist_items.map(function (item) {
            return item.id;
        });

        const item_diff = this.arr_diff(selectedItemIds, checklist_item_ids);

        if (item_diff.length === 0) {
            this.relocationChecklistStatusService.changeStatus(true);
        } else {
            this.relocationChecklistStatusService.changeStatus(false);
        }
    }

    // get latest checked items id

    getSelectedItems() {

        const selectedItemIds = this.checklistForm.value.checklists
            .map((v, i) => v ? this.checklist_items[i].id : null)
            .filter(v => v !== null);

        return selectedItemIds;

    }

    // from API checklists, extract the item id

    getExistingSelectedItemIds(checklist_items) {

        const existing_checklist_item_ids = checklist_items.map(item => item.pivot.checklist_item_id);

        console.log('existing_checklist_item_ids', existing_checklist_item_ids);

        return existing_checklist_item_ids;
    }

    // pre-selected existing checklist

    setExistingItem(existing_checklists) {

        const selected_ids = this.getExistingSelectedItemIds(existing_checklists);

        console.log('selected_ids', selected_ids);

        this.checklistForm.setControl('checklists', this.rebuildChecklist(selected_ids) );

        this.enableApproveRelocation(selected_ids);

    }

    // store latest checklist item

    storeChecklist(items) {

        if (!this.state.can_update) {
            return false;
        }

        this.meta.is_loading = true;

        this.relocationChecklistService.syncItems(this.rcu_case_id, items)
            .pipe(finalize(() => this.meta.is_loading = false ))
            .subscribe(
                res => {
                    console.log(res);
                },
                err => {
                    console.log('err', err);

                });

    }

    arr_diff(a1, a2) {

        const a = [], diff = [];

        for (let i = 0; i < a1.length; i++) {
            a[a1[i]] = true;
        }

        for (let i = 0; i < a2.length; i++) {
            if (a[a2[i]]) {
                delete a[a2[i]];
            } else {
                a[a2[i]] = true;
            }
        }

        for (const k in a) {
            diff.push(k);
        }

        return diff;
    }
}
<div class="panel panel-default">
    <div class="panel-body">
        <form [formGroup]="checklistForm">
            <h5>Relocation Checklist</h5>
            <span>

                <div class="checkbox" formArrayName="checklists" *ngFor="let item of formData.controls; let i = index" >
                    <label>
                        <small>
                            <input type="checkbox" [formControlName]="i" >{{ checklist_items[i].name }}
                        </small>
                    </label>
                </div>

            </span>
        </form>
    </div>
</div>