Skip to content
This repository has been archived by the owner on Dec 12, 2022. It is now read-only.

Commit

Permalink
fix: make connector patch ItemCache only once (#1128)
Browse files Browse the repository at this point in the history
Previous fix for client and server grids on the same page was to
store the original `ensureSubCacheForScaledIndex` method before it
was patched by connector, but that introduced a stack overflow
error after a second grid is added from the server.

This fix makes ItemCache patching to happen only once the first
server grid is added, and prevents it from happening after other
grids are added.

Fixes #1077
  • Loading branch information
DiegoCardoso authored Oct 2, 2020
1 parent ab7c1ff commit f716b8c
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.NativeButton;
import com.vaadin.flow.router.Route;

@Route("grid-on-client-and-slot")
public class GridOnClientAndSlotPage extends Div {
public GridOnClientAndSlotPage() {
GridOnClientAndSlot gridOnClientAndSlot = new GridOnClientAndSlot();

gridOnClientAndSlot.add(createGrid());

NativeButton addGridButton = new NativeButton("add grid", e -> gridOnClientAndSlot.add(createGrid()));
addGridButton.setId("add-new-grid-button");

add(gridOnClientAndSlot, addGridButton);
}

private Grid<String> createGrid() {
Grid<String> grid = new Grid<>();
grid.addColumn(String::toString).setHeader("Column");
grid.setItems("Item 1", "Item 2", "Item 3", "Item 4");

gridOnClientAndSlot.add(grid);

add(gridOnClientAndSlot);
return grid;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;

import com.vaadin.flow.component.grid.testbench.GridTHTDElement;
import com.vaadin.flow.component.grid.testbench.TreeGridElement;
Expand All @@ -34,9 +35,15 @@ public void treeGridOnClientShouldWorkIfAnotherGridIsAddedFromServer() {
TestBenchElement parent = $("grid-on-client-and-slot").first();

TreeGridElement treeGrid = parent.$(TreeGridElement.class).id("tree");
treeGrid.getExpandToggleElement(0, 0).click();;
treeGrid.getExpandToggleElement(0, 0).click();
GridTHTDElement cell = treeGrid.getCell(1, 0);

Assert.assertEquals("child 1-1", cell.getText().trim());

findElement(By.id("add-new-grid-button")).click();
treeGrid.getExpandToggleElement(3, 0).click();
cell = treeGrid.getCell(4, 0);

Assert.assertEquals("child 2-1", cell.getText().trim());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,74 @@ import { ItemCache } from '@vaadin/vaadin-grid/src/vaadin-grid-data-provider-mix
return window.Vaadin.Flow.tryCatchWrapper(callback, 'Vaadin Grid', 'vaadin-grid-flow');
};

let isItemCacheInitialized = false;

window.Vaadin.Flow.gridConnector = {
initLazy: grid => tryCatchWrapper(function(grid) {
// Check whether the connector was already initialized for the grid
if (grid.$connector){
return;
}

// Storing original implementation of the method to be used for client
// side only grids
ItemCache.prototype.ensureSubCacheForScaledIndexOriginal = ItemCache.prototype.ensureSubCacheForScaledIndex;
ItemCache.prototype.ensureSubCacheForScaledIndex = tryCatchWrapper(function(scaledIndex) {
if (!this.grid.$connector) {
this.ensureSubCacheForScaledIndexOriginal(scaledIndex);
return;
}
// Make sure ItemCache patching is done only once, but delay it for when
// a server grid is initialized
if (!isItemCacheInitialized) {
isItemCacheInitialized = true;
// Storing original implementation of the method to be used for client
// side only grids
ItemCache.prototype.ensureSubCacheForScaledIndexOriginal = ItemCache.prototype.ensureSubCacheForScaledIndex;
ItemCache.prototype.ensureSubCacheForScaledIndex = tryCatchWrapper(function(scaledIndex) {
if (!this.grid.$connector) {
this.ensureSubCacheForScaledIndexOriginal(scaledIndex);
return;
}

if (!this.itemCaches[scaledIndex]) {
this.grid.$connector.beforeEnsureSubCacheForScaledIndex(this, scaledIndex);
}
})
if (!this.itemCaches[scaledIndex]) {
this.grid.$connector.beforeEnsureSubCacheForScaledIndex(this, scaledIndex);
}
});

ItemCache.prototype.doEnsureSubCacheForScaledIndex = tryCatchWrapper(function(scaledIndex) {
if (!this.itemCaches[scaledIndex]) {
const subCache = new ItemCache.prototype.constructor(this.grid, this, this.items[scaledIndex]);
subCache.itemkeyCaches = {};
if(!this.itemkeyCaches) {
this.itemkeyCaches = {};
ItemCache.prototype.doEnsureSubCacheForScaledIndex = tryCatchWrapper(function(scaledIndex) {
if (!this.itemCaches[scaledIndex]) {
const subCache = new ItemCache.prototype.constructor(this.grid, this, this.items[scaledIndex]);
subCache.itemkeyCaches = {};
if(!this.itemkeyCaches) {
this.itemkeyCaches = {};
}
this.itemCaches[scaledIndex] = subCache;
this.itemkeyCaches[this.grid.getItemId(subCache.parentItem)] = subCache;
this.grid._loadPage(0, subCache);
}
this.itemCaches[scaledIndex] = subCache;
this.itemkeyCaches[this.grid.getItemId(subCache.parentItem)] = subCache;
this.grid._loadPage(0, subCache);
}
})
});

ItemCache.prototype.getCacheAndIndexByKey = tryCatchWrapper(function(key) {
for (let index in this.items) {
if(grid.getItemId(this.items[index]) === key) {
return {cache: this, scaledIndex: index};
ItemCache.prototype.getCacheAndIndexByKey = tryCatchWrapper(function(key) {
for (let index in this.items) {
if(this.grid.getItemId(this.items[index]) === key) {
return {cache: this, scaledIndex: index};
}
}
}
const keys = Object.keys(this.itemkeyCaches);
for (let i = 0; i < keys.length; i++) {
const expandedKey = keys[i];
const subCache = this.itemkeyCaches[expandedKey];
let cacheAndIndex = subCache.getCacheAndIndexByKey(key);
if(cacheAndIndex) {
return cacheAndIndex;
const keys = Object.keys(this.itemkeyCaches);
for (let i = 0; i < keys.length; i++) {
const expandedKey = keys[i];
const subCache = this.itemkeyCaches[expandedKey];
let cacheAndIndex = subCache.getCacheAndIndexByKey(key);
if(cacheAndIndex) {
return cacheAndIndex;
}
}
}
return undefined;
})
return undefined;
});

ItemCache.prototype.getLevel = tryCatchWrapper(function() {
let cache = this;
let level = 0;
while (cache.parentCache) {
cache = cache.parentCache;
level++;
}
return level;
})
ItemCache.prototype.getLevel = tryCatchWrapper(function() {
let cache = this;
let level = 0;
while (cache.parentCache) {
cache = cache.parentCache;
level++;
}
return level;
});
}

const rootPageCallbacks = {};
const treePageCallbacks = {};
Expand Down

0 comments on commit f716b8c

Please # to comment.