diff --git a/Demo/Controllers/HomeController.cs b/Demo/Controllers/HomeController.cs index 57e33e4..e11f4e3 100644 --- a/Demo/Controllers/HomeController.cs +++ b/Demo/Controllers/HomeController.cs @@ -60,6 +60,16 @@ public IActionResult Index() new(2, "Category 2") }; + var subCats = new List() + { + new(1, "Sub Cat 1-1", 1), + new(2, "Sub Cat 1-2", 1), + new(3, "Sub Cat 2-1", 2), + new(4, "Sub Cat 2-2", 2), + }; + + ViewBag.SubCategories = subCats; + ViewBag.QualityList = Enum.GetValues() .Select(x => new SelectDto((int)x, x.ToString())) .ToList(); @@ -102,7 +112,7 @@ public IActionResult GetCategories(string search) } [HttpGet] - public IActionResult GetSubCategories(string search, List categoryIds) + public IActionResult GetSubCategories(int categoryId, string search) { var subCats = new List() { @@ -112,8 +122,9 @@ public IActionResult GetSubCategories(string search, List categoryIds) new(4, "Sub Cat 2-2", 2), }; - return Json(subCats.Where(x => string.IsNullOrEmpty(search) || x.Name.Contains(search)) - .Where(x => categoryIds.Contains(x.CatId)) + return Json(subCats + .Where(x => string.IsNullOrEmpty(search) || x.Name.Contains(search)) + .Where(x => x.CatId == categoryId) .Select(x => new { text = x.Name, diff --git a/Demo/Views/Home/Index.cshtml b/Demo/Views/Home/Index.cshtml index 7c6fb2d..b3221e7 100644 --- a/Demo/Views/Home/Index.cshtml +++ b/Demo/Views/Home/Index.cshtml @@ -57,6 +57,7 @@ text: item.name })) }, + onCellValueChanged: (params) => gridazorDropdownHelper.resetDependentValue(params, 'subCatId'), valueFormatter: (params) => gridazorDropdownHelper.valueFormatter(params) }, { @@ -65,10 +66,10 @@ cellEditorParams: params => { return { searchUrl: "Home/GetSubCategories?categoryId=" + params.data.catId, - values: subCategories.map(item => ({ + values: subCategories.filter(item => item.catId === params.data.catId).map(item => ({ value: item.id, text: item.name - })) // for the first load + })) // Initial values based on current selection }; }, valueFormatter: (params) => gridazorDropdownHelper.valueFormatter(params) diff --git a/Demo/wwwroot/lib/gridazor/gridazor.js b/Demo/wwwroot/lib/gridazor/gridazor.js index faf4954..f7806d6 100644 --- a/Demo/wwwroot/lib/gridazor/gridazor.js +++ b/Demo/wwwroot/lib/gridazor/gridazor.js @@ -351,30 +351,26 @@ class GridazorDropdown { this.options = params.values || []; this.selectedValue = params.value; this.searchUrl = params.searchUrl; + this.params = params; - // Create container and input this.eGui = document.createElement('div'); this.eGui.classList.add('gridazor-select-wrapper'); - this.eInput = document.createElement('input'); this.eInput.classList.add('gridazor-select'); this.eInput.value = this.getTextByValue(this.selectedValue); this.eGui.appendChild(this.eInput); - // Create dropdown this.eDropdown = document.createElement('div'); this.eDropdown.classList.add('gridazor-dropdown'); this.eDropdown.style.display = 'none'; this.eGui.appendChild(this.eDropdown); - // Event listeners this.eInput.addEventListener('input', () => this.filterOptions(this.eInput.value)); this.eInput.addEventListener('focus', () => this.showDropdown()); document.addEventListener('click', (event) => { if (!this.eGui.contains(event.target)) this.hideDropdown(); }); - // Initial render this.renderDropdown(this.options); } @@ -404,50 +400,68 @@ class GridazorDropdown { renderDropdown(options) { this.eDropdown.innerHTML = ''; // Clear dropdown content + + if (options.length === 0) { + const noResultsElement = document.createElement('div'); + noResultsElement.classList.add('gridazor-dropdown-item', 'no-results'); + noResultsElement.textContent = 'No options available'; + this.eDropdown.appendChild(noResultsElement); + return; + } + options.forEach(option => { const optionElement = document.createElement('div'); optionElement.classList.add('gridazor-dropdown-item'); optionElement.textContent = option.text; - if (option.value === this.selectedValue) { optionElement.classList.add('selected'); } - optionElement.addEventListener('click', () => { this.selectedValue = option.value; this.eInput.value = option.text; this.hideDropdown(); }); - this.eDropdown.appendChild(optionElement); }); } filterOptions(query) { - const localOptions = this.options.filter(option => - option.text.toLowerCase().includes(query.toLowerCase()) - ); - - if (localOptions.length || !this.searchUrl) { - this.renderDropdown(localOptions); + if (this.searchUrl) { + this.fetchOptions(query).then(filteredOptions => { + this.renderDropdown(filteredOptions); + }); } else { - this.fetchOptions(query).then(filteredOptions => this.renderDropdown(filteredOptions)); + const localOptions = this.options.filter(option => + option.text.toLowerCase().includes(query.toLowerCase()) + ); + this.renderDropdown(localOptions); } } fetchOptions(query) { - return fetch(`${this.searchUrl}?search=${encodeURIComponent(query)}`) + let url = this.searchUrl; + + if (query) { + url += url.includes('?') ? '&' : '?'; + url += `search=${encodeURIComponent(query)}`; + } + + return fetch(url) .then(response => response.ok ? response.json() : []) - .catch(error => console.error('Error fetching options:', error)); + .catch(error => { + console.error('Error fetching options:', error); + return []; + }); } getTextByValue(value) { + if (value === null || value === undefined) return ''; + const selectedOption = this.options.find(option => option.value === value); - return selectedOption ? selectedOption.text : ''; + return selectedOption ? selectedOption.text : value.toString(); } } - // Helpers var gridazorDateInputHelper = { valueGetter: function (p) { @@ -471,12 +485,33 @@ var gridazorFileInputHelper = { } }; -var gridazorDropdownHelper = { +const gridazorDropdownHelper = { valueFormatter: function (params) { - console.log(params) - const option = params.colDef.cellEditorParams.values - .find(opt => opt.value === params.value); + if (params.value === null || params.value === undefined) { + return ''; + } + + const editorParams = typeof params.colDef.cellEditorParams === 'function' + ? params.colDef.cellEditorParams(params) + : params.colDef.cellEditorParams; - return option ? option.text : params.value; + if (editorParams && Array.isArray(editorParams.values)) { + const option = editorParams.values.find(opt => opt.value === params.value); + if (option) { + return option.text; + } + } + + return params.value.toString(); + }, + resetDependentValue: function (params, childField) { + if (params.oldValue !== params.newValue) { + params.data[childField] = null; + params.api.refreshCells({ + rowNodes: [params.node], + columns: [childField], + force: true + }); + } } }; \ No newline at end of file