private TreeExternal<?> getMenuItemTree() {
return Externals.tree()
.sandboxTrackable(MenuChangeContainers.MENU_ITEM)
.translatable()
.label("menu-item.tree-view")
.itemDisplayNameKey(MenuItemProps.LABEL)
.itemIdKey(MenuItemProps.ID)
.itemParentIdKey(MenuItemProps.PARENT_MENU_ITEM_ID)
.itemTypeLabel("menu-item.tree-view.item-type")
.createForm(this::getMenuItemCreateForm)
.updateForm(MenuForms.UPDATE, this::getMenuItemUpdateForm)
.grid(this::getMenuItemGrid)
.apply(this::addMenuItemEndpoints);
}
/**
* This form defines the fields to create a new MenuItem belonging to the current Menu. It's like a
* miniature Create View. Other types of external collection fields like an `GridExternal` will
* render this form in a modal, but for the `TreeExternal`, it renders inline with the tree-
* hierarchy navigator (like with Categories).
*/
private FormView<?> getMenuItemCreateForm(FormView<?> menuItemCreateForm) {
return menuItemCreateForm
.label("menu-item.forms.create")
.addField(MenuItemProps.LABEL, Fields.string()
.label("menu-item.fields.label")
.translatable()
.required()
.order(1000))
.addField(MenuItemProps.TYPE, Fields.select()
.label("menu-item.fields.type")
.helpText("menu-item.fields.type.help-text")
.options(MenuItemTypeOptionEnum.toOptions())
.defaultValue(MenuItemTypeOptionEnum.LINK.name())
.required()
.order(2000))
.addField(MenuItemProps.URL, getDynamicMenuItemUrlField()
.order(3000))
.addField(MenuItemProps.DISPLAY_ORDER, Fields.integer()
.label("menu-item.fields.display-order")
.order(4000));
}
/**
* Similar to the MenuItem Create Form, we'll add an Update for editing existing items.
*/
private FormView<?> getMenuItemUpdateForm(FormView<?> menuItemUpdateForm) {
return menuItemUpdateForm
.label("menu-item.forms.update")
.addField(MenuItemProps.LABEL, Fields.string()
.label("menu-item.fields.label")
.translatable()
.required()
.order(1000))
.addField(MenuItemProps.TYPE, Fields.select()
.label("menu-item.fields.type")
.helpText("menu-item.fields.type.help-text")
.options(MenuItemTypeOptionEnum.toOptions())
.defaultValue(MenuItemTypeOptionEnum.LINK.name())
.required()
.order(2000))
.addField(MenuItemProps.URL, getDynamicMenuItemUrlField()
.order(3000))
.addField(MenuItemProps.DISPLAY_ORDER, Fields.integer()
.label("menu-item.fields.display-order")
.order(4000))
.addField(MenuItemProps.IMAGE_URL, Fields.string()
.label("menu-item.fields.image-url")
// add more complex validation for this field in the frontend
.validationMethod(
// choose a method and pass in the error message
ValidationMethods.urlPath("menu-item.fields.image-url.validation"))
.order(5000))
.addField(MenuItemProps.IMAGE_ALT_TEXT, Fields.string()
.label("menu-item.fields.image-alt-text")
.translatable()
.order(6000))
.addField(MenuItemProps.CUSTOM_HTML, Fields.html()
.label("menu-item.fields.custom-html")
.helpText("menu-item.fields.custom-html.help-text")
.order(7000));
}
private TreeGridView<?> getMenuItemGrid(TreeGridView<?> menuItemGrid) {
return menuItemGrid
// read endpoint defined by parent tree: #addMenuItemEndpoints
.sortable()
.filterByTextQuery(filter -> filter.label("menu-item.tree-view.grid.search"))
.openDetails("menu-item.tree-view.grid.open", MenuScopes.MENU)
.addColumn(MenuItemProps.LABEL, Columns.string()
.label("menu-item.columns.label")
.order(1000))
.addColumn(MenuItemProps.URL, Columns.string()
.label("menu-item.columns.url")
.order(2000));
}
private void addMenuItemEndpoints(TreeExternal<?> menuItemTree) {
menuItemTree
.readRootItemsEndpoint(endpoint -> endpoint
.uri(MENU_ITEMS)
.scope(MenuScopes.MENU)
.param("rootsOnly", "true"))
.readChildrenItemsEndpoint(endpoint -> endpoint
.uri(MENU_ITEM_CHILDREN)
.scope(MenuScopes.MENU))
.readAncestorsEndpoint(endpoint -> endpoint
.uri(MENU_ITEM_ANCESTORS)
.scope(MenuScopes.MENU))
.readGridItemsEndpoint(endpoint -> endpoint
.narrowedPaging()
.uri(MENU_ITEMS)
.scope(MenuScopes.MENU)
.param("rootsOnly", "false")
.mapParam(Mappings.mapValue("sort.sort", "sort")))
.readItemEndpoint(endpoint -> endpoint
.uri(MENU_ITEM)
.scope(MenuScopes.MENU))
.createItemEndpoint(endpoint -> endpoint
.uri(MENU_ITEMS)
.scope(MenuScopes.MENU))
.updateItemEndpoint(endpoint -> endpoint
.uri(MENU_ITEM)
.scope(MenuScopes.MENU))
.deleteItemEndpoint(endpoint -> endpoint
.uri(MENU_ITEM)
.scope(MenuScopes.MENU))
.updateTranslationsEndpoint(endpoint -> endpoint
.uri(MENU_ITEM_TRANSLATIONS)
.scope(MenuScopes.MENU));
}