Monday, October 18, 2010

Creating a MathQuestion

In the previous post I talked about information hiding in Context.

In this post I show a simple component that is using that feature. The component makes a mathematical question and let's the user answer to it. The functionality is following:

  • Question contains two integer values and an operation (+ or -)
  • New values and operation is randomly generated when component is created
    or if user asks for it.
  • When user answer to the question, a message of success or failure is shown

The example shows that it is only needed to send the answer back to the server when answered. The original values and the operation are already there.

So, let's begin. First I decided to model the value handling as enum
enum Operation {
        
        ADDITION("+") {
            @Override
            public long calculate(long val1, long val2) {
                return val1 + val2; 
            }
        },
        
        SUBTRACTION("-") {
            @Override
            public long calculate(long val1, long val2) {
                return val1 - val2; 
            }
        };
        
        private final String name;
        
        private Operation(String name) {
            this.name = name;
        }
        public abstract long calculate(long val1, long val2);
    }

Next the component is needed. First let's create a skeleton of all properties it needs:

public class MathQuestion extends EnhancedElement {
    
    @EmbeddedElement
    private SimpleLabel answer = new SimpleLabel();
    
    private Operation operation;
    
    @EmbeddedAttribute
    private long val1;
    
    @EmbeddedAttribute
    private long val2;
    
    @EmbeddedAttribute
    public String operation() {
        return operation.name;
    }
}

So the component contains the values and the operation as text which are shown to the user.

That component contains also premade component SimpleLabel that is used to contain the actual answer. You may find similar example from tutorial.

Next let's add the rest.
public MathQuestion() {
        tryAgain();
    }
    
    @RemoteMethod
    public void tryAgain() {
        Random rand = new Random();
        val1 = rand.nextInt(201) - 100;
        val2 = rand.nextInt(201) - 100;
        operation = Operation.values()[rand.nextInt(2)];
        answer.setValue("");
        refresh();
    }
    
    @RemoteMethod
    public void guess(Long guess) {
        if (guess != null) {
            if (guess == operation.calculate(val1, val2)) {
                answer.setValue("Your answer was correct :-)");
            } else {
                answer.setValue("Your answer was incorrect :-(");
            }
            answer.refresh();
        }
    }

Now, I have added to remote methods that can be called from the client. The tryAgain() simply resets the values and refreshes the component. The guess() takes a guess from the client and does the calculation to check if the answer is correct. Then it sets a proper message to answer-label and refreshes it.

Ok, that was the Java-side. Next the template is needed:

<xsl:template match="MathQuestion">
  <div id="{@id}">
    <xsl:call-template name="MathQuestion" />
  </div>
</xsl:template>

<xsl:template match="MathQuestion.update">
  <replaceInner id="{@id}">
    <xsl:call-template name="MathQuestion" />
  </replaceInner>
</xsl:template>

<xsl:template name="MathQuestion">
  <xsl:value-of select="@val1" />
  &#160; 
  <xsl:value-of select="@operation" />
  &#160;
  <xsl:value-of select="@val2" />
  &#160; = &#160;
  <input type="text" size="3" id="{@id}_guess" />
  
  <input type="button" class="button" value="Answer"
    onclick="contextfw.call('{@id}', 'guess', $('#{@id}_guess').val());" />
  
  &#160;<xsl:apply-templates select="answer" />
  <br/>
  <input type="button" class="button" value="Try again"
    onclick="contextfw.call('{@id}', 'tryAgain');" />
  
</xsl:template>

That is about it. The template is quite straight forward. All the properties that were bound as attributes are shown, and elements are put to the desired places. Then with some jQuery answer is read from input and then the request is made.

No comments:

Post a Comment