GWT Activities: Widget added to view again when using browser back button - gwt

The way I understand it, if I want to add widgets dynamically to a view created with UIBinder, I would do that in the start method of the activity that is the presenter for that view.
Here's my code:
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
view = clientFactory.getDashboardView();
view.setPresenter(this);
ArrayList<Department> deps = ModelFactory.getDepartments();
view.passData(deps); // Correct?
panel.setWidget(view.asWidget());
}
public void passData(ArrayList<Department> departments) {
TextCell text = new TextCell();
CellList<String> cellList = new CellList<String>(text);
String[] departmentNames = new String[departments.size()];
for (int i = 0; i < departments.size(); i++) {
departmentNames[i] = departments.get(i).getName();
}
cellList.setRowData(Arrays.asList(departmentNames));
departmentsDiv.add(cellList);
}
It works. However, when using the back button to navigate to the previous place and back, the widget is added again.
How do I handle this correctly?

You have two options:
If you don't want to refresh the data on each visit to this view, you need to add a flag to the view to tell if the data has been already populated. Then, when this view is visited again, your activity should call view.passData(deps); only if the flag is set to false. After the data is loaded, set the flag to true.
If you do want to refresh the data on each visit, call departmentsDiv.clear() before adding a new CellList.
NB: A better approach is to create your CellList once, when the view is displayed for the first time, and then only call setRowData when the new data is available.

You are creating View object by using Factory method. You should consider creating views during application load using, for instance: GIN and marking them as Singletons. The proper way is to pass them as start() method parameters and just set presenter reference on them.
General idea is to make Views singletons. Activities should be created while throwing GWT place (stateless) and just using singleton Views, so you can keep your view input data.
Read tutorial here on using MVP / GIN pattern: http://blog.hivedevelopment.co.uk/2009/08/google-web-toolkit-gwt-mvp-example.html

Related

GWT Detect DOM changes or modifications

What am I trying to do?
I have an existing page (generated by system automatically and I don't have any control on it) in which I am injecting GWT code to modify the behaviour of the page after it loads based on certain columns and augment the functionality of the page. For example after adding my GWT code, cells in one of the table columns become clickable and when the user clicks it, additional information is displayed to the user in a pop-up panel. All that is working fine.
What is the issue?
The generic page in which I am injecting my code has paginated table which shows 15 rows at a time. Now, when I load/refresh the page, my GWT code kicks in and sinks events in the specific column which adds functionality (described above) to the cells. However, when the user uses the left and right buttons to navigate the paginated result, the page does not refresh as it is an asynchronous call. The specific column in the new set of 15 rows is now without the sunk events as the GWT code does not know that the page changed.
I am trying to find a way to tell my GWT code that page has changed and it should sink events to the cells of specific column for the new 15 rows but unable to find any method or mechanism to help me capture a DOM/Document change event. I tried doing this but did not help:
new ChangeHandler(){
#Override
public void onChange(ChangeEvent event) {
Window.alert("Something Changed");
}
It is possible I am missing something very obvious. Posting this question to know if there is an easy way to figure out DOM changes in GWT. Have searched for DOM/Document change/mutation/ etc. without luck.
If anyone knows how to detect DOM changes in GWT would really appreciate otherwise would go ahead writing native code using native mutation observers.
You can try something like this:
First get the input elements with:
InputElement goOn = DOM.getElementById("IdOfGoOnButton").cast();
InputElement goBack = DOM.getElementById("IdOfGoBackButton").cast();
Next add a native EventHandler:
Event.addNativePreviewHandler(new Event.NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(Event.NativePreviewEvent event) {
if (event.getTypeInt() == Event.ONCLICK) {
if (event.getNativeEvent()
.getEventTarget() != null) {
Element as = Element.as(event.getNativeEvent()
.getEventTarget());
if (as.getTagName()
.toLowerCase()
.equals("input")) {
InputElement clickedElement = as.cast();
if (clickedElement.getId().equals(goOn.getId()) ||
clickedElement.getId().equals(goBack.getId())) {
// one of the paging button is pressed
}
}
}
}
}
});
Hope that helps.

How to retrieve view from controller of another view?

I am just wondering how can I get one of the UI5 views in an application.
I know there is a method:
sap.ui.jsview(); // in case the view is written in JavaScript
But the problem with this method is: if you assign ID for any of the controls and you have already inflated this view, you get an error.
So I want to know how to check if the view already exists and then if yes return that existing view, otherwise create the view with the corresponding API such as the above one.
I also know in the control for view I can go
this.getView();
But as I said, how to get this view from another view?
I am not quite understanding your question
With managed object id's are unique, so if you try and create the same view twice you will get an error.
when you create your view the easiest way to access it is via an Id
sap.ui.jsview("view1",'testapp.view.view1');
sap.ui.getCore().byId('view1');
NB. views should not talk to anyone other than their controller A terrific Model View Controller (MVC) diagram
sap.ui.getCore().byId(<id_of_the_view>)
Where <id_of_the_view> can be obtained in the following way:
suppose that the corresponding controller of <id_of_the_view> is "controllerA.js",
then you can console.log, inside controllerA.js,
console.log(this.getView())
This will print you an object which contains the id of such view. This will be <id_of_the_view>
I think here is one solution.
Make a global variable;
Use it to create element.
In First View:
var mytextField ;(use it as global)
mytextField = new sap.ui.commons.TextField('textfieldId');
In Second View:
var myValue = mytextField .getValue();
~Mansi Rao

GWTP: Correct information flow for popup presenter

i had this task: I have Presenter/View Couple (lets call it ItemListPresenter) showing a Celltable. Now i wanted to edit an item by double-clicking it (or pressing a button, whatever). Then a popup dialog should appear (Lets call it PopupWidget), letting me edit my item properties.
i found a solution how to do it, but i am not sure it is the "right" way. Since i am trying to learn the philosophy behind GWT/GWTP, i would appreciate if you could give me hints what i did right and what i did wrong:
In the onbind method of ItemListPresenter, i wire the CellTable with a DoubleClick handler:
getView().getCellTable().setSelectionModel(selectionModel);
getView().getCellTable().addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(final DoubleClickEvent event) {
DeviceDto selectedDeviceDto = selectionModel.getSelectedObject();
//TODO: Figure out how to best handle editing of DeviceDto
if (selectedDeviceDto != null) {
devicesDialog.setCurrentDeviceDTO(selectedDeviceDto);
addToPopupSlot(devicesDialog);
}
} }, DoubleClickEvent.getType());
What does not feel right is setting the object i want to edit (selectedDeviceDto) in the Dialog Presenter Widget. Is this the "right" way?
My popup presenter which is defined as
public class DeviceEditDialogPresenterWidget extends PresenterWidget<DeviceEditDialogPresenterWidget.MyView> implements
DeviceEditDialogUiHandlers {
is still ugly, since i just set every property into a text box and after editing, i recollect the properties and rebuild the object. This is messy and i guess i should GWT Editors for that. However, when i click the "Save" Button in the dialog, an UiHandler is triggered:
#UiHandler("okButton")
void okButtonClicked(ClickEvent event) {
DeviceDto dev = new DeviceDto(idBox.getText(), deviceIdBox.getText(), typeBox.getText(), firmwareVersionBox.getText(), userBox.getText(), statusBox.getText());
getUiHandlers().updateDevice(dev);
hide();
}
This triggers my DeviceEditDialogPresenterWidget, which itself fires and event:
#Override
public void updateDevice(DeviceDto device) {
eventBus.fireEvent(new DeviceUpdatedEvent(device));
}
This event is caught by a handler in the "mother" presenter with the CellTable, which is wired in the onBind method again:
addRegisteredHandler(DeviceUpdatedEvent.TYPE, new DeviceUpdatedEvent.DeviceUpdatedHandler() {
#Override
public void onDeviceUpdatedEvent(DeviceUpdatedEvent event) {
updateDevice(event.getDevice());
}
});
I would really like to avoid going down a road of messy mistakes, so any hints would be appreciated.
Thanks
Arthur
PresenterWidgets are usually designed to have a setter which is used to set a Model or DTO that the PresenterWidget works on (the same way you did it).
Alternatively you can avoid a PresenterWidget and use an Editor (extends Composite) that you manually add to a PopupPanel or DialogBox in your ListItemEditor.
This way you avoid the complexity of a PresenterWidget. But you have to handle the clicks (i.e. save button) from the ListItemPresenter. I would always try to start small (use a composite) and if you realize that you might need the functionality also in other places, create a PresenterWidget.
Also you don't need the updateDevice method because you pass a reference to your DTO. You only need to fresh the CellTable.
But apart from that your approach looks fine.

Gwt get Components

I have a Vertiacal panel object and This object contains many radiobuttons
So can i get those radioButton objects through Vertiacal panel object.
Maybe via iteration or ?
private void initCourse() {
coursePopupPanel.clear();
VerticalPanel verticalPanel = new VerticalPanel();
coursePopupPanel.setWidget(verticalPanel);
JsArray<JCourse> jCourseArray = JCourse.getList(stringMainData);
for (int i = 0; i < jCourseArray.length(); i++) {
final RadioButton courseRadioButton = new RadioButton("course");
courseRadioButton.setText(jCourseArray.get(i).getName());
courseRadioButton.getElement().setId(jCourseArray.get(i).getView());
verticalPanel.add(courseRadioButton);
//handler of course radio buttons
courseRadioButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
}
});
}
}
I have a reference to coursePopupPanel. but i have not reference to vertical panel, so can i get elements of vertical panel sonce holding reference to coursePopupPanel.
A GWT VerticalPanel is a subclass of ComplexPanel, an abstract class for Panels that contain more than one child widget. In ComplexPanel (and so inherited by VerticalPanel) are methods for getting the number of child widgets, getting references to them by index, and so on. You could build an iterator something like this:
Iterator<Widget> vPanelWidgets = myVerticalPanel.iterator();
while (vPanelWidgets.hasNext()){
Widget childWidget = vPanelWidgets.next();
if (childWidget instanceof RadioButton) {
...do stuff
}
}
I tend not to query a widget for its members. That ties me to the decisions I made about how to display the RadioButtons, following your example. What if you decide later to display your radio buttons in the cells of a FlexTable in order to control vertical and horizontal arrangement? To make that change means your widget iterator won't work. FlexTable is a Panel but not a ComplexPanel. The code I wrote above won't work if you decide to replace the VerticalPanel with a FlexTable.
If was to take something like this approach, I would keep my lists of related widgets (like a group of RadioButtons) in some sort of Java Collection. I pass that Collection to my presentation class, and inside there I write the code to do the layout. Usually that's a UiBinder class, with "#UiField(provided = true)" for these RadioButtons. The code in the presenter then associates the RadioButton elements of the Collection I passed in to the UiField placeholders for the RadioButtons in the UiBinder layout. So all my layout decisions are actually in the UiBinder xml file. If I decide to rip out my Vertical Panel and replace it with a FlexTable, I might not have to touch a single line of Java code, assuming I separated things out correctly.
[Actually, I would probably keep my decision to use RadioButtons inside the presentation layer, and inside the XML file in particular. That presentation class would fire a message on the EventBus to indicate the user had made a selection via a RadioButton ValueChangeHandler, and I wouldn't care if I used RadioButtons in a VerticalPanel or ToggleButtons in a FlexTable.]
You're not being to specific, add more details and maybe a code example.
I'm gonan try to guesstimate what you're trying to say here: You have a verticalPanel object. To it you add several radioButton objects. Later you want to retrive those radioButton objects (to maybe check if they're selected or not), right? There's several ways to do this. At any rate, why don't you check the code examples at the Gwt Showcase site here:
http://gwt.google.com/samples/Showcase/Showcase.html?locale=en_UM#!CwRadioButton
it has tons of visual examples, each with the attached code and css.
Since PopupPanel implements HasOneWidget interface you can coursePopupPanel.getWidget() to get a reference to your verticalPanel. And iterate widgets in it simply using
for (Widget w : verticalPanel){
//Do Stuff
}

GWT Accessing Widget Methods without knowing the name of the Widget

This may have been asked before but I have no idea how to word it to search for it.
I have a composite widget that has methods to update some of the widgets that make up the composite widget. When I add this composite widget to my panel I use a do while loop to pull data from an XML file and populate the composite data. When I instantiate the object each time to add the data it has a scope local to the do-while loop and I cannot call methods to update the data in the composite widget later on. Is there maybe a way to make an array of these composite widgets or another solution to be able to access the Widget?
Eric
Sure... use
List<Composite> widgetList = new ArrayList<Composite>();
// loop
widgetList.add(widget);
// end loop
widgetList.get(3).toString();
You'll want to use your custom class instead of Composite in the list generic... there's nothing stopping you from making data structures using widgets, just like you would with any other Java class.
If you are putting all your Widgets in that loop into one panel (presumably on of the subclasses of ComplexPanel, since you are adding many Widgets to one panel), then you could use one of the methods to access Widgets contained within a panel (assuming you add only those XML generated Widgets to the panel and nothing more):
com.google.gwt.user.client.ui.ComplexPanel.iterator() - returns an java.util.Iterator<Widget> you can use to traverse the list of Widgets within that Panel
com.google.gwt.user.client.ui.ComplexPanel.getWidgetCount() and getWidget(int index) can be used in a for loop to go through all the Widgets within a panel
So, let's look at an example:
VerticalPanel vPanel = new VerticalPanel();
// Fill up the vPanel using XML
Iterator<Widget> iterator = vPanel.iterator();
while(iterator.hasNext()) {
Widget w = iterator.next();
// Do something with w
}
// Or...
for (int i = 0; i < vPanel.getWidgetCount(); i++) {
Widget w = vPanel.getWidget(i);
// Do something with w
}
Of course, substitute VerticalPanel with the one you are you using :)
I definately recommend that you watch "Best Practices for Architecting GWT App" (from Google I/O 2009):
http://www.youtube.com/watch?v=PDuhR18-EdM
At about 24 minutes through it talks about how to write composite widgets using the MVP design pattern - although you should watch it all. Unfortunately it does not provide ready to use code snipets, but it does show you how to construct a framework to decouple your XML and UI objects nicely.