Статьи

Связывание данных на основе карт в GWT 1.6+


Связывание является наиболее обсуждаемой проблемой в сообществе GWT. У каждого свое решение и разные материалы в поддержку своих аргументов. Когда мы говорим «связывание», мы говорим «связывание на основе POJO». Я из школы мысли, которая любила видеть, что клиентский интерфейс полностью отделен от бизнес-уровня, и передача Business Objects на уровень пользовательского интерфейса разрушит концепцию разделения. Мы можем реализовать концепцию отделения уровня пользовательского интерфейса от бизнес-уровня, если передадим данные на основе карты из бизнес-уровня. Карта — это система пар ключ / значение, в которой мы рассматриваем «ключ» как имя свойства домена, а значением будет «Объект», связанный с этим свойством. В этой статье я объясню механизм создания системы привязки для виджета пользовательского интерфейса с данными на основе карты.Мы создали служебный класс Mab2BeanConvertor, который преобразует POJO в HashMap и наоборот. Этот служебный класс возьмет на себя бремя преобразования HashMap в POJO и POJO в HashMap.

Теперь я объясню, как мы достигли связывания с GWT. Мы придерживаемся стиля JGoodies концепции ValueModel хранения данных и системы Binder, которая связывает ValueModel с компонентом пользовательского интерфейса.

Эта система имеет три (3) числа классов, т.е.

    (i) GWTMapValueModel (ii) GWTBinding (iii) GWTWidgetDomainSynchronizer

  1. GWTMapValueModel — это модель значений, которая содержит значение для виджета, связанное с некоторым свойством компонента, т. Е. Ключом Map.
  2. GWTBinding является основным действующим лицом этой системы, которая будет привязывать виджет к ключу карты.
  3. GWTWidgetDomainSynchronizer будет служить моделью для синхронизации виджета и карты.

Наша
GWTMapValueModel показана ниже

import java.util.Map;

/**
 * @author Anees
 *
 */
public class GWTMapValueModel {
	
	private String key;
	private Map map;
	private GWTWidgetDomainSynchronizer synchronizer ;
	
	public GWTMapValueModel(String key, Map map){
		this.key = key;
		this.map = map;
	}
	
	
	public void setValue(final Object newValue){
		Object oldValue = map.get(key);
		map.put(key, newValue);
		firePropertyChange(oldValue, newValue);
	}
	
	public Object getValue(){
		return map.get(key);
	}
	public Map getMap(){
		return map;
	}
	
	protected String getKey(){
		return key;
	}
		
	public void createBindableSynch(GWTWidgetDomainSynchronizer    synchronizer){
		this.synchronizer = synchronizer;
	}
	/**
	 * 
	 * @param oldValue
	 * @param newValue
	 */
	private void firePropertyChange(Object oldValue, Object newValue){
		synchronizer.processGWTMapValueModelChange(oldValue, newValue);
	}
}

GWTBinding класс показан ниже с полным исходным кодом для вашего руководства:

package com.eagle.coders.core.web.gwt.client.ui.bindings;

import com.eagle.coders.core.web.gwt.client.ui.form.widgets.RadioButtonWidget;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.datepicker.client.DateBox;

/**
 * @author Anees

 *
 */
public class GWTBinding  {
	private GWTMapValueModel valueModel;
	private static GWTWidgetDomainSynchronizer sync ;
	
	public static void bindTextBox(TextBox textBox, GWTMapValueModel valueModel, String
caseType){
		sync = new GWTWidgetDomainSynchronizer(valueModel, textBox);
		textBox.addValueChangeHandler(new 
			BindingValueChangeHandler(valueModel, caseType));
	}
	
	public static void bindPasswordBox(PasswordTextBox passwordBox, GWTMapValueModel 
	valueModel){
		sync = new GWTWidgetDomainSynchronizer(valueModel, passwordBox);
		passwordBox.addValueChangeHandler(new 
			BindingValueChangeHandler(valueModel, null));
	}
	
	public static void bindRadioButton(RadioButtonWidget radioButton, GWTMapValueModel 
	valueModel){
		sync = new GWTWidgetDomainSynchronizer(valueModel, radioButton);
		radioButton.addValueChangeHandler(new 
			BindingValueChangeHandler(valueModel, null));
	}
	
	public static void bindCheckBox(final CheckBox checkBox, GWTMapValueModel valueModel){
		sync = new GWTWidgetDomainSynchronizer(valueModel, checkBox);
		checkBox.addValueChangeHandler(new 
			BindingValueChangeHandler(valueModel, null));
	}
	
	public static void bindComboBox(final ListBox listBox, GWTMapValueModel valueModel){
		sync = new GWTWidgetDomainSynchronizer(valueModel, listBox);
		listBox.addChangeHandler(new 
			BindingChangeHandler(valueModel));
	}
	
	public static void bindTextAreaWidget(final TextArea textArea, GWTMapValueModel valueModel){
		sync = new GWTWidgetDomainSynchronizer(valueModel, textArea);
		textArea.addValueChangeHandler(new 
			BindingValueChangeHandler(valueModel, null));
	}
	public static void bindDateBoxWidget(final DateBox dateBox, GWTMapValueModel valueModel){
		sync = new GWTWidgetDomainSynchronizer(valueModel, dateBox);
		dateBox.addValueChangeHandler(new 
			BindingValueChangeHandler(valueModel, null));
	}
	/**
	 * @help for ListBox Binding
	 * @author Anees
	 *
	 * @param 
	 */
	private static class BindingChangeHandler implements 
	ChangeHandler{
		private T valueModel;
		
               public BindingChangeHandler(T valueModel){
			this.valueModel = valueModel;
		}
		@Override
		public void onChange(ChangeEvent event) {
			
			if(event.getSource() instanceof ListBox){
				int index = ((ListBox)event.getSource()).getSelectedIndex();
				String value = ((ListBox)event.getSource()).getItemText(index);
				processListBox(value, valueModel);
			}
		}
	}
	
	/**
	 * 
	 * @author Anees
	 *
	 * @param 
	 */
	private static class BindingValueChangeHandler implements ValueChangeHandler{

		private T valueModel;
		private String caseType;
		
		public BindingValueChangeHandler(T valueModel, String caseType){
			this.valueModel = valueModel;
			this.caseType = caseType;
		}
		@Override
		public void onValueChange(ValueChangeEvent event) {
			
			if(event.getSource() instanceof TextBox && !(event.getSource() instanceof 
			PasswordTextBox))
				sync.processTextBox(event.getValue(), caseType ,valueModel);
			else if(event.getSource() instanceof PasswordTextBox)

				sync.processPasswordTextBox(event.getValue(), valueModel);
			
			else if(event.getSource() instanceof RadioButtonWidget)
		sync.processRadioButton(event.getValue(),(RadioButtonWidget)event.getSource() 
			,valueModel);
			
			else if(event.getSource() instanceof CheckBox)
				sync.processCheckBox(event.getValue(), valueModel);
			
			else if(event.getSource() instanceof DateBox)
				sync.processDateBox(event.getValue(), valueModel);
		}
	}
}

 

Теперь класс модератора, который будет синхронизировать состояние HashMap и Widgets, т.е. GWTWidgetDomainSynchronizer :

package com.eagle.coders.core.web.gwt.client.ui.bindings;

import java.util.List;

import com.eagle.coders.core.web.gwt.client.ui.form.widgets.RadioButtonWidget;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Anees
 *
 */
public class GWTWidgetDomainSynchronizer {
	
	private String propertyName;
	private GWTMapValueModel valueModel;
	private Widget widget;
	
	public GWTWidgetDomainSynchronizer(GWTMapValueModel valueModel, Widget widget){
		this.propertyName = valueModel.getKey();
		this.valueModel = valueModel;
		this.widget = widget;
		this.valueModel.createBindableSynch(this);
		synchOnBinding();
	}		
	private void synchOnBinding(){
		processGWTMapValueModelChange(null, valueModel.getValue());
	}	
	/**
	 * 
	 * @param valueModel
	 * @param property
	 */
	public void processGWTMapValueModelChange(Object oldValue, Object newValue){
		if(!(widget instanceof PasswordTextBox) && widget instanceof TextBox){
			((TextBox)widget).setText(newValue.toString());

		}else if(!(widget instanceof RadioButtonWidget)&& widget instanceof CheckBox){
			CheckBox checkBox = (CheckBox)widget;			
			if(!newValue.equals(checkBox.getValue())){
				checkBox.setValue(Boolean.valueOf(newValue.toString()));
			}
						
		}else if(widget instanceof ListBox){			
//			TODO: as listbox can have multiple selected data
			if(newValue instanceof List){
				
			}
			
		}else if(widget instanceof RadioButtonWidget){
			RadioButtonWidget radioButton =(RadioButtonWidget)widget;
			
			if(radioButton.getChoice().equals(newValue)){
				
				radioButton.setValue(true);
			}else
				radioButton.setValue(false);			
						
		}else if(widget instanceof TextArea){
			((TextArea)widget).setText(newValue.toString());
			
		}else if(widget instanceof PasswordTextBox){
			((PasswordTextBox)widget).setText(newValue.toString());
		}
	}	
	/**
	 * 
	 * @param 
	 * @param value
	 * @param valueModel
	 */
	public  void processTextBox(Object value,String 
	caseType ,T valueModel ){		
		String cValue = "";
		if(null != caseType){
			if(caseType.equals("upper")){
				cValue = value.toString().toUpperCase();				
			}else if (caseType.equals("lower")){
				cValue = value.toString().toLowerCase();
				
			}else if (caseType.equals("mixed")){
				cValue = value.toString();
			}			
			valueModel.setValue(cValue);
		     }else {
			valueModel.setValue(value);
		}
	}	
	/**
	 * 
	 * @param 
	 * @param value
	 * @param valueModel
	 */
	public   void processPasswordTextBox(Object value, 
T valueModel ){
		valueModel.setValue(value);
	}	 
	/**
	 * 
	 * @param 
	 * @param value
	 * @param valueModel
	 */
	public  void processRadioButton(Object
 value, RadioButtonWidget radioButton ,T valueModel ){
		valueModel.setValue(radioButton.getChoice());
	}
	
	/**
	 * 
	 * @param 
	 * @param value
	 * @param valueModel
	 */

	public  void processCheckBox(Object value, T valueModel 
	){
		valueModel.setValue(value);
	}	
	/**
	 * 
	 * @param 
	 * @param value
	 * @param valueModel
	 */
	public  void processListBox(Object value, T valueModel 
	){
		valueModel.setValue(value);
	}
	/**
	 * 
	 * @param 
	 * @param value
	 * @param valueModel
	 */
	public  void processDateBox(Object value, T valueModel 
	){
		valueModel.setValue(value);
	}
}

Мы протестировали со всеми видами виджетов, предоставляемых GWT1.6.x +. У этого подхода есть один недостаток: мы можем использовать GWT1.6 + совместимые предоставленные виджеты, так как они основаны на новой EventModel, то есть на Handler, а не на Listeners.

  Map map = new HashMap();
    map.put("firstName","");

    GWTMapValueModel valueModel = new GWTMapValueModel("firstName", map);

    TextBox firstNameWidget = new TextBox();

   GWTBinding.bindTextBox(firstNameWidget, valueModel);

Надеемся, что этот подход решит многие проблемы связывания в Google Web Toolkit.