I am using Angular 11 and 2020-Dec IG build.
My grid has a column of Name (string) and a column of Type (dropdown of static string-type choices). The validation of Name is based on Type and a regex that looks like this: /^PLAN\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_]{0,39}$/
"PLAN" is one of many prefixes that align with specific types and are not interchangeable.
What I need is a dynamically appearing warning about the Name not properly validating and prevention of submission by either click or enter keypress. I intend to have a warning snackbar floating in the upper right corner of the grid (zindex = 999), a warning span in the igxRowEditText area, and the "done" button in the igxRowEditActions area disabled until the Name matches the correct format.
I am finding that (onCellEdit) is not working for me, neither is (keypress) or (input) on my template for the column.
HTML template for Name and igxRowEditText:
<ng-template igxRowEditText> <span class="error" *ngIf="badName">{{badNameWarning}}</span> </ng-template> <igx-column [pinned]="true" field="name" header="Scenario Name" [dataType]="'string'" [resizable]="true" width="246px" [sortable]="true"> <ng-template igxCellEditor let-cell="cell"> <igx-input-group> <input igxInput [igxFocus]="true" name="ScenarioName" [(ngModel)]="cell.editValue" (keypress)="validateName(cell.editValue, cell.scenarioType)"> </igx-input-group> </ng-template> </igx-column>
Handling in my Component:
//validation badName:boolean = false; badNameWarning:string = ''; validateName(scenName:string, type:string) { let prefix:string = ''; this.badName = false; if (type == 'Commit' && !/^PLAN\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_]{0,39}$/.test(scenName)) { this.badName = true, prefix = 'PLAN'; } ...else if (type == ...types this.badNameWarning = `${type} Name should be: ${prefix}_YYYY_WW(optional [a-z,A-Z,0-9,-,_] to 50 total)`; };
1) While the Ignite UI for Angular suite provides built-in Snackbar and Toast components, I suppose they do not fit your particular requirement about positioning the warning in the upper-right corner of the grid. Nevertheless, here is an additional sample applying an IgxToast, positioned at the top of the page.
So, to have more control over the warning’s position, my suggestion is to use the IgxTooltip. It can accept an OverlaySettings object as a parameter of its open method. With the help of the positionStrategy property and its PositionSettings object, the tooltip can be placed accordingly. The target of the OverlaySettings can be directly specified as the grid's nativeElement, or as done in the sample - as a point (using the grid’s html element ClientRect and specifying a bit of an offset, so that the tooltip is slightly distanced from the grid’s bounds).
2) The warning span’s implementation is same as yours.
3) Disabling the igxRowEditActions’ “Done” button by binding its “disabled” property to the “badName” variable.
I was unable to declare the Subscription, uninitialized for this warning: Property 'inputSubscription' has no initializer and is not definitely assigned in the constructor.ts(2564)
I changed the entry in TS to the following, which gets rid of the error, but also generates a new one when changing the value of the column (expanded below snippet):
private inputSubscription: Subscription = Object.create(null);
ERROR TypeError: this.inputSubscription.unsubscribe is not a function at ScenariosComponent.modelChange (scenarios.component.ts:49) at ScenariosComponent_div_0_ng_template_10_Template_input_ngModelChange_1_listener (scenarios.component.html:19) at executeListenerWithErrorHandling (core.js:14994) at wrapListenerIn_markDirtyAndPreventDefault (core.js:15035) at SafeSubscriber.schedulerFn [as _next] (core.js:25687) at SafeSubscriber.__tryOrUnsub (Subscriber.js:183) at SafeSubscriber.next (Subscriber.js:122) at Subscriber._next (Subscriber.js:72) at Subscriber.next (Subscriber.js:49) at EventEmitter_.next (Subject.js:39)
That helps. I'm noticing, though, that the value selected in the dropdown columns is erased when you click into the dropdown field. This is definitely not ideal as some dropdown fields (in other pages) will literally have 100s of options . Can you fix that issue in your example and let me know what the resolution there is?
Hello Chris,
I am glad that the suggestion about the compilerOptions helps.
To address your question about the IgxCombo, I have modified the previous sample to correct this behavior. For additional information regarding the IgxCombo, please refer to its dedicated page in our documentation.
Since your initial question was not about the IgxCombo, I am not familiar with your requirements and created the “Category” column in the sample’s grid just for demo purposes. For example, under the assumption that only single item can be selected at a time, the approach shown here is applied. The most important aspect (that was causing the initially described behavior) is that the IgxCombo’s value has to be bound to a list (e.g. array). Even if your column’s value is a single string, please, make sure to have it contained within an array. Another sample, showing multiple selection for a combo, contained within an IgxGrid can be found in the Grid cell editing topic here.
By the way, have you had the chance to implement the cell validation? Please, keep me posted on your progress.
Best regards,Bozhidara PachilovaAssociate Software Developer
I cannot tell what has changed to affect retaining the value.
Here is what I currently have for my column with a dropdown (not retaining value upon dropdown):
<igx-column field="scenarioType" header="Type" [dataType]="'string'" [resizable]="true" width="83px" [sortable]="true"> <ng-template igxCell let-cell="cell">{{cell.value}}</ng-template> <ng-template igxCellEditor let-cell="cell" let-value> <igx-combo [(ngModel)]="cell.editValue" (ngModelChange)="modelChange($event, cell)" [data]="types" width="220px" [igxFocus]="true" (onSelectionChange)="singleSelection($event)" [overlaySettings]="customOverlaySettings"></igx-combo> </ng-template> </igx-column>
A further frustration I've overcome is the use of @ViewChild with *ngIf in the grid. I figured out for myself that you need @ViewChildren when your component reference is gated by a conditional, which mine is due to asynchronously loading the list of Scenarios.
Here is what I have in the component.ts for this:
@ViewChildren('scenarioList', { read: IgxGridComponent }) public scenarioList: IgxGridComponent; customOverlaySettings:OverlaySettings; types:string[] = []; ngOnInit(): void { this.getScenarios(); this.getScenarioTypes(); //sets values to types:string[] this.getHorizons(); this.getWorkweeks(); } ngAfterViewInit() { this.customOverlaySettings = { outlet: this.scenarioList.outlet }; } modelChange(event:any, cell:IgxGridCellComponent) { if (this.inputSubscription) { this.inputSubscription.unsubscribe(); } let type:string = this.types.indexOf(event[0]) > -1 ? event[0] : cell.rowData.scenarioType, prefix:string = ''; this.inputSubscription = of(event).pipe(delay(500), distinctUntilChanged()) .subscribe(eventVal => { if (type == 'Commit' && !/^PLAN\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_\-]{0,38}$/.test(eventVal)) { this.badName = true; prefix = 'PLAN'; } else if (type == 'Scenario' && !/^BETA\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_\-]{0,38}$/.test(eventVal)) { this.badName = true; prefix = 'BETA'; } else if (type == 'LRP' && !/^LRP\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_\-]{0,39}$/.test(eventVal)) { this.badName = true; prefix = 'LRP'; } else { this.badName = false; } this.badNameWarning = prefix !== '' ? `${type} Name should be: ${prefix}_YYYY_WW(optional [a-z,A-Z,0-9,-,_] to 50 total)` : ''; }); }; singleSelection(event: IComboSelectionChangeEventArgs) { if (event.added.length) { event.newSelection = event.added; } };
I only filled out for the first 3 scenario types, but I assume it makes sense for the whole group. Validation is applied whether the Scenario Name or Type is changed. That portion is working like a charm and I am saving the Toast/ToolTip for save responses on the attributes of Scenario.
Regardless, the dropdown doesn't persist the populated scenario type when opened: no value is currently selected.
let type:string = this.types.indexOf(event[0]) > -1 ? event[0] : cell.rowData.scenarioType, …
Your latest sample prevents updating the other column when the value in the one you're editing doesn't match validation. This is not what I wanted. I will have to reassign values to the Combo to ensure the data that hasn't changed is still persisted.
Thank you for clarifying the current version you are using.
Yes, there have been some breaking changes between versions 11 and 12, such as the IgxGridCellComponent being deprecated and replaced by IgxGridCell, as well as the onSelectionChange output of the igx-combo renamed to selectionChanging. Looking at your snippets, I can see that you have addressed those changes accordingly. All changes are listed in our Changelog here.
Attached you will find the sample updated with Angular major v.12 and IgniteUI v.12.3.13. On my side, the validation when changing the selection in the igx-combo in the “Category” field seems to be working correctly, as far as my understanding of the requirement. Please, check it out and let me know whether it properly reflects the idea. Generally, I do not think that there is an issue with the IgxGrid regarding this feature, as this is not built-in functionality, but rather a custom validation-message implementation. In case the sample shows such validation working alright, please, compare any discrepancies between it and your app code. The test project is really simple and its purpose is to isolate the logix as much as possible, so that wat may not be working could be easily reproduced.
Please, keep me posted on your progress.
Best regards, Bozhidara Pachilova
6014.igx-grid-editing-validation-with-tooltip-extended-12.3.zip
I apologize. I'm now on AG/IG 12.3 and I forgot to include the column templates for scenario Name and Type. I have added them to the previous response.
The scenarioExists function is used in both cases and only triggered if the name is valid by validation via RegEx.
My assumption is that implementation has changed since AG/IG 11 or that there is a significant difference in the way this should be implemented when moving from a formless (stand-alone input fields) modal to the ig-grid interaction with embedded forms.
If you do not believe the transition from IGX 11 to 12.3 is significant, let me know and I will modify the above sample. Otherwise, let me know when you can have a similar sample with 12.3 implementation changes ready.
As it is been a while since our last communication in this thread, it is possible that the StackBlitz environment has undergone version requirements changes. For your convenience, I am attaching the sample with igniteui-angular major version 11 below, so that you could run it locally. In case you have upgraded major versions since then, it is possible that breaking changes, related to the grid functionalities used might have been introduced, so if that is the case, please let me know.
The sample is also modified, so that validation logic is executed only when the “Category” field is edited, based on your latest clarifications.
However, I am afraid that currently it is pretty unclear to me as to what the issue you are facing might be. I encourage you to run the attached sample on your side, modify it, so that it reflects what you would like to achieve and possibly reproduce the issue you are facing. As you mentioned yourself, specific requirements are complex and often subject to change. Having this in mind, providing us with small isolated samples with steps and description (such as comments) would be most effective, so that we can assist you. For example, in the provided snippets, there are still missing pieces, such as the “scenarioExists” method and possibly other details, which might be related to anything going on. There might also be template code errors, for instance.
Lastly, please keep in mind that the sample uses simple regular expressions for demo purposes. As you mention that the validation works for you in another configuration, I suppose the expressions themselves are not the issue here. In any case, assisting with custom regex patterns would be considered out of scope of Infragistics Support.
Best regards,Bozhidara PachilovaInfragistics
5635.igx-grid-editing-validation-with-tooltip-extended.zip
I have cleaned up the logic for efficiency and modified again, per users' changing requirements. At present, the validation is based upon this REGEX: /^(LRP|LTP|PLAN|TOP)\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_\-]{0,38}$/.
Current implementation works with validating fields in a copy modal, as seen here:
validateName(type:string) { let prefix:string = type === 'Commit' ? 'PLAN' : type === 'Scenario' ? '(LRP|LTP|PLAN|TOP)' : type, regex:RegExp = new RegExp(`^${prefix}\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_\-]{0,${prefix.length === 3 ? 39 : 38}}$`); this.scenario.scenarioType = type, this.scenario.scenarioTypeId = this.types.filter(t => t.name === type)[0].id, this.badNameCopy = !regex.test(this.scenario.name), this.badNameCopyWarning = this.badNameCopy ? `${type} Name should be: ${prefix}_YYYY_WW(optional [a-z,A-Z,0-9,-,_] to 50 total)` : ''; if (!this.badNameCopy) { this.scenarioExists(this.scenario.name, 'copy'); } };
Yet, when implemented in a similar way on the Scenarios grid, it fails to validate any name, no matter what the entry. Here is my updated code for modelChange:
modelChange(event:any, cell:IgxGridCell, method:string) { if (this.inputSubscription) { this.inputSubscription.unsubscribe(); } let type:string = method === 'type' ? event[0] : cell.row.data.scenarioType, prefix:string = type === 'Commit' ? 'PLAN' : type === 'Scenario' ? '(LRP|LTP|PLAN|TOP)' : type, regex:RegExp = new RegExp(`^${prefix}\_[0-9]{4}\_[0-9]{2}[a-zA-Z0-9\_\-]{0,${prefix.length === 3 ? 39 : 38}}$`); this.inputSubscription = of(event).pipe(distinctUntilChanged()).subscribe(eventVal => { this.badName = !regex.test(eventVal.length > 9 ? eventVal : cell.row.data.name), this.badNameWarning = this.badName ? `${type} Name should be: ${prefix}_YYYY_WW(optional [a-z,A-Z,0-9,-,_] to 50 total)` : ''; if (method === 'name' && !this.badName) { this.scenarioExists(eventVal.length > 9 ? eventVal : cell.row.data.name, 'update'); } }); };
Here is the HTML implementation of the scenario Name and Type columns in the grid:
<igx-column [pinned]="true" field="name" header="Scenario Name" [dataType]="'string'" [resizable]="true" width="12%" [sortable]="true"> <ng-template igxCellEditor let-cell="cell"> <igx-input-group type="border"> <input igxInput [igxFocus]="true" [(ngModel)]="cell.editValue" (ngModelChange)="modelChange($event, cell, 'name')" displayDensity="compact"> </igx-input-group> </ng-template> </igx-column> <igx-column field="scenarioType" header="Type" [resizable]="true" width="5%" [sortable]="true"> <ng-template igxCell let-cell="cell">{{cell.value}}</ng-template> <ng-template igxCellEditor let-cell="cell" let-value> <igx-combo [(ngModel)]="cell.editValue" (ngModelChange)="modelChange($event, cell, 'type')" [data]="types" width="220px" [igxFocus]="true" (selectionChanging)="singleSelection($event)" [overlaySettings]="customOverlaySettings" [displayKey]="'name'" [valueKey]="'name'"></igx-combo> </ng-template> </igx-column>
Perhaps you can see where I'm going wrong with this.
In case it's unclear, a 'Commit' scenario type should have a prefix of "PLAN", other scenario types (LRP, LTP, TOP) will have their type name as the prefix, and Scenario can have any of those prefixes. Additionally, the ScenarioExists function verifies the name is not already assigned to an existing scenario of any type in the database.
Also, your samples are broken due to changes in the imported styling libraries.
You're double-binding the double-binding, which is the issue. In my case, I'm only reading the change on the category and applying validation to the Name. In this way, users are able to change either field without being restricted to that field when making the change. I can't imagine why you would have built it so you can't actually change the category. That was obviously not what I was looking for.
As it showed me how to get where I wanted to go, I marked it as the answer anyway.