import {Component, OnInit, Input} from "@angular/core"; import {FormControl, Validators, FormGroup, FormArray} from "@angular/forms"; import {IterableOptions} from "./IterableOptions"; import {get} from 'lodash'; import {Defaults} from "../../constant/defaults.constant"; import {Helper} from "../../helper"; @Component({ selector: 'rss-iterable', templateUrl: './iterable.component.html', styleUrls: ['./iterable.component.scss'] }) export class IterableComponent implements OnInit { @Input() config; @Input() control:FormGroup; @Input() readonly:Boolean; @Input() label:any; @Input() questionGroups:any; @Input() placeholder = Defaults.PLACEHOLDER; @Input() minimumRecordsRequired:boolean = Defaults.REQUIRED; @Input() type:string = Defaults.INPUT_TYPE; @Input() canEdit:boolean = false; @Input() headerKey:string; minimumRecordsRequiredMsg:string = Defaults.MINIMUM_RECORDS_REQUIRED; formGroup:FormGroup; editFormGroup:FormGroup; editIndex:any = null; get iterables():FormArray { return this.control.get(Defaults.ITERABLE_KEY) } constructor() { } ngOnInit() { if (this.config) { const config = new IterableOptions(this.config); Helper.merge(this, config); } if (!this.control) { console.error(Defaults.MISSING_FORM_CONTROL_ERROR_MESSAGE); } else { if (this.minimumRecordsRequired) { this.control.setValidators(Validators.required); } } this.initializeFormGroup(); } /* Actions on the Iterable Responses : Add and Delete */ add() { Helper.showErrors(this.formGroup); if (this.formGroup.status === Defaults.VALID) { this.iterables.insert(0, this.formGroup['controls'][this.questionGroups[0].key]); this.initializeFormGroup(); } } edit(index) { this.editFormGroup = Helper.buildFormGroup(this.questionGroups, { [this.questionGroups[0].key]: this.iterables.at(index).value }); this.editIndex = index; } update(index) { Helper.showErrors(this.editFormGroup); if (this.editFormGroup.status === Defaults.VALID) { this.iterables.at(index).patchValue(this.editFormGroup['controls'][this.questionGroups[0].key].value); this.editIndex = null; this.initializeFormGroup(); } } delete(index) { if (confirm(Defaults.CONFIRMATION_MESSAGE_ON_DELETION)) { this.iterables.removeAt(index); this._validateControl(); } } /* Component specific form group behavior in read, write and edit modes of the form : Do not pass responses here*/ initializeFormGroup() { if (this.questionGroups) { // The formGroup remains the same for new, editable and readonly modes this.formGroup = Helper.buildFormGroup(this.questionGroups); } } /* Helper functions to operate on form groups and controls */ hasResponses() { return !!(this.iterables && this.iterables['controls'] && this.iterables['controls'].length); } getTitle(index) { if (this.hasResponses()) { if (this.headerKey) return get(this.iterables.at(index).value, this.headerKey); const primaryAttribute = this._getPrimaryResponse(this.iterables.at(index).value); return this.iterables.at(index).value[primaryAttribute].label; } return ''; } _getPrimaryResponse(obj:{}) { return Object.keys(obj)[0]; } /* Validations */ _validateControl() { if (this.minimumRecordsRequired && this.control.touched && !this.hasResponses()) { this.control.setErrors(Validators.min); } } displayEmptyResponses() { return Defaults.EMPTY_RESPONSE_MSG; } displayRequiredError() { return (this.control.touched && !this.control.valid); } } /* Todo Enhancements : * 1. The title of the expandable response can be made dynamic right now it takes the first response for the title and assumes that it is in {label, value} format * 2. Assumes group.key under the iterable should be the same as that of iterable.key in the template * 3. Check if we want to display both primary question and answer in the title of the expansion panel * 4. Check if the primary question and answer can be hidden in the readonly form once they are displayed as the title * 5. 'items' is carrying the responses and is present in the main control. * */ /* Test cases (Working) : * Editable form with responses : * 1. render in editable * 3. add/delete responses * 4. empty and add new responses * 5. client app should be able to persist the responses * New form : * 1. add/delete responses * 2. client-app should be able to persist * Readonly form : * 1. Render with responses * 2. Render without responses * Validations : * Iterable supports two types of validations : * 1. Required field validations : * When individual fields are required, they should be defined at the individual fields level in the template. * Required validation should prevent addition of more items. * 2. MinimumRecordsRequired validation : * When this flag is set in the template, the iterable component should check for items and set the validity of the main control. * 3. On every deletion from items the main control should be re-validated. * 4. Validation should be triggered only when form is touched or when items are emptied out. * */