On Gwt TreeViewModel getNodeInfo() method - gwt

I can't understand that part, neither trying the showcase examples.
I'm using an extension of AsyncDataProvider to bind my tree to RPC service. Here's my method:
public <T> NodeInfo<?> getNodeInfo(T value) {
/*
if (value instanceof Categoria) {
dataProvider.setCurrentParent((Categoria)value);
}
*/
return new DefaultNodeInfo<Categoria>(dataProvider, new CategoriaCell());
}
"currentParent" is my stuff: except for (null => root) values, I set the parent to pass via RPC to my service. Actually, in my widget code:
dataProvider = new CategorieTreeDataProvider() {
#Override
protected void onRangeChanged(HasData<Categoria> display) {
updateTree(getCurrentParent());
}
};
private void updateTree(Categoria categoria) {
rpcService.getCategorie(categoria, new AsyncCallback<Categoria[]>() {
#Override
public void onSuccess(Categoria[] result) {
dataProvider.updateRowCount(result.length, true);
dataProvider.updateRowData(0, Arrays.asList(result));
}
#Override
public void onFailure(Throwable caught) {
Window.alert(caught.toString());
}
});
}
My rpc-server code, however, is working as expected:
#Override
public Categoria[] getCategorie(Categoria parent) {
List<Categoria> categoryList = categorieDao.listByProperty("parent", parent);
for (Categoria c : categoryList) {
if (categorieDao.listByProperty("parent", c).size() == 0) {
c.setLeaf(true);
}
}
return categoryList.toArray(new Categoria[0]);
}
**Then I add some data to my Categories: 'GrandFather', 'Father' and 'Son'.
Unfortunately, after loading my widget, I see:
The grandfather correctly, with his "+" how expected;
Then I click it and...
The grandfather disappear and I see 'Father' with his '+'
same for father -> son
I suspect the bug is in updateRowCount / updateRowData usage.**
Any ideas?

The getNodeInfo is called whenever you open a node so you have to create distinct DataProvider for each of the nodes's childs.
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == null) {
return new DefaultNodeInfo<Category>(dataProvider, new CategoriaCell());
}
else if (value instanceof Categoria) {
Category category = (Category)value;
return new DefaultNodeInfo<Grandfather>(new ListDataProvider<Grandfather>(category.getGrandFathers()),new GrandFatherCell());
}
else if (value instanceof Grandfather) {
Grandfather grandfather = (Grandfather)value;
return new DefaultNodeInfo<Father>(new ListDataProvider<Father>(granfather.getFathers()),new FatherCell());
}
else if (value instanceof Father) {
//same as above but with fathers.
}
}
The category.getGrandFathers() function can for example do a RPC request to the server or just return the list if you retrieve everything in one RPC request.
UPDATE based on comment:
So in case you have only one class and want to achieve a dynamic CellTree (number of levels are not pre-determined) you could take following approach.
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == null) {
return new DefaultNodeInfo<Category>(dataProvider, new CategoriaCell());
}
else {
Category category = (Category)value;
return new DefaultNodeInfo<Category>(new ListDataProvider<Category>(category.getSubCategories()),new CategoryCell());
}
}
category.getSubCategories() is either an RPC call which retrieves the subcategories for the current category or if the Category class is a linked list type datastructure it could just return the list of subcategories.

Each data provider updates a given "list" (child nodes of a given parent node), so you have to use a distinct data provider instance for each parent node, or your calls will update some random list.

Related

AEM - Predicate to list only immediate children

I am using aem-commons's contextualPathBrowser (similar to pathfiled component) in a cq dialog with a predicate property. The java predicate class will evaluate the nodes and only return children pages(cq:Page) in the path picker for user to select from. Predicate class given below:
#Component(
service = Predicate.class,
property = {
"predicate.name=pagePathPredicate"
}
)
public class PagePathPredicate extends AbstractNodePredicate {
#Override
public boolean evaluate(final Node node) throws AccessDeniedException, ItemNotFoundException, RepositoryException {
try {
return isInPredicate(node);
} catch (RepositoryException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
private boolean isInPredicate(final Node node) throws RepositoryException {
if (node.getProperty("jcr:primaryType").getString().equals("cq:Page")) {
return true;
}
return false;
}
}
The code works well and list only node with jcr:primaryType = cq:Page. However, finding it difficult to list only children pages of the page user is currently on, as I am not sure how to fetch the current resource/node/page in the Predicate class.

GXT 3 GridRowEditing SimpleComboBox entries not displayed

i'm currently using a GXT3 grid to display data from a custom object EntityDAO.
This class contains 3 attributes: an id and two references to complex type objects
Let's call them
Long id;
UserInfo userInfo;
OutputInfo outputInfo;
I created an interface to explicit the desired display of these info:
interface EntityDAOProperties extends PropertyAccess<EntityDAO> {
ModelKeyProvider<EntityDAO> id();
#Path("userInfo.name")
ValueProvider<EntityDAO, String> step();
#Path("outputInfo.name")
ValueProvider<EntityDAO, String> outputInfo();
}
The display is perfectly fine. The matter is that i want to be able to add/edit rows to my grid.
To do so, I have a
GridRowEditing<EntityDAO> editing = createGridEditing(grid);
comprising a
SimpleComboBox<String> comboUser = new SimpleComboBox<String>(new LabelProvider<String>() {
#Override
public String getLabel(String item) {
return item;
}
});
for(...){
comboUser.add("entry " + i); // For instance
logger.info("entry : " +i); // For instance
i++;
}
comboUser.setEditable(false);
comboUser.setTriggerAction(TriggerAction.ALL);
When i double click on my line and make the GridRowEditing appear, the combo doesn't seem to have more than 1 row and the click on the expand arrow doesn't change anything to the matter.
I think you miss the part where you set the property editor for the combobox, here is the example code:
SimpleComboBox<Light> combo = new SimpleComboBox<Light>(new StringLabelProvider<Light>());
combo.setClearValueOnParseError(false);
combo.setPropertyEditor(new PropertyEditor<Light>() {
#Override
public Light parse(CharSequence text) throws ParseException {
return Light.parseString(text.toString());
}
#Override
public String render(Light object) {
return object == null ? Light.SUNNY.toString() : object.toString();
}
});
combo.setTriggerAction(TriggerAction.ALL);
combo.add(Light.SUNNY);
combo.add(Light.MOSTLYSUNNY);
combo.add(Light.SUNORSHADE);
combo.add(Light.MOSTLYSHADY);
combo.add(Light.SHADE);
// combo.setForceSelection(true);
editing.addEditor(cc2, new Converter<String, Light>() {
#Override
public String convertFieldValue(Light object) {
return object == null ? "" : object.toString();
}
#Override
public Light convertModelValue(String object) {
try {
return Light.parseString(object);
} catch (ParseException e) {
return null;
}
}
}, combo);
Hope this could help you.

How do I tell a GWT cell widget data has changed via the Event Bus?

I have a GWT Cell Tree that I use to display a file structure from a CMS. I am using a AsyncDataProvider that loads data from a custom RPC class I created. I also have a Web Socket system that will broadcast events (File create, renamed, moved, deleted etc) from other clients also working in the system.
What I am trying to wrap my head around is when I recieve one of these events, how I correctly update my Cell Tree?
I suppose this problem would be analogus to having two instances of my Cell Tree on the page, which are presenting the same server-side data and wanting to ensure that when the user updated one, that the other updated as well, via using the EventBus.
I feel this should be pretty simple but I have spent about 6 hours on it now with no headway. My code is included below:
NOTE: I am not using RequestFactory even though it may look like I am it is my custom RPC framework. Also, FileEntity is just a simple representation of a file which has a name accessible by getName().
private void drawTree() {
// fileService is injected earlier on and is my own custom rpc service
TreeViewModel model = new CustomTreeModel(new FileDataProvider(fileService));
CellTree tree = new CellTree(model, "Root");
tree.setAnimationEnabled(true);
getView().getWorkspace().add(tree);
}
private static class CustomTreeModel implements TreeViewModel {
// I am trying to use a single AsyncDataProvider so I have a single point of loading data which I can manipulate (Not sure if this is the correct way to go)
public CustomTreeModel(FileDataProvider dataProvider) {
this.provider = provider;
}
public <T> NodeInfo<?> getNodeInfo(final T value) {
if (!(value instanceof FileEntity)) {
// I already have the root File loaded in my presenter, if we are at the root of the tree, I just add it via a list here
ListDataProvider<FileEntity> dataProvider = new ListDataProvider<FileEntity>();
dataProvider.getList().add(TreeWorkspacePresenter.rootFolder);
return new DefaultNodeInfo<FileEntity>(dataProvider,
new FileCell());
} else {
// Otherwise I know that we are loading some tree child data, and I invoke the AsyncProvider to load it from the server
provider.setFocusFile(value);
return new DefaultNodeInfo<FileEntity>(provider,
new FileCell());
}
}
public boolean isLeaf(Object value) {
if(value == null || value instanceof Folder)
return false;
return true;
}
}
public class FileDataProvider extends AsyncDataProvider<FileEntity> {
private FileEntity focusFile;
private FileService service;
#Inject
public FileDataProvider(FileService service){
this.service = service;
}
public void setFocusFile(FileEntity focusFile){
this.focusFile = focusFile;
}
#Override
protected void onRangeChanged(HasData<FileEntity> display) {
service.getChildren(((Folder) focusFile),
new Reciever<List<FileEntity>>() {
#Override
public void onSuccess(List<FileEntity> files) {
updateRowData(0, files);
}
#Override
public void onFailure(Throwable error) {
Window.alert(error.toString());
}
});
}
}
/**
* The cell used to render Files.
*/
public static class FileCell extends AbstractCell<FileEntity> {
private FileEntity file;
public FileEntity getFile() {
return file;
}
#Override
public void render(Context context, FileEntity file, SafeHtmlBuilder sb) {
if (file != null) {
this.file = file;
sb.appendEscaped(file.getName());
}
}
}
Currently there is no direct support for individual tree item refresh even in the latest gwt version.
But there is a workaround for this. Each tree item is associated with an value. Using this value you can get the corresponding tree item.
In your case, i assume, you know which item to update/refresh ie you know which File Entity has changed. Use this file entity to search for the corresponding tree item. Once you get the tree item you just need to expand and collapse or collapse and expand its parent item. This makes parent item to re-render its children. Your changed file entity is one among the children. So it get refreshed.
public void refreshFileEntity(FileEntity fileEntity)
{
TreeNode fileEntityNode = getFileEntityNode(fileEntity, cellTree.getRootTreeNode()
// For expnad and collapse run this for loop
for ( int i = 0; i < fileEntityNode.getParent().getChildCount(); i++ )
{
if ( !fileEntityNode.getParent().isChildLeaf( i ) )
{
fileEntityNode.getParent().setChildOpen( i, true );
}
}
}
public TreeNode getFileEntityNode(FileEntity fileEntity, TreeNode treeNode)
{
if(treeNode.getChildren == null)
{
return null;
}
for(TreeNode node : treeNode.getChildren())
{
if(fileEntity.getId().equals( node.getValue.getId() ))
{
return node;
}
getEntityNode(fileEntity, node);
}
}
You can use the dataprovider to update the celltree.
You can update the complete cell tree with:
provider.setList(pList);
provider.refresh();
If you want to update only a special cell you can get the listwrapper from the dataprovider and only set one element.
provider.getList().set(12, element);

GWT Cell Tree - leaf nodes

I'm using a cell tree and I have this problem:
I get the data via RPC calls. I decide if a node is a leaf or not - based on the data that I get for its children. For example - if a node has a son called "foo" - then this node should be a leaf.
I don't know how to make this node to be a leaf and not to show its children on the tree. (instead, I want to show them somewhere else, when clicking on the node)
Is it possible? Does anyone have an idea?
Please help me, I'm stuck with it for 2 days...
Thanks!
You can override isLeaf() method to return true or false.
There will be a problem, however, from the UI perspective. Before a user clicks on a node, you don't know if this should be a node or a leaf. This is a little confusing, although I saw such implementations more than once. If your tree is not very large, consider loading all data at once, and then building it the way you want - creating nodes or leafs as necessary.
If each node has a type, could you create some list or map of types that aren't expected to have children in your TreeViewModel impl?
In an impl I did I used a meta-model for all types, but it's not a requirement.
E.g.,
#Override
public boolean isLeaf(Object value) {
boolean result = true;
if (value == null) {
result = false; // assumes all root nodes have children
} else if (value instanceof NavNode) {
final NavNode currentNode = (NavNode) value;
final NodeType currentNodeType = NodeType.fromValue(currentNode.getType());
if (currentNode.hasChildren() || NodeHelper.couldHaveChildren(currentNodeType)) {
result = false;
}
}
return result;
}
// Create a data provider for root nodes
protected ListDataProvider<NavNode> getDataProvider(Collection<NavNode> rootNodes) {
return new ListDataProvider<NavNode>(new LinkedList<NavNode>(rootNodes));
}
// Create a data provider that contains the immediate descendants.
protected AsyncDataProvider<NavNode> getDataProvider(final NavNode node) {
return new AsyncDataProvider<NavNode>() {
#Override
protected void onRangeChanged(final HasData<NavNode> display) {
final Set<NavNode> clientNodes = util.getAncestorNodes(node);
clientNodes.add(node);
final NavigationInfo clientInfo = new NavigationInfo(clientNodes);
navigationService.getNavInfo(clientInfo, node, resources, qualifications, new SafeOperationCallback<NavigationInfo>(eventBus, false) {
#Override
public void onFailureImpl(Throwable caught) {
GWT.log("Something went wrong retreiving children for " + node.getName(), caught);
updateRowCount(0, false);
}
#Override
public void onSuccessImpl(OperationResult<NavigationInfo> or) {
util.mergeNavInfo(or.getResult());
final NavNode nodeFromServer = util.getNode(node.getId());
final Range range = display.getVisibleRange();
final int start = range.getStart();
final Set<NavNode> nodes = util.getNodes(nodeFromServer.getChildren());
updateRowData(display, start, new LinkedList<NavNode>(nodes));
}
});
}
};
}
private static class NodeHelper {
private static final Set<NodeType> PARENTAL_NODE_TYPES;
static {
PARENTAL_NODE_TYPES = new HashSet<NodeType>();
PARENTAL_NODE_TYPES.add(NodeType.ASSET_OWNER);
PARENTAL_NODE_TYPES.add(NodeType.OPERATING_DAY);
PARENTAL_NODE_TYPES.add(NodeType.RESOURCES);
PARENTAL_NODE_TYPES.add(NodeType.RESOURCE);
PARENTAL_NODE_TYPES.add(NodeType.ENERGY);
PARENTAL_NODE_TYPES.add(NodeType.RESERVE);
PARENTAL_NODE_TYPES.add(NodeType.DAY_AHEAD_CLEARED_OFFERS);
PARENTAL_NODE_TYPES.add(NodeType.DRR_LOAD_FORCAST);
PARENTAL_NODE_TYPES.add(NodeType.RESERVE_DISPATCH);
PARENTAL_NODE_TYPES.add(NodeType.RESERVE_RAMP_RATE);
}
public static boolean couldHaveChildren(NodeType nodeType) {
boolean result = false;
if (PARENTAL_NODE_TYPES.contains(nodeType)) {
result = true;
}
return result;
}
}
}

GWT CEll Browser Real Time Update

has someone been able to correctly to update a cell browser at runtime, i.e. when u remove a node or add a node, the change is reflected immediately in the CEll Browser, because I am using a List and when i am making a change it is not being updated on the spot
You can use ListDataProvider setList(...) method for dynamic updates. Here is an example how I update cell browser via RPC:
private void loadAllData(final ListDataProvider<Data> dataProvider) {
dBservice.getAllData(new AsyncCallback<List<Data>>() {
public void onSuccess(List<Data> result) {
dataProvider.setList(result);
}
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
});
}
to refresh a cellBrowser you have to close all the child on the root node.
anyway something like this
for (int i = 0; i < cellBrowser.getRootTreeNode().getChildCount(); i++) {
cellBrowser.getRootTreeNode().setChildOpen(i, false);
}
the AsyncDataProvider calls refreshes data
private final class Model implements TreeViewModel{
private List<ZonaProxy> zonaList = null;
private List<CategoriaProxy> categoriaList = null;
public void setCategoriaList(List<CategoriaProxy> categoriaList) {
this.categoriaList = categoriaList;
}
public void setListZona(List<ZonaProxy> zonaList) {
this.zonaList = zonaList;
}
#SuppressWarnings({ "unchecked", "rawtypes" })
public <T> NodeInfo<?> getNodeInfo(T value) {
CategoryDataProvider dataProvider1 = new CategoryDataProvider();
return new DefaultNodeInfo(dataProvider1, new CategoriaCell());
}
/**
* Check if the specified value represents a leaf node. Leaf nodes cannot be
* opened.
*/
public boolean isLeaf(Object value) {
if (value instanceof CategoriaProxy){
if (((CategoriaProxy) value).getLivello() == 3) {
return true;
}
}
return false;
}
}
private class CategoryDataProvider extends AsyncDataProvider<CategoriaProxy>
{
#Override
protected void onRangeChanged(HasData<CategoriaProxy> display)
{
requests.categoriaRequest().findAllCategorias(0, 8).with().fire(new Receiver<List<CategoriaProxy>>() {
#Override
public void onSuccess(List<CategoriaProxy> values) {
updateRowCount(values.size(), true);
updateRowData(0, values);
}
});
}
}
it Works.
Apparently it is not enough to change the data provider and refresh it.
You need also to force the affected cell to close and reopen it, as in this example
public void updateCellBrowser(String id) {
TreeNode node = getNode(cellBrowser.getRootTreeNode(),id);
if(node != null && ! node.isDestroyed()) {
TreeNode parent = node.getParent();
int index = node.getIndex();
parent.setChildOpen(index, false,true);
parent.setChildOpen(index, true, true);
}
}
In my particular example the cell ids are pathnames hence the following
implementation of getNode().
private TreeNode getNode(TreeNode node, String id) {
for(int i=0; i < node.getChildCount(); i++)
if(node.isChildOpen(i)) {
Object value = node.getChildValue(i);
if(value instanceof String) {
String nodeId = ((String) value);
if(id.equals(nodeId))
return node.setChildOpen(i, true);
if(id.startsWith(nodeId))
getNode(node.setChildOpen(i, true),id);
}
}
return null;
}