Java / Servlets: Sample application: "Guess number"

Entry point

To launch a jasty.web application, there should be an entry page, resolving the initial Form.

    <%@ taglib prefix="jst" uri="http://textorius.net/jsp" %>
    <html>
    <head>
        <title>Guess a number</title>
        <script src="resources/jquery-1.7.min.js" type="text/javascript"></script>
        <script src="resources/jquery.form.js" type="text/javascript"></script>
        <script src="resources/jasty-core.js" type="text/javascript"></script>
        <script src="resources/jasty-components.js" type="text/javascript"></script>
    </head>
    <body>
        <script>
            jasty.settings.formEngineUrl = "formEngine";
        </script>
        <jst:formViewer id="guessnum" entryPointClass="guessnumber.MainForm" />
    </body>
    </html>

The entry page typically references all necessary JS-libraries, configures Form Engine URL and contains formViewer-tag, pointing to the initial Form or Form resolver (EntryPoint).

Forms and Views

Forms are defined as plain classes, derived from Form-component. Object, returned by the prepareModel-method can be referenced on the main view as "model"-attribute. Form class also contains event handlers, which can be bound to the component events (startNewGame-method)

package guessnumber;

import com.jasty.components.JQuery;
import com.jasty.components.std.TextBox;
import com.jasty.core.EventArgs;
import com.jasty.core.Form;

public class MainForm extends Form {

    private int randomNumber = ((int) Math.floor(Math.random() * 100)) + 1;
    private int numberOfTries = 0;
    private int lowerBound = 1;
    private int upperBound = 100;

    @Override
    public Object prepareModel() {
        return "";
    }

    public void startNewGame(EventArgs e) {
        replaceWith(new MainForm());
    }

    public void processGuess(EventArgs e) {
        JQuery stats = $$(JQuery.class, "stats");
        TextBox guessEntryField = $$(TextBox.class, "guessEntryField");

        int guess;
        try {
            guess = Integer.parseInt(guessEntryField.getValue());
        } catch (NumberFormatException ex) {
            JQuery statusLabel = $(JQuery.class, "#stats .status");
            statusLabel.text("Your guess was not valid.");
            return;
        }


        ++numberOfTries;

        if (guess == randomNumber) {
            replaceWith(new CongratulationForm(numberOfTries));
            return;
        }

        guessEntryField.setValue("");

        String statusText = "";

        if (guess < 1 || guess > 100) {
            statusText = "Your guess, " + guess + " was not between 1 and 100.";
        } else if (guess < randomNumber) {
            if (guess >= lowerBound) {
            lowerBound = guess + 1;
            }
            statusText = "Your guess, " + guess + " was too low.  Try again:";
        } else if (guess > randomNumber) {
            statusText = "Your guess, " + guess + " was too high.  Try again:";
            if (guess <= upperBound) {
            upperBound = guess - 1;
            }
        }

        stats.html(renderFragment("stats", statusText));
    }

    public int getLowerBound() {
        return lowerBound;
    }

    public int getUpperBound() {
        return upperBound;
    }

    public String getCounter() {
        // Update number of tries label.
        if(numberOfTries == 0)
            return "You have made no guesses";
        if (numberOfTries == 1)
            return "You have made 1 guess.";
        return "You have made " + numberOfTries + " guesses.";
    }
}

JSP-views, related to the Form, should be located in the directory, corresponding to the package of the Form class, and named as the class (e.g.: /guessnumber/MainForm.jsp)

    <%@ taglib prefix="jst" uri="http://textorius.net/jsp" %>
    <h1>Guess a number</h1>
    <div id="${currentForm.id}_stats">
        <jsp:include page="MainForm_stats.jsp"/>
    </div>
    <jst:textBox id="guessEntryField" />
    <jst:button onClick="processGuess" text="Submit Your Guess" />
    <jst:button onClick="startNewGame" text="Start a New Game" />

Subviews or fragments must be located in the same directory as the main view. The subview file name is the Form class name with the fragment name, separated by underscore (e.g. /guessnumber/MainForm_stats.jsp)

    <div class="status">${model}</div>
    <div>${currentForm.counter}</div>
    <div>Guess a number between ${currentForm.lowerBound} and ${currentForm.upperBound}:</div>

Groovy / Grails: Sample application: "Guess number"

Entry point

To launch a gasty.web application, there should be an entry page, resolving the initial Form.

    <%@ page contentType="text/html;charset=UTF-8" %>
    <!DOCTYPE html>
    <html>
    <head>
        <title>Guess a number</title>
        <script src="${'$'}{jasty.resource(dir: 'js', file: 'jquery-1.8.3.js')}" type="text/javascript"></script>
        <script src="${'$'}{jasty.resource(dir: 'js', file: 'jquery.form.js')}" type="text/javascript"></script>
        <script src="${'$'}{jasty.resource(dir: 'js', file: 'jasty-core.js')}" type="text/javascript"></script>
        <script src="${'$'}{jasty.resource(dir: 'js', file: 'jasty-std.js')}" type="text/javascript"></script>
    </head>
    <body>
    <script>
        jasty.settings.formEngineUrl = "${'$'}{g.createLink([controller: 'gasty', action: 'doAction'])}";
    </script>
    <jasty:formViewer id="myform" entryPoint="guessnumber.MainForm" />

    </body>
    </html>

The entry page typically references all necessary JS-libraries, configures Form Engine URL and contains formViewer-tag, pointing to the initial Form or Form resolver (EntryPoint).

Forms and Views

Forms are defined as plain classes, derived from Form-component. Object, returned by the prepareModel-method can be referenced on the main view as "model"-attribute. Form class also contains event handlers, which can be bound to the component events (startNewGame-method)

package guessnumber

import com.jasty.core.Form
import com.jasty.components.JQuery
import com.jasty.components.std.TextBox

class MainForm extends Form {

    private int randomNumber = ((int) Math.floor(Math.random() * 100)) + 1
    private int numberOfTries = 0
    int lowerBound = 1
    int upperBound = 100

    void startNewGame() {
        replaceWith(new MainForm())
    }

    void processGuess(JQuery stats, TextBox guessEntryField) {

        int guess

        try {
            guess = guessEntryField.getValue() as int
        } catch (NumberFormatException ex) {
            $(JQuery, "#stats .status").text("Your guess was not valid.")
            return
        }

        ++numberOfTries

        if (guess == randomNumber) {
            replaceWith(new CongratulationForm(numberOfTries))
            return
        }

        guessEntryField.value = ""

        def statusText = ""

        if (guess < 1 || guess > 100) {
            statusText = "Your guess, ${'$'}{guess} was not between 1 and 100."
        } else if (guess < randomNumber) {
            if (guess >= lowerBound) {
                lowerBound = guess + 1
            }
            statusText = "Your guess, ${'$'}{guess} was too low.  Try again:"
        } else if (guess > randomNumber) {
            statusText = "Your guess, ${'$'}{guess} was too high.  Try again:"
            if (guess <= upperBound) {
                upperBound = guess - 1
            }
        }

        stats.html(renderFragment("stats",[model: statusText, currentForm: this]))
    }

    String getCounter() {
        // Update number of tries label.
        if(numberOfTries == 0)
            return "You have made no guesses"
        if (numberOfTries == 1)
            return "You have made 1 guess."
        "You have made ${'$'}{numberOfTries} guesses."
    }
}

GSP-views, related to the Form, should be located in the directory, corresponding to the package of the Form class, and named as the class (e.g.: /guessnumber/MainForm.gsp)

    <h1>Guess a number</h1>
    <div id="${'$'}{currentForm.id}_stats">
        <g:include view="/forms/guessnumber/MainForm_stats.gsp" model="[currentForm: currentForm]"/>
    </div>
    <jasty_std:textBox id="guessEntryField" />
    <jasty_std:button onClick="processGuess" text="Submit Your Guess" />
    <jasty_std:button onClick="startNewGame" text="Start a New Game" />

Subviews or fragments must be located in the same directory as the main view. The subview file name is the Form class name with the fragment name, separated by underscore (e.g. /guessnumber/MainForm_stats.gsp)

    <div class="status">${'$'}{model}</div>
    <div>${'$'}{currentForm.counter}</div>
    <div>Guess a number between ${'$'}{currentForm.lowerBound} and ${'$'}{currentForm.upperBound}:</div>

.NET / MVC: Sample application: "Guess number"

Entry point

To launch a Nasty-Web application, there should be an entry page, resolving the initial Form.

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
    <%@ Register TagPrefix="t" Assembly="Nasty.Mvc" Namespace="Nasty.Mvc" %>
    <!DOCTYPE html >
    <html>
    <head>
        <title>Guess a number</title>
        <script src="<%=Url.FormEngineScript("jquery-1.7.min.js")%>" type="text/javascript"></script>
        <script src="<%=Url.FormEngineScript("jquery.form.js")%>" type="text/javascript"></script>
        <script src="<%=Url.FormEngineScript("jasty-core.js")%>" type="text/javascript"></script>
        <script src="<%=Url.FormEngineScript("jasty-std.js")%>" type="text/javascript"></script>
    </head>
    <body>
        <script>
            jasty.settings.formEngineUrl = "<%=Url.FormEngine()%>";
        </script>
        <t:FormViewer runat="server" ID="myform" EntryPointClass="Nasty.Samples.Forms.MainForm, Nasty.Samples"/>
    </body>
    </html>

The entry page typically references all necessary JS-libraries, configures Form Engine URL and contains FormViewer-tag, pointing to the initial Form or Form resolver (EntryPoint).

Forms and Views

Forms are defined as plain classes, derived from Form-component. Object, returned by the PrepareModel-method can be referenced on the main view as "model"-attribute. Form class also contains event handlers, which can be bound to the component events (StartNewGame-method)

[Serializable]
public class MainForm : Form {

    private readonly int _randomNumber = new Random().Next(100);
    private int _numberOfTries;

    public MainForm()
    {
        LowerBound = 1;
        UpperBound = 100;
    }

    public override object PrepareModel() {
        return "";
    }

    public void StartNewGame(Core.EventArgs e) {
        ReplaceWith(new MainForm());
    }

    public void ProcessGuess(Core.EventArgs e)
    {
        var stats = Get<JQuery>("stats");
        var guessEntryField = Get<TextBox>("guessEntryField");

        int guess;
        try {
            guess = int.Parse(guessEntryField.Value);
        } catch (FormatException) {
            var statusLabel = Query<JQuery>("#stats .status");
            statusLabel.Text("Your guess was not valid.");
            return;
        }


        ++_numberOfTries;

        if (guess == _randomNumber) {
            ReplaceWith(new CongratulationForm(_numberOfTries));
            return;
        }

        guessEntryField.Value = "";

        var statusText = "";

        if (guess < 1 || guess > 100) {
            statusText = "Your guess, " + guess + " was not between 1 and 100.";
        } else if (guess < _randomNumber) {
            if (guess >= LowerBound) {
                LowerBound = guess + 1;
            }
            statusText = "Your guess, " + guess + " was too low. Try again:";
        } else if (guess > _randomNumber) {
            statusText = "Your guess, " + guess + " was too high. Try again:";
            if (guess <= UpperBound) {
                UpperBound = guess - 1;
            }
        }

        stats.Html(RenderFragment("stats", statusText));
    }

    public int LowerBound
    {
        get; private set;
    }

    public int UpperBound
    {
        get; private set;
    }

    public String Counter {
        get
        {
            // Update number of tries label.
            if (_numberOfTries == 0)
            return "You have made no guesses";
            if (_numberOfTries == 1)
            return "You have made 1 guess.";
            return "You have made " + _numberOfTries + " guesses.";
        }
    }
}

ASPX-views, related to the Form, should be located in the same directory as the Form class, and named as the class (e.g.: MainForm.aspx)

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
    <%@ Import Namespace="Nasty.Mvc" %>
    <%
        var currentForm = (Nasty.Samples.Forms.MainForm)ViewData["currentForm"];
    %>
    <h1>Guess a number</h1>
    <div id="<%=currentForm.Id%>_stats">
    </div>
    <%=Html.Component(new Nasty.Components.TextBox { Id = "guessEntryField" })%>
    <%=Html.Component(new Nasty.Components.Button { OnClick = "ProcessGuess", Text = "Submit Your Guess" })%>
    <%=Html.Component(new Nasty.Components.Button { OnClick = "StartNewGame", Text = "Start a New Game" })%>

Subviews or fragments are implemented as ViewuserControls (ASCX) and must be located in the same directory as the main view. The subview file name is the Form class name with the fragment name, separated by underscore (e.g. MainForm_stats.ascx)

    <%@ Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
    <%
        var currentForm = (Nasty.Samples.Forms.MainForm)ViewData["currentForm"];
    %>
    <div class="status"><%=ViewData["model"]%></div>
    <div><%=currentForm.Counter%></div>
    <div>Guess a number between <%=currentForm.LowerBound%> and <%=currentForm.UpperBound%>:</div>