{"id":23922611,"url":"https://github.com/datenhahn/componentrenderer","last_synced_at":"2025-04-11T23:52:17.074Z","repository":{"id":215468870,"uuid":"48401967","full_name":"datenhahn/componentrenderer","owner":"datenhahn","description":"A ComponentRenderer for the Vaadin Grid","archived":false,"fork":false,"pushed_at":"2017-06-30T20:47:42.000Z","size":1430,"stargazers_count":6,"open_issues_count":8,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-11T23:52:12.338Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datenhahn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-12-22T00:47:58.000Z","updated_at":"2017-11-18T03:12:22.000Z","dependencies_parsed_at":"2024-01-04T16:44:10.644Z","dependency_job_id":null,"html_url":"https://github.com/datenhahn/componentrenderer","commit_stats":null,"previous_names":["datenhahn/componentrenderer"],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datenhahn%2Fcomponentrenderer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datenhahn%2Fcomponentrenderer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datenhahn%2Fcomponentrenderer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datenhahn%2Fcomponentrenderer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datenhahn","download_url":"https://codeload.github.com/datenhahn/componentrenderer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248497856,"owners_count":21113984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-05T17:15:24.551Z","updated_at":"2025-04-11T23:52:17.048Z","avatar_url":"https://github.com/datenhahn.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ComponentRenderer\n\n**CAUTION!!! Does NOT support Vaadin 8 Grid. Only Vaadin 7 compatibility Grid. Version 2.0.0 runs with Vaadin 8 but is only compatible with Vaadin 8 V7 compatibility layer components. For Vaadin 7 you can still use the latest 1.X.X Version which is stable.**\n\n**Upcoming Vaadin 8.1 supports components out-of-the box**:\nhttps://demo.vaadin.com/sampler/#ui/grids-and-trees/grid/component-renderer\n\nRenders standard Vaadin components in the grid.\n\n\u003cimg src=\"./doc/componentrenderer_logo.png\"\u003e\n\n## Features\n * Plain Java server side coding, as usual\n * Standard Tooltips work\n * Components can be combined in layouts (e.g. multiple images in a horizontal layout)\n * Works with standard colums and generated columns\n * Standard ValueChange/ClickListeners\n * Supports Keyboard-Navigation (ENTER focuses the component inside the cell or its input field if it has one, ESC\n   switches the focus back to the surrounding cell, so navigating through the grid with the arrow keys can continue)\n * Open and Close the row-details with CTRL + DOWN and CTRL + UP\n * Preserve the grid focus on manual triggered refresh\n * creates component editor fields for the grid editor (display or unbuffered-edit only)\n \n## Limitations\n\n * Components inside a cell should have fixed sizes, otherwise all browsers expect Chrome do lots of measurements\n   and relayout which slows down rendering\n * Created Editor-Fields just show the components from the cell 1:1, works nicely for displaying only components,\n   for fields any changes to the underlying properties are up to your implementation, buffered-mode has no\n   effect\n   \n## Important info\n\n * see componentrenderer.scss for styling info\n * make sure you add the addons.scss to your own theme, so the componentrenderer's default styles get loaded,\n   they vertically center the components in the cells\n   \n## Bugs \u0026 Features\n\nPlease report bugs or feature-wishes in the github issue-tracker to further improve this renderer.\n   \n## Use the renderer\n\nAdd this dependency to your pom\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003ede.datenhahn.vaadin\u003c/groupId\u003e\n        \u003cartifactId\u003ecomponentrenderer\u003c/artifactId\u003e\n        \u003cversion\u003eENTER VERSION HERE\u003c/version\u003e\n    \u003c/dependency\u003e\n\nthen recompile the widgetset.\n\n## Demo Application\n\nHave a look at the demo app, you can start it with:\n    \n    cd componentrenderer-demo\n    mvn jetty:run\n    \n## Use with standard grid\n\nExample using the renderer with the standard Vaadin grid, see Demo Source-Code for complete example.\n\n    // don't forget to activate all extensions (ComponentGrid automatically adds these)\n    ComponentCellKeyExtension.extend(grid);\n    focusPreserveExtension = FocusPreserveExtension.extend(grid);\n    DetailsKeysExtension.extend(grid);\n    \n    myGrid.addColumn(COL_DELETE, Component.class).setRenderer(new ComponentRenderer());\n    //...\n    generatedPropertyContainer.addGeneratedProperty(COL_DELETE, new DeleteButtonValueGenerator(myGrid));\n    \n    //...\n    // You can use the FocusPreserveExtension to refresh the grid withoud losing the current\n    // cell focus\n    focusPreserveExtension.saveFocus();\n    grid.setCellStyleGenerator(grid.getCellStyleGenerator());\n    focusPreserveExtension.restoreFocus();\n\n## Use with the ComponentGrid supplied with the renderer\n\nExample using the renderer with the ComponentGrid, see Demo Source-Code for complete example.\n\n    grid.addComponentColumn(FOOD, cust -\u003e createFoodSelector(grid, cust));\n    grid.addComponentColumn(FOOD_ICON, cust -\u003e createFoodIcon(cust));\n    \n## Using the componentrenderers addon stylesheet\n\nTo use the componentrenderers automatic addon stylesheet which centers components correctly\ninclude the addons.scss in your theme, have a look at the demotheme:\n\n    @import \"../valo/valo.scss\";\n    @import \"addons.scss\";\n    \n    .demotheme {\n      @include valo;\n      @include addons;\n\n## OSGI\n\nIf you want to add the serverside parts of the renderer as OSGI bundle, you can\nuse the karaf feature (if you use karaf) which installs automatically all needed\ndependencies (including vaadin).\n\n    feature:repo-add mvn:de.datenhahn.vaadin/componentrenderer/0.3.4/xml/features\n    feature:install vaadin-componentrenderer\n    \nOr you use the classic way only installing the componentrenderer bundle:\n    \n    bundle:install -s mvn:de.datenhahn.vaadin/componentrenderer/0.3.4\n    \nTo see all dependencies have a look at the features.xml, most come from vaadin itself:\n\n[features.xml](https://github.com/datenhahn/componentrenderer/blob/master/componentrenderer/src/main/resources/features.xml)\n\n## Keyboard-Navigation\n\nThere are two extensions improving on keyboard navigation.\n\n### ComponentCellKeyExtension\n\nWith the arrow keys you can navigate through the grid, but if you wanted to press a component-button in a cell\nor if you wanted to change a dropdown you had to use the mouse. This extensions adds two new keyboard actions:\n \n * Press ENTER to focus the component inside a cell\n * Press ESC to switch focus back to the cell so you can continue navigating\n  \n### DetailsKeysExtension\n\nYou also want to open/close the details-rows with the keyboard. So this extension adds the following to keyboard actions:\n \n * Press CTRL+DOWN_ARROW to expand the details of the current row\n * Press CTRL+UP_ARROW to collapse the details of the current row and its row above. The grid cursors movement\n   cannot be stopped, so when pressing the DOWN_ARROW the cursor always moves to the next row.\n   The closing of the row above the cursor is a hack to allow flicking open and shut details without\n   having to reposition the cursor)\n\n## Focus Preserving refresh\n\nWhen rerendering the grid looses its focus. I fixed that by suppling functions for saving and restoring\nthe current focus. The ComponentGrid's \u003ctt\u003erefresh()\u003c/tt\u003e function automatically makes use of that.\n\n\u003cb\u003eStandard Grid\u003c/b\u003e\n\n    focusPreserveExtension.saveFocus();\n    grid.setCellStyleGenerator(grid.getCellStyleGenerator());\n    focusPreserveExtension.restoreFocus();\n\n\u003cb\u003eComponentGrid\u003c/b\u003e\n\n    grid.refresh()\n    \nIn case you wonder \u003ctt\u003egrid.setCellStyleGenerator(grid.getCellStyleGenerator());\u003c/tt\u003e is a nasty hack to force the\ngrid to rerender.\n\n## HeaderGenerator\n\nThe header generators make it very easy to generate headers for all bean properties and generated properties. See\nthe ResourceBundleComponentHeaderGenerator for an example.\n\n    componentGridDecorator.generateHeaders(new ResourceBundleTextHeaderGenerator(ViewComponents.getLabels()));\n\nThe method \u003ctt\u003egenerateHeaders\u003c/tt\u003e calls the HeaderGenerators \u003ctt\u003egetHeader\u003c/tt\u003e method for every property and\nsets its header-caption. You should use that method once you have set the columns you want to use.\n\nThere are the following types of HeaderGenerators:\n\n * TextHeaderGenerator\n * HtmlHeaderGenerator\n * ComponentHeaderGenerator\n\n# Server-Side Memory Use\n\nI used jvisualvm to check if the components were released correctly. Below you find the screenshots.\n\nLater on I added an integration test, see: de.datenhahn.vaadin.componentrenderer.testbench.MemoryIT\n\nrun e.g. MemoryIT#profileFirefox(), you can also play around with different setups if you modifiy the\ngrid loaded for testbench test (see method\nde.datenhahn.vaadin.componentrenderer.demo.ComponentRendererDemoUI.startTestBenchApp()).\n\n## Initial Load\n\nThe grids initially displayed rows are loaded, memory use about 50MB.\n\n\u003cimg src=\"./doc/jvisualvm/after_initial_load.jpg\"\u003e\n\n## Scroll to row 1000 (ARROW_DOWN key) before GC\n\nDuring scrolling we create a lot of objects, memory use before GC about 400MB.\n\n\u003cimg src=\"./doc/jvisualvm/scroll_to_1000_before_gc.jpg\"\u003e\n\n## Scroll to row 1000 (ARROW_DOWN key) after GC\n\nBut the generated objects do not stay in heap, but can be garbage collected which we can\nobserve here. After GC again only about 50MB of memory is used. The number of instances\nleft (100) is the amount the grid's RpcDataSource holds in its row-cache.\n\n\u003cimg src=\"./doc/jvisualvm/scroll_to_1000_after_gc.jpg\"\u003e\n\n\n# Internals of the renderer explained\n\nHere you find a description how the renderer works. You won't need that to use the renderer, but\nit may be useful if you want to extend it or when writing your own renderers.\n\n## Grid basics for writing renderers\n\nBefore starting to extend the grid I recommend watching this video. Leif explain very detailed\nthe mechanics of the grid.\n\n\u003cb\u003eGWT.create 2015 - Improving the HTML table (Leif Åstrand)\u003c/b\u003e\n\n\u003chttps://www.youtube.com/watch?v=Yi8ioIHTnZw\u003e\n\nOther resources\n\n * \u003cb\u003eBook of Vaadin: Renderers\u003c/b\u003e \u003chttps://vaadin.com/docs/-/part/framework/clientsidewidgets/clientsidewidgets-grid.html\u003e\n * \u003cb\u003eBlog-Post: Vaadin 7.5 - Grid Extensions\u003c/b\u003e \u003chttps://vaadin.com/blog/-/blogs/vaadin-7-5-grid-extensio-1\u003e\n\n### ItemId, RowIndex and RowKey explained\n\nWhen programming for the grid you will encounter these different keys for identifying rows. Be sure to convert\ncorrectly when you do not receive the type of key you need.\n\n * the \u003cb\u003eitemId\u003c/b\u003e is the id of the serverside vaadin container, it may be a bean for example when using the\n   BeanItemContainer\n * the \u003cb\u003erowIndex\u003c/b\u003e is the index of an IndexedContainer (all grid containers have to implement Container.Indexed)\n * the \u003cb\u003erowKey\u003c/b\u003e is an internal row key of the clientside grid implementation\n\n\u003cb\u003eConversions\u003c/b\u003e\n\n * \u003cb\u003erowKey -\u003e itemId\u003c/b\u003e the AbstractGridExtension contains a method getItemId(String rowKey)\n * \u003cb\u003erowIndex -\u003e itemId\u003c/b\u003e Container.Indexed contains a method getItemIds(int startIndex, int numberOfItems)\n   if you do getItemIds(rowIndex,1).get(0) you get the itemId of the rowIndex\n\n## Lifecycle of rendering a cell\n\nNow you should have a basic understanding how the grid's internals work and we will look into the lifecycle\nof rendering a cell.\n\n 1. \u003cb\u003eclient-side\u003c/b\u003e: the grid is in need for data (first load, or scrolling)\n 2. the grid requests the needed range of rows from its DataSource, which in the default implementation is\n    the RpcDataSource.\n 3. the RpcDataSource makes a request to its server-side counterpart\n 4. \u003cb\u003eserver-side\u003c/b\u003e: for each column the associated renderer is asked to encode the data from the container\n 5. the renderer uses \u003ctt\u003egetValue()\u003c/tt\u003e to retrieve the serverside java value object which it will encode to json\n 6. before receiving the value converters are applied to convert from data model to presentation form\n 7. the renderer's serverside \u003ctt\u003eencode(T value)\u003c/tt\u003e function is called. T is the presentation type this renderer supports.\n 8. (since 7.6) if any DataGenerators are registered with the grid the DataGenerator its \u003ctt\u003egenerateData(Object itemId, Item item, JsonObject jsonObject)\u003c/tt\u003e function is called.\n    the fully json encoded row is passed to this function and additional modifications can be made by the DataGenerator (e.g. add\n    additional information)\n 9. \u003cb\u003eclient-side\u003c/b\u003e: the renderer's client-side connector retrieves the json value it should display and uses the \u003ctt\u003edecode(JsonValue jsonConnectorId)\u003c/tt\u003e\n    function of its connector.\n 10. now the client-side renderer's \u003ctt\u003erender(...)\u003c/tt\u003e function is called. It gets passed the cell-reference and the\n     value it is supposed to display as returned from the \u003ctt\u003edecode(...)\u003c/tt\u003e function. When you are using a WidgetRenderer\n     you also have access to the widget contained in the cell.\n 11. \u003cb\u003ethe row scrolls out of sight and out of the row-cache\u003c/b\u003e\n 12. the row's data which is cached on the client-side is destroyed\n 13. \u003cb\u003eserver-side\u003c/b\u003e(since 7.6) if DataGenerators are registered with the grid the \u003ctt\u003edestroyData(Object itemId)\u003c/tt\u003e function of the\n     DataGenerator is called.\n\n## The complete rendering lifecycle of the component renderer\n\n\u003ca target=\"_blank\" href=\"./doc/ComponentRenderer_How_it_works.png\"\u003e\n    \u003cimg src=\"./doc/ComponentRenderer_How_it_works.png\" width=\"900\"\u003e\n\u003c/a\u003e\n\n### Problems I encountered (with vaadin 7.5)\n 1. although the grid implements \u003ctt\u003eHasComponents\u003c/tt\u003e it does keep track of components in its hierarchy in its own way,\n   meaning if you do \u003ctt\u003esetParent(grid)\u003c/tt\u003e on a component it will \u003cb\u003enot\u003c/b\u003e be added to the grid's child\n   components and therefore it will have no effect (not added to the hierarchy) :(\n   \u003ci\u003eI circumvented that in my first trys by keeping of the renderer-components myself and overwriting the\n   \u003ctt\u003eiterator()\u003c/tt\u003e method of the grid (either by extending from Grid, or by a cglib-decorator) adding my\n   renderer components to the grid's own tracked components\u003c/i\u003e\n 2. Adding components to the hierarchy slows down the component. So a grid with 1000 rows worked, but with 10000 rows\n   it got barely useable, that was in chrome. Firefox seems not to do some optimisations or is in general much\n   slower in measuring/preventing reflow. It was killed already by several hundreds of rows.\n   \u003cb\u003eConclusion: you want to keep as little components in the hierarchy as possible\u003c/b\u003e\n 3. When vaadin renders a component with relative size it determines all sizes of the components involved and does\n   some calculations to translate the percent values into absolute values. That is part of the vaadin magic. Unfortunately\n   measuring an element causes the browser to reflow/relayout, doing it a lot of time results in a severe slowing-down\n   effect called \"layout thrashing\" (https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing).\n   The vaadin core is already highly optimized to minimize reflow, but some browsers are better (chrome) than others\n   (firefox) in measuring elements and optimizing reflow.\n 4. Whereas you have a clear entrypoint when components have to be available to the renderer and therefore added to the\n   hierarchy (\u003ctt\u003eencode()\u003c/tt\u003e) there was no possibility to determine when it is not needed anymore.\n   \u003ci\u003eI tried to circumvent that by keeping track of the components widgets (if a widget in a cell was replaced with\n   a different one I could throw the component away). The destruction of the component had to be made on the server-side\n   so a server-side-rpc was necessary leading to rpc-spamming. Caching of the list of disposable components did leverage\n   that a bit with the downside of having a bit more components in the hierarchy than necessary.\u003c/i\u003e\n 5. The renderer's \u003ctt\u003eencode()\u003c/tt\u003e function only gets passed the value but has no information about its context\n   (e.g. the rowId).\n   \u003ci\u003eI used some homemade component tracking using ItemSet and PropertySet listeners. Let's just say, it was quite\n   painful.\u003c/i\u003e\n\nBeside all these problems, the 7.5 version of the renderer worked already extremly well in chrome and was useable\nin firefox if not too many rows were used (up to 1000).\n\n### Vaadin 7.6: The brave new world\n\nLuckily these problems also were noticed at vaadin, so Teemu added some pretty neat features to the grid, which made\nmost of my workarounds unnecessary. Joining the Vaadin Boattrip to JFokus I had the chance to meet him in person and\nduring the conference whenever there was time we fixed the renderer to use the new features.\n\n 1. \u003cb\u003eHasComponents-Workaround\u003c/b\u003e. 7.6 grid contains the functions \u003ctt\u003eaddComponentToGrid(Component component)\u003c/tt\u003e\n  and \u003ctt\u003eremoveComponentFromGrid(Component component)\u003c/tt\u003e which allow to add own components to the grids hierarchy. \n 2. \u003cb\u003eBig Hierarchy Problem\u003c/b\u003e Remember the conclusion: you want to keep as little components in the hierarchy as possible. With\n    the introduction of \u003ctt\u003eDataGenerator\u003c/tt\u003e this is quite easy (see below).\n 3. \u003cb\u003eMeasurement/Reflow-Problem\u003c/b\u003e not solved, but if you set fixed sizes on your component\n    the browser does not need to measure.\n 4. \u003cb\u003eComponent-Destruction Workaround\u003c/b\u003e \u003ctt\u003eDataGenerator\u003c/tt\u003e again saves the day. Its \u003ctt\u003edestroy(...)\u003c/tt\u003e function\n    is the hook I was waiting for.\n 5. \u003cb\u003eRenderer Context Workaround\u003c/b\u003e \u003ctt\u003eDataGenerator\u003c/tt\u003e again it is. The \u003ctt\u003egenerateData(...)\u003c/tt\u003e function is\n    aware of its context.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatenhahn%2Fcomponentrenderer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatenhahn%2Fcomponentrenderer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatenhahn%2Fcomponentrenderer/lists"}