views:

285

answers:

2

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.

A: 

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.

Dustin Digmann
+3  A: 

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.

Henning
Thank you for your answer it was very helpful.However, I am unsure on where I should place the Mixin.java class?I was reading somewhere that i should place it in a sub folder named mixins.Should I create this folder?I tried creating the folder and placing my mixin class there, but when I ran the application, I got an exception saying the mixin could not be found.I am using maven and eclipse to develop my project.Any help would be much appreciated. Thanks again.
shane87
@Shane: Yes, 'mixins' is one of Tapestry's magical packages, like 'components' and 'pages'. If you place it next to 'pages' and 'components', everything should work. You may have to restart the application after you add the package, as live reloading doesn't work for newly created packages. (And of course, the class name must match the type you specify in `t:mixins`.)
Henning
Thanks once again Henning.One final question, I have a javascript file which I wish to run when the textfield gains focus.Should this script be included in the mixin.js file, this is what I have been trying to do.For example my script has an init() function which I wish to call in the onfcus event in the mixin.jsWhat I have done is copy and pasted the code you gave me for the mixin.js(tweaked this code to suit my needs) into the top of my javascript file I wish to run.Am i going about this the right way?Sorry if this is a dumb question. I am new to this concept.
shane87
Should I be using Prototype?
shane87
Prototype is the basis for all built-in components that use JS, so it makes sense to use it when coding JS. My sample JS class should execute the onFocus() method whenever the text field recieves focus. The text field is available as `this.textField`. You could integrate your script into the `onFocus` method or just call the `init()` method of your script there, as you originally intended. If you're not sure what's working and why, setting breakpoints in Firebug or just putting `alert()` statements in the approriate places while debugging will help.
Henning
Thanks, I have a much clearer idea of how a javascript mixin works now. If i wanted to get the id of the textfield in the javascript would I call this.textField.getID();?
shane87
Probably `this.textField.id`.
Henning