JavaScript Mixin

We can make JavaScript even easier to reuse by packaging it as a Tapestry mixin. Here we have turned the "textbox hint" into a mixin and applied it to the firstName and lastName fields. The page no longer mentions JavaScript, the mixin does it all.
Mixin location is important. Mixins must be put in a package called mixins. Tapestry gives this package special treatment.

References: @InjectContainer, ClientElement, Component Mixins, Tapestry JavaScript, JavaScriptSupport, RequireJS, jQuery API.

Home


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like &nbsp; 
     We use a "strict" DTD to make IE follow the alignment rules. -->
     
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
<body class="container">
    <h3>JavaScript Mixin</h3>

    <noscript class="js-required">
        ${message:javascript_required}
    </noscript>     

    We can make JavaScript even easier to reuse by packaging it as a Tapestry mixin. Here we have turned the "textbox hint" into a mixin 
    and applied it to the firstName and lastName fields. The page no longer mentions JavaScript, the mixin does it all.
    
    <div class="eg">
        <t:form class="form-inline" autofocus="false">
            <div class="form-group">
                <t:textfield t:id="firstName" t:mixins="TextboxHint" 
                    TextboxHint.hintText="Enter First Name" TextboxHint.hintColor="#808080"/>
            </div>
            <div class="form-group">
                <t:textfield t:id="lastName" t:mixins="TextboxHint" 
                    TextboxHint.hintText="Enter Last Name" TextboxHint.hintColor="#808080"/>
            </div>
        </t:form>
    </div>
    
    Mixin location is important.  Mixins must be put in a package called <code>mixins</code>.  Tapestry gives this package 
    special treatment.<br/><br/>
    
    References: 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/InjectContainer.html">@InjectContainer</a>, 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/ClientElement.html">ClientElement</a>, 
    <a href="http://tapestry.apache.org/component-mixins.html">Component Mixins</a>,
    <a href="http://tapestry.apache.org/javascript.html">Tapestry JavaScript</a>, 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a>,
    <a href="http://requirejs.org">RequireJS</a>, 
    <a href="http://api.jquery.com">jQuery API</a>.<br/><br/> 
    
    <t:pagelink page="Index">Home</t:pagelink><br/><br/>
    
    <t:tabgroup>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/javascript/JavaScriptMixin.tml"/>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/javascript/JavaScriptMixin.java"/>
        <t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/js.css"/>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/mixins/TextboxHint.java"/>
        <t:sourcecodetab src="/web/src/main/resources/META-INF/modules/textbox-hint.js"/>
    </t:tabgroup>
</body>
</html>


package jumpstart.web.pages.examples.javascript;

import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Property;

@Import(stylesheet = "css/examples/js.css")
public class JavaScriptMixin {

    // Screen fields

    @Property
    private String firstName;

    @Property
    private String lastName;

}


.eg {
                margin: 20px 0;
                padding: 14px;
                color: #888;
                border: 1px solid #ddd;
                border-radius: 6px;
                -webkit-border-radius: 6px;
                -mox-border-radius: 6px;
}

.js-required {
                color: red;
                display: block;
                margin-bottom: 14px;
}

.js-recommended {
                color: red;
                display: block;
                margin-bottom: 14px;
}


/**
 * A simple mixin that uses JavaScript to observe a field, giving it a "textbox hint" when it is empty and does not have focus.
 */
package jumpstart.web.mixins;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ClientElement;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

public class TextboxHint {

    // Parameters

    @Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
    private String hintText;

    @Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
    private String hintColor;

    // Generally useful bits and pieces

    @Inject
    private JavaScriptSupport javaScriptSupport;

    @InjectContainer
    private ClientElement attachedTo;

    // The code

    public void afterRender() {

        // Give a "textbox hint" to the field we're being mixed into.

        javaScriptSupport.require("textbox-hint").with(attachedTo.getClientId(), hintText, hintColor);

    }

}


// This module observes a field, giving it a "textbox hint" when it is empty and does not have focus.
// Beware: it is flawed because it won't allow the user to submit text that is the same as the hint!

define(["jquery"], function($) {

    return function(textboxId, hintText, hintColor) {
        var $textbox = $("#" + textboxId);

        var normalColor = $textbox.css("color");

        $textbox.on("focus submit", doClearHint);
        $textbox.on("blur change", doCheckHint);

        $textbox.blur();

        function doClearHint() {
            var $field = $(this);
            
            if ($field.val() == hintText) {
                $field.val("");
            }

            $field.css("color", normalColor);
        }

        function doCheckHint() {
            var $field = $(this);

            // If $field is empty, put the hintText in it and set its color to
            // hintColor

            if ($field.val() == "") {
                $field.val(hintText);
                $field.css("color", hintColor);
            }

            // Else if $field contains hintText, set its color to hintColor

            else if ($field.val() == hintText) {
                $field.css("color", hintColor);
            }

            // Else, set the $field's color to its normal color

            else {
                $field.css("color", normalColor);
            }
        }

    }

})