I am creating a mixin which renders a javascript file when a textfield gains focus.
I am new to the idea of mixins in Tapestry, and I am unsure of where to place my original javascript file which i wish to run when the textfield gains focus.
The following is an example of my code:
The Java mixin class:
package asc.mixins;
import org.apache.tapestry5.RenderSupport;
import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.IncludeJavaScriptLibrary;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.corelib.base.AbstractTextField;
#IncludeJavaScriptLibrary("js_dasher_mixin.js")
public class DasherMixin {
#Environmental
private RenderSupport renderSupport;
#InjectContainer
private AbstractTextField field;
#AfterRender
void addScript() {
this.renderSupport.addScript("new JSDasher('%s');",
this.field.getClientId());
}
}
The Javascript mixin file:
JSDasher = Class.create({
initialize: function(textField)
{
this.textField = $(textField);
this.textField.observe('focus', this.onFocus.bindAsEventListener(this));
},
onFocus: function(event)
{
//call my javascript init() function
}
}
part of my javascript file I wish to run when the textfield gains focus:
var posX, posY;
// Sets up our global variables and dispatches an init request to the server.
function init() {
posX=0;
posY=0;
canvas = document.getElementById("canvas");
canvasWidth = canvas.offsetWidth;
canvasHeight = canvas.offsetHeight;
if (canvas.getContext) {
ctx = canvas.getContext("2d");
}
canvas.onclick = canvasClicked;
canvas.onmousemove = mouseMoved;
canvasOffsetX = findPosX(canvas);
canvasOffsetY = findPosY(canvas);
sessID = -1;
sendInitRQ(canvasWidth, canvasHeight);
}
My javascript file is much larger than above, my question is where should I put my javascript code above?
Should it all be contained in the mixin.js file? if so where exactly should it go?
Thanks in advance for any help.
The method is ok free-floating in mixin.js but with name like init, you might have a conflict. You can put it in the JSDasher class itself or move the body to the onFocus function. You can also define any other functions on the JSDasher class and call them with this.function_name. Look at datefield.js in the Tapestry source as an example.
Related
this is my first spring application (maven project) which is supposed to allow users to upload and validate a file. At the moment I only have the default class called MyUI.java which contains all I need, here is an extract:
EDITED CODE:
FileUploader.java
public class FileUploader extends AbstractJavaScriptComponent {
public void callScript(){//as usual, it needs a method!!
JavaScript.getCurrent().execute(""
+"jsInit();"
+"");
}
And changes to the MyUI class:
`final FileUploader fileUploader = new FileUploader();//has to be final apparently
final Button button = new Button("Open popup");
button.addClickListener(new Button.ClickListener(){
#Override
public void buttonClick(ClickEvent event){
fileUploader.callScript();
}
});`
I need to move the js call to a new java class in a separate file, so I created a new class which now sits in its own file and that will extend AbstractJavaScriptComponent, my question is, what imports do I need for this class, all the ones that are in the previous class except for the UI or including those? Literally this class will only have the js call for now (there will be more stuff later)?
Also, to make use of this class presumably I will have to run As --> maven install again? And in general, what should I do?
cheers
This is a simple case of refactoring to extract a method and add a class.
You want to replace this
final Button button = new Button("Open popup");
JavaScript.getCurrent().execute("jsInit();");
With something like this
final Button button = new Button("Open popup");
JavaScriptCaller jsCaller = new JavaScriptCaller(JavaScript.getCurrent());
jsCaller.jsInit();
So, you can define a class like so
public class JavaScriptCaller extends AbstractJavaScriptComponent {
final JavaScript js;
public JavaScriptCaller(JavaScript js) {
this.js = js;
}
public void jsInit() {
this.js.execute("jsInit();")
}
}
The reason I added the parameter is so you don't need to keep calling the Javascript.getCurrent() since you only need one instance of it.
To get right directly to my question.
How do you do large scale GUI projects. I have not done any larger GUI projects in java so far but what i am working on now grew pretty fast and pretty big and now i am stuck whit a huge pile of code that is really annoying and messy.
Since i come from field of web development i am used to MVC frameworks so i have 3 packages in my projects Model where i keep classes that interact whit files or db, Views where i keep my classes for Forms or GUI and Controller package where i keep the majority of my logic.
I have been told to separate my logic as well keep actions in one class and listeners in another class but i have no idea how to link all that up.
So far i only have 1 Controller class where i execute all the methods regarding whats happening on the GUI once its invoked.
package pft.controller;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.JTree;
import java.awt.event.*;
import javax.swing.JProgressBar;
import pft.view.Invoke_GUI;
import pft.model.Events;
import pft.model.Parse;
public class Tower_Controller {
public Tower_Controller() {
}
//Global variables
String isSelected = null;
int hasModules = 0;
int cap = 0;
int cpu = 0;
int shield = 0;
int armor = 0;
public void setName(String name){
this.isSelected = name;
}
public String getName(){
return this.isSelected;
}
public void setCap(int cap){
this.cap = cap;
}
public int getCap(){
return this.cap;
}
public void setCpu(int cpu){
this.cpu = cpu;
}
public int getCpu(){
return this.cpu;
}
public void setShield(int shield){
this.shield = shield;
}
public int getShield(){
return this.shield;
}
public void setArmor(int armor){
this.armor = armor;
}
public int getArmor(){
return this.armor;
}
public void invoke() throws IOException {
Invoke_GUI runnable = new Invoke_GUI();
final JLabel tower_name = runnable.tower_name;
final JComboBox tower_select = runnable.tower_select;
final JTree module_browser = runnable.module_browser;
final JTree selected_modules = runnable.selected_modules;
final JProgressBar cap_bar = runnable.cap_bar;
final JProgressBar cpu_bar = runnable.cpu_bar;
final JLabel em_res = runnable.em;
final JLabel th_res = runnable.thermic;
final JLabel ki_res = runnable.kinetic;
final JLabel ex_res = runnable.explosive;
setTowerName(tower_name, tower_select);
removeTower(tower_name);
runnable.setVisible(true);
}
public void removeTower(final JLabel tower_name) {
tower_name.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
if (hasModules == 1 & isSelected != null) {
Events evt = new Events();
evt.towerHasModules();
} else if (isSelected == null) {
} else {
tower_name.setText("No Control Tower selected");
isSelected = null;
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
}
public void updateVariables(String name) throws IOException{
Parse tower = new Parse();
String data[] = tower.towerData(name);
Integer x = Integer.valueOf(data[1]).intValue();
setCap(x);
}
public void setTowerName(final JLabel tower_name, final JComboBox tower_select) {
tower_select.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isSelected != null) {
Events evt = new Events();
evt.towerSelected(isSelected);
} else {
tower_name.setText(tower_select.getSelectedItem().toString());
setName(tower_name.toString());
try {
updateVariables(tower_name.toString());
} catch (IOException ex) {
Logger.getLogger(Tower_Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
}
}
There are a lot of tutorials and examples how to do small usually single class Java GUI but no tutorials or examples on how to do projects that are bit larger than a single class.
Thanks in advance for all the help and
advice.
Here is my advice for Swing development in general. It does discuss the importance of using Controllers to bridge the needs of the view and the interace of the model.
GUI guidelines for swing
Last Swing project I did I designed a MVC framework that used Spring for defining the model of the program and Controllers, then used annotations in the Controller to wire up events dispatched by the view onto methods in the controller. The view had access to the event dispatcher which was an event bus, and events sent over the bus called methods on the Controller through the annotations. This allowed any Controller to respond to events from the View. So as a Controller got too large it was super simple to refactor each set of methods into another Controller, and the view or model didn't have to change.
The beauty of the event bus was it could be shared with the model as well so the model could dispatch asynchronous events the Controller could register for as well. It looked something like:
public class SomeController {
private AuthenticationModel authenticationModel;
private LoginService loginService;
private MyApp view;
#Listener( event = "login" )
public void login( LoginEvent event ) {
view.showWaitDialog();
loginService.login( event.getUserName(), event.getPassword() )
.onResult( new Callback<User>() {
public void onResult( User user ) {
authenticationModel.setUser( user );
view.hideWaitDialog();
view.showStartScreen(user);
}
});
}
}
Once this framework was in place it was amazing how fast we could get things done. And it held up pretty well as we added features. I've done my fair share of large Swing projects (3 to date), and this architecture made a huge difference.
The easiest way to scale a GUI is to make everything loosely coupled. Events (Swing's and your own) are the best way to do this. Unless a class is directly creating or showing a GUI element, it shouldn't know or care about anything else in the UI.
The Controller should continue doing what it's supposed to do - firing events in response to other events. But, these events should be application level events defined by the needs of your app. The Controller shouldn't directly manipulate GUI elements. Instead, you should create components (maybe just subclasses of JWhatever) that register themselves with the Controller as interested in events.
For example, create an TowerEventListener interface with a nameChanged() function. The Controller also has a changeTowerName() function, which when called, updates the model (a Tower class) then calls nameChanged() on all registered TowerEventListeners.
Then create a TowerRenamer class that, for example, subclasses JDialog (i.e. a popup window) that includes a text box and and OK button along with a reference to the Controller. When the user clicks OK, Controller.changeTowerName() is called. Other parts of your GUI that register as TowerEventListeners will receive the event and update as needed (maybe by updating a JLabel on the UI).
Note, if your needs are simple enough, you can just use PropertyChangeEvents and not worry about a whole event interface structure. PropertyChangeSupport can be used by the Controller to fire event notifications.
In addition to the great advice already given, I would recommend reading some of what Trygve Reenskaug has written and/or recorded on his MVC page. He was there during the development of this architectural style in the late 70's. His two page technical report entitled Models - Views - Controllers from December of 1979 presents the most concise description of the model, view, and controller.
Of particular note, views are both observers and manipulators of the model. The controller is primarily concerned with arranging (wiring) the views and translating user input into interactions with the model. Several of the MVC frameworks out there have the controller relaying data from the model to the view - this is simply wrong. A paper from earlier in 1979 included the concept of an editor as a composite of related views. The editor was discarded; its functionality was moved into both the controller and the view.
Another article that is good at describing how to apply this guideline is Burbeck's How to use Model-View-Controller. It is written with Smalltalk in mind so it might not translate to Java easily, but it is a good description of how to apply the guideline.
I think that the most important thing to consider is that the original MVC style was created for user interfaces that included more than one view (representation) of the same model. This really works well for user interfaces but does not translate exceptionally well to the web service world. Using MVC for a GUI lets you really see and understand the power of this style.
I'm trying to figure out how to make a dynamically generated csv available to a dygraphs JavaScript.
I'm using a wicket behavior to add the dygraph (JavaScript graph) to my markup like shown in the codesample bellow. Right now I've hardcoded it to use a csv file named "dygraph.csv". I want to change this, and instead make dygraph use the values from String csv, how do I achieve this?
Any help help is greatly appreciated.
public class DygraphBehavior extends AbstractBehavior {
private static final long serialVersionUID = -516501274090062937L;
private static final CompressedResourceReference DYGRAPH_JS = new CompressedResourceReference(DygraphBehavior.class, "dygraph-combined.js");
#Override
public void renderHead(IHeaderResponse response) {
response.renderJavascriptReference(DYGRAPH_JS);
}
#Override
public void onRendered(Component component) {
final String id = component.getId();
Response response = component.getResponse();
response.write(JavascriptUtils.SCRIPT_OPEN_TAG);
response.write("new Dygraph(document.getElementById(\""+id+"\"), \"dygraph.csv\", {rollPeriod: 7, showRoller: true, errorBars: true});");
response.write(JavascriptUtils.SCRIPT_CLOSE_TAG);
}
}
public class Dygraph extends WebPage {
public Dygraph() {
String csv = "Date,ms\n20070101,62\n20070102,62";
add(new ResourceLink<File>("csv", new ByteArrayResource("text/csv", csv.getBytes())));
add(new Label("graphdiv").add(new DygraphBehavior()));
}
}
<div>
<h1>Dygraph:</h1>
<div wicket:id="graphdiv" id="graphdiv" style="width:500px; height:300px;"></div>
<a wicket:id="csv" href="#">dl generated csv</a>
</div>
public class Dygraph extends WebPage {
public Dygraph() {
String csv = "Date,ms\n20070101,62\n20070102,62";
ResourceLink<File> link = new ResourceLink<File>("csv", new ByteArrayResource("text/csv", csv.getBytes()));
add( link );
//this is the url that should be passed to the javascript code
CharSequence url = link.urlFor( IResourceListener.INTERFACE );
add(new Label("graphdiv").add(new DygraphBehavior()));
}
}
There are other solutions based on the scope of your resource, maybe a dynamic shared resource would work better (if your graph parameters can simply be passed as url parameters), but this will work.
The JavaScript needs to see the data in some way after the page has been rendered. So you have two options:
Embed the data in the page (say in a hidden div) and then let JavaScript read the data from there as text.
Create a servlet where the JavaScript can download the data from.
The second option means that your page rendering code has to pass the data somehow to the servlet. You can try to put it into the session but then, it will sit there, occupying RAM. Probably not a problem if it's just a little bit of data and you have only a few users. But if that's not true, option #1 is probably better.
in my gwt, i have a button that fire like below to create new widget
#UiHandler("buttonfire")
void addNewWidget(ClickEvent event) {
htmlPanelHolder.add(new MyCustomWidget(),"placeholder");
}
how to use jquery so that when the MyCustomWidget() show on screen it is using jquery "fadein" effect
i already tried putting jsni to call jquery inside new MyCustomWidget() onload() but doesnt work. can guide me on this?
It's also possible to create a fade in effect without using jQuery. If you want to do that, just write an Animation class like this:
public class FadeInAnimation extends Animation {
private final UIObject uiObject;
FadeInAnimation(final UIObject uiObject) {
this.uiObject = uiObject;
}
#Override
protected void onUpdate(final double progress) {
uiObject.getElement().getStyle().setOpacity(progress);
}
}
Then you can use it on any widget:
new FadeInAnimation(myWidget).run(5000);
Note: The call to getStyle().setOpacity(..) even takes care of the special case IE6/7, where it sets style.filter = 'alpha(opacity=' + (value * 100) + ')';
You can also try with the GQuery library: http://code.google.com/p/gwtquery/
use the fadeIn effect of GwtQuery
I have been trying to create a custom textfield in tapestry which will render some javascript when it gains focus. But I have been having trouble trying to find an example of this.
Here is some of the code i have started off with:
package asc.components;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Field;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.ComponentDefaultProvider;
public class DahserTextField implements Field {
#Parameter (defaultPrefix = "literal")
private String label;
#Inject
private ComponentResources resources;
#Inject
private ComponentDefaultProvider defaultProvider;
#Parameter
private boolean disabled;
#Parameter
private boolean required;
String defaultLabel(){
return defaultProvider.defaultLabel(resources);
}
public String getControlName() {
return null;
}
public String getLabel() {
return label;
}
public boolean isDisabled() {
return disabled;
}
public boolean isRequired() {
return required;
}
public String getClientId() {
return resources.getId();
}
}
I have been unsure on what to do next. I do not know what to put into the .tml file. I would be grateful if anyone could help or point me in the right direction.
There is no need to replicate any of TextField's functionality in your own component, instead you should create a component mixin. Mixins are designed to add behaviour to existing components.
From the Tapestry 5 docs:
Tapestry 5 includes a radical feature,
component mixins. Component mixins are
a tricky concept; it basically allows
a true component to be mixed together
with special limited components called
mixins. The component plus its mixins
are represented as just a single tag
in the component template, but all the
behavior of all the elements.
You would use the mixin like this:
<input type="text" t:type="TextField" t:mixins="MyMixin" t:someParam="foo" />
A mixin stub:
#IncludeJavaScriptLibrary("MyMixin.js")
public class MyMixin {
/**
* Some string param.
*/
#Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String someParam;
#Environmental
private RenderSupport renderSupport;
#InjectContainer
private AbstractTextField field;
#AfterRender
void addScript() {
this.renderSupport.addScript("new MyJavascriptClass('%s', '%s');",
this.field.getClientId(), this.someParam);
}
}
Note the #InjectContainer annotation, which injects the containing TextField into your Mixin. In this case, we want the TextField's clientId.
Also note the #IncludeJavaScriptLibrary("MyMixin.js") annotation, which includes the required Javascript file.
The Javascript could look like this:
MyJavascriptClass = Class.create({
initialize: function(textField, someParam)
{
this.textField = $(textField);
this.someParam = someParam;
this.textField.observe('focus', this.onFocus.bindAsEventListener(this));
},
onFocus: function(event)
{
//do something
}
}
The key difference to your approach is that this involves defining your own JS class and using Tapestry's built-in facilities to load and initialize the JS. The use of mixins is also relatively light-weight and elegant in comparison to creating your own components.
The .tml
<t:textfield onfocus="somethingCool()" />
The Java should probably extent TextField? It will need to import a new stylesheet too probably.
--
Pages are actually components, so you would build a component just like you would have any other page. You can embed any other component into them. I hope this is a good starting point for you.