p-multiSelect how prevent adding elements with the same index - javascript

I have a list of options for multi-select. in one of the option I should add another field-remark field. so selecting in the first time add this field. but when removing the selection it does not removing this selection from the array becouse I did not remove the remark field. so when select this option again will add twice the same index(one with the remark field and one with null in the remark) I need to set value only if I dont have this index in the array
<p-multiSelect [required]="formGroup.hasError('remark-reasons-required')"
[options]="reasons" defaultLabel="" formControlName="remarks" optionLabel="hebName"
selectedItemsLabel="{0} "
(onChange)="onChangeReasonsValue($event)"></p-multiSelect>
onChangeReasonsValue(event: { value: ReviewDecisionReasonModel[] }): void {
//
var selectedArray = event.value.filter(function (item, pos) {
return event.value.indexOf(item) == pos;
})
this.formGroup.get('remarks').setValue(selectedArray);
this.selectedReasons = selectedArray;
this._decision.reasons = selectedArray;
}

It seems the multi-select component have a bug, where the disabled/removed options from the component remain added to the related formControl.
I propose you add a "disabled" property to your options, and set this option as the selections change instead of adding/removing them. After that, you could adjust the formValues with only enabled options.
Also, you could not use (OnChange) in favor of subscribing to the form changes from the component.
something like
otherReasonWhen2 = { id: 3, hebName: 'heb3', freeTextAllow: false, disabled: true };
reasons = [
{ id: 1, hebName: 'heb1', freeTextAllow: false, disabled: false },
{ id: 2, hebName: 'heb2', freeTextAllow: false, disabled: false },
this.otherReasonWhen2,
];
ngOnInit() {
this.formGroup.get('remarks').valueChanges.subscribe((newValues) => {
console.log(newValues) // This is here for you to see the values as they change
this.otherReasonWhen2.disabled = newValues.every(reason => reason.id !== 2)
if (newValues.some(reason => reason.disabled)){
// work-around for the observed bug: when there are disabled options selected, remove them from the form.
this.formGroup.get('remarks').setValue(newValues.filter(reason => !reason.disabled));
}
});
}
and don't forget to add the directive to disabled the option:
<p-multiSelect
[options]="reasons"
optionDisabled="disabled" <-- Here
defaultLabel=""
formControlName="remarks"
optionLabel="hebName"
selectedItemsLabel="{0} "
></p-multiSelect>

Related

Angular: Select checkbox on another checkbox selection ( dynamic form control )

I'm new to angular.
I have this UI. I wanted to make Bill To checkbox checked if any one (Address, Email or Phone) of the checkbox is checked. I'm using dynamic form control for checkbox.
code snippet: This solution didnt work , as combineLatest is emitting only when all the three checkboxes get selected
combineLatest([this.form.customerInfo.address.valueChanges(),
this.form.customerInfo.email.valueChanges(),
this.form.customerInfo.phone.valueChanges()])
.subscribe((formValues)=>{
if (formValues.every((value) => value === false || value === undefined)) {
this.form.customerInfo.billTo.setValue(false)
}
else {
this.form.customerInfo.billTo.setValue(true)
}
});
this.form.customerInfo.billTo.valueChanges().pipe(distinctUntilChanged()).subscribe(value =>{
// if billTo is false or unchecked- unchecked all
if(!value){
this.form.customerInfo.address.setValue(value);
this.form.customerInfo.phone.setValue(value);
this.form.customerInfo.email.setValue(value);
}
})
another solution: it's a bit complicated
combineLatest([
this.form.customer.address.valueChanges().pipe(startWith(false)),
this.form.customer.phone.valueChanges().pipe(startWith(false)),
this.form.customer.email.valueChanges().pipe(startWith(false)),
])
// adding debounce until we can listen for form changes and patch multiple form values simultaneously
.pipe(debounceTime(100))
.subscribe(([address, phone, email]) => {
if ((address || phone || email) && !this.form.customerInfo.billTo.value) {
// one of the nested values is true, and billTo is false, make billTo true
this.form.customer.billTo.setValue(true);
}
});
// when customer toggle billTo to false, we set all children to false
this.form.customer.billTo
.valueChanges()
.pipe(filter((value) => value !== true))
.subscribe(() => {
const address = this.form.customer.address.value;
const phone = this.form.customer.phone.value;
const email = this.form.customer.email.value;
// if any of the nested values are true, set them to false
if (address) {
this.form.customer.address.setValue(false);
}
if (phone) {
this.form.customer.phone.setValue(false);
}
if (email) {
this.form.customer.email.setValue(false);
}
});
Can anyone tell how to improve this? Thanks in advance.

Save slickgrid cell autocomplete value

I have a slickgrid cell with an autocompletion and a custom formatter. The cell value is a key field of dynamically loading autocompletion list. The list is shown as labels (e.g. Contract: ABCDEF-123, thick:10, width:210, City: Chicago) and when I select one it appears in the according input field. The point is that the formatter does not know that label, it only knows the key (order_id).
function contractFormatter(row, cell, value, columnDef, dataContext) {
var res = '';
var val = get_contract_list()[value] ? get_contract_list()[value]['label'] : '';
res = '<span class="' + getSpanClass() + '" style="float: left;"></span>\n\
'+ (val =='' ? '<span style="color:slategrey;"> Enter 3 symbols </span>' + val : val) + '';
return res;
}
The function get_contract_list returns the whole list of contracts and it is very big, so it was decided to make that list dynamic. So the function is empty now and it would be nice just to take the selected label into val.
Is there any way to achieve it?
You have to remember that Formatters are synchronous and it must return right away in a string format, if it requires a lot of processing power while staying synchronous then you'll end up slowing down your grid. You should probably cache your list once in a separate variable and use it afterward instead of reloading the list every time. If you load something that takes time and is asynchronous (a delayed output) then you'll want to look up the asyncPostRenderer (you can see this Example)
So going back to displaying the associated key to a label, I've done something similar in my lib in this Example and a live demo here, in my use case the value is a key index and I use the complexityLevelList to find its associated object which I can then read its label to display in the formatter.
export class MyExample {
complexityLevelList = [
{ value: 0, label: 'Very Simple' },
{ value: 1, label: 'Simple' },
{ value: 2, label: 'Straightforward' },
{ value: 3, label: 'Complex' },
{ value: 4, label: 'Very Complex' },
];
prepareGrid() {
this.columnDefinitions = [
{
id: 'complexity', name: 'Complexity', field: 'complexity', minWidth: 100,
formatter: (_row, _cell, value) => this.complexityLevelList[value].label,
filter: {
model: Filters.multipleSelect,
collection: this.complexityLevelList
},
editor: {
model: Editors.singleSelect,
collection: this.complexityLevelList,
massUpdate: true
},
},
];
}
}
Note that the Filters & Editors are specific to my lib Slickgrid-Universal, but you should get the idea on how to refactor your code to make it work.

Angular Slickgrid | How to disable the row selection of checkboxSelector of selectableOverride dynamically

Want to disable the selected row items by updating the gridOptions after some button clicks.
initGridOptions() {
this.gridOptions = {
enableSorting: true,
enableFiltering: true,
enablePagination: true,
enableAutoResize: true,
autoResize: {
containerId: 'grid-wrapper',
sidePadding: 5
},
alwaysShowVerticalScroll: false,
enableCheckboxSelector: true,
enableRowSelection: true,
checkboxSelector: {
hideInFilterHeaderRow: false,
hideInColumnTitleRow: true,
},
rowSelectionOptions: {
// True (Single Selection), False (Multiple Selections)
selectActiveRow: false
}
}
}
//prepareGrid() { ...... }
disableButtonClick() {
this.gridObj.setOptions({
checkboxSelector: {
selectableOverride: (row: number, dataContext: any, grid: any) => {
// validate each row and disable the selected rows
return false;
}
}
});
}
Stackblitz Demo
I'm not sure if you can toggle the checkbox column without removing it (maybe with grid.setColumns() but it's probably better to just use the selectableOverride callback. It will allow you to dynamically change its usability on the fly (see the Wiki) and in your case just use your boolean flag to have the callback return true or false (the later will disable/remove all checkboxes)
export class Example1 implements OnInit {
prepareGrid() {
this.gridOptions = {
enableRowSelection: true,
enableCheckboxSelector: true,
checkboxSelector: {
// you can override the logic for showing (or not) the expand icon
// for example, display the expand icon only on every 2nd row
selectableOverride: (row: number, dataContext: any, grid: any) => (dataContext.id % 2 === 1)
},
multiSelect: false,
rowSelectionOptions: {
// True (Single Selection), False (Multiple Selections)
selectActiveRow: true,
},
};
}
}
As per the new comments and the stachblitz, you need to have only 1 common method and in that method you do different logic depending on what button is clicked outside. For example, if I take some code from your demo, let's use a new flag showOnlyOddRows = false and let say that when you click your external button it will turn that flag to true and as we can expect it will re-render the grid and only show the row selection on odd rows
export class AppComponent implements OnInit {
showOnlyOddRows = true;
ngOnInit(): void {
this.gridOptions = {
checkboxSelector: {
hideInFilterHeaderRow: true,
hideInColumnTitleRow: false,
selectableOverride: this.validateRowSelection.bind(this)
// or
// selectableOverride: (row: number, dataContext: any) => this.validateRowSelection(row, dataContext),
},
// ...
};
}
validateRowSelection(row: number, dataContext: any, grid: any) {
return this.showOnlyOddRows ? dataContext.id % 2 === 1 : true; // returning true means that we want to show the row selection
}
// change flag when external button is clicked and re-render grid with new row selection set
disableOddRows() {
this.showOnlyOddRows = true;
this.gridObj.invalidate(); // this will re-execute the validateRowSelection method
}
So again, do not change the override with setOptions, it will completely break the code, so don't do that. If you really need to change options of the plugin, you should use the plugin setOptions not the grid.setOptions. Something like this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.checkboxSelector).setOptions({ /* ... */ }) or this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.checkboxSelector).selectableOverride = newOverrideFn ... but again I probably wouldn't do that, it's easier to just keep 1 method with different logic inside it (like validateRowSelection shown earlier)

i dont want select2 multi select drop down loose data in page refresh. select2 drop down should maintain state

I don't want select2 multi select drop down loose data in page refresh. select2 drop down should maintain state
below is my code. its working fine, I have only problem that select2 is losing selection/data if we refresh the page.My requirement is even after page refresh it should not remove there values/or empty.
$processOrderNbr.select2({
ajax: getProcessOrderNbr(),
minimumInputLength: 4,
placeholder: " ",
allowClear: false,
multiple: true
});
function getProcessOrderNbr() {
return {
url: 'GetProcessOrder',
dataType: 'json',
delay: 250,
data: function (params) {
return {
searchKeyword: params
};
},
results: function (data) {
return {
results: $.map(data, function (item) {
return {
text: item,
id: item
}
})
};
}
};
}
You need to put them again into the select2 boxes.
I had a similar issue, where I work with window.sessionStorage to store the selected values. After reloading the page, I set the values stored in the session storage into the select2 boxes with this code:
var selectionList = [{ id: window.sessionStorage.getItem("searchValue"), text: window.sessionStorage.getItem("searchValue")}];
$("#searchValue").select2({
data: selectionList
});
$('#searchValue').val(window.sessionStorage.getItem("searchValue")).trigger("change");
To store the values in the window.sessionStorage you can use this code:
$('#searchValue').select2({
...
.on("change", function (e) {
window.sessionStorage.setItem("searchValue", $(this).val());
}
...
Maybe this solution helps you in your case, but someone knows a better solution?
If you need to show the previous wrote value after reloading the page without losing the data that is the solution.
var dataCategory = JSON.parse(sessionStorage.getItem('category'));
var select = $("#category");
select.select2({
tags: true,
allowClear: true,
maximumInputLength: 30,
maximumSelectionLength: 20,
tokenSeparators: [',', ' '],
data: dataCategory
}).on("change", function (e) {
var selected = []; // create an array to hold all currently wrote by user
// loop through each option
$('#category option').each(function() {
// if it's selected, add it to the array above
if (this.selected) {
selected.push(this.value);
}
});
// store the array of selected options
sessionStorage.setItem('category', JSON.stringify(selected));
});
// set and show the previous wrote keywords
var dataCategoryLength = dataCategory.length;
for (var i =0; i < dataCategoryLength; i++) {
$('#category').val(dataCategory).trigger('change');
}

kendo grid dynamic field-editable definition

I have kendo-ui grid, with some fields.
I need one of the fields to be editable on add new row, and not editable on update row.
I try to change data-source definitions before add row, and change it back before update.
But the changing doesn't help.
Is there any way to do it?
Here is what I tried to do:
var schema = {
data: 'results',
model: {
id: 'GroupCode',
fields: {
GCode: { editable: false },
GroupPrincipalId: { editable: false },
GroupPrincipalName: { editable: false },
ChildCount: { editable: true },
}
}
};
onAddClick: function(){
var gridElement = ('#myGrid').data('kendoGrid');
gridElement.dataSource.options.schema.model.fields.GroupPrincipalId.editable = true;
gridElement.dataSource.options.schema.model.fields.GroupPrincipalName.editable = true;
gridElement.addRow();
}
(onAddClick is called by my custom adding-button, not related to kendo-adding-logic);
You can use the approach described here:
http://www.telerik.com/forums/making-column-as-readonly-on-update-and-editable-on-insert-in-grid
When create button is pressed you mark a variable as isCreating and in the edit section you check it and if is false you disable the requiered field/fields.

Categories

Resources