Meteor Reactivity Playground 2.0
HTML
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="http://www.chicagogrooves.com/js/mini-meteor.min.js"></script>
<!--
Check out the video explaining these at:
http://bit.ly/meteor-form-validation-video
-->
<form>
<h1>Meteor Reactive Form Validation</h1>
<div id="error-messages" class="error">
Please review the required fields:
</div>
<div>
<label for="question_11">
<span class="required">*</span>
What is your name:
</label>
<input name="question_11" id="question_11" autofocus="1"/>
</div>
<div>
<label for="question_22">What is your BFF's name:</label>
<input name="question_22" id="question_22"/>
</div>
<div>
<label for="question_33">
<span class="required">*</span>
You are a child of the:
</label>
<select name="question_33" id="question_33">
<option value="">Select One</option>
<option value="2000">2000's</option>
<option value="1990">1990's</option>
<option value="1980">1980's</option>
<option value="1970">1970's</option>
<option value="1960">1960's</option>
<option value="1950">1950's</option>
<option value="-1">an earlier era</option>
</select>
</div>
<div>
<input type="submit"/>
</div>
</form>
<a id="fs" style="float: left; margin-top: 2em" href="http://jsfiddle.net/chicagogrooves/y18er7wr/embedded/result/" target="_blank">Launch Full Screen -></a>
<a id="sc" style="float: right; margin-top: 2em" href="http://bit.ly/meteor-form-validation-video" target="_blank">See Screencast</a>
CSS
body{ margin-top: 25px; margin-left: 10px;}
.error, .required{ color: red; }
input[type=submit]{ margin-top: 20px; }
input, select{ margin-right: 20px; }
JavaScript
// keys are ids, values are true if in error
var errorStates = initErrorStates();
// define and wireup event handler
$("form").on("submit", validationHandler);
function validationHandler(e){
validateFields();
console.log(errorStates.allValid() ? "will submit" : "no submit 4 you");
if(e.type=="submit"){ e.preventDefault(); }
};
// set up an instance of ReactiveDict suitable
// for initial and future validation
// (for display purposes, fields start *not* in error)
function initErrorStates(){
var states = new ReactiveMap();
states.set(11, false);
states.set(33, false);
states.allValid = function(){
for(var key in states.keys){
if(states.get(key)==true)
return false;
}
return true;
}
return states;
}
// a simple loop, setting errors for any ids lacking a value
function validateFields(){
for(var key in errorStates.keys){
errorStates.set(key, false);
var answer=$("[name=question_" + key + "]").val();
if(!answer){
errorStates.set(key, true);
}
}
}
function setFieldState(key){
var inError = errorStates.get(key);
var methodName = inError ? "addClass" : "removeClass";
var label = $("[for=question_" + key + "]");
label[methodName]("error");
}
function setFormState(){
var style = errorStates.allValid() ? "hidden" : "visible";
$("#error-messages").css("visibility", style);
}
function reactify(){
//set up reactivity for form as a whole
Meteor.run(setFormState);
// set up reactivity for each field
for(var key in errorStates.keys){
Meteor.run(setFieldState.bind({}, key));
}
}
reactify();