address validation using google maps - meteor

I'm trying to do address validation using google api within a Meteor Platform. What's the best way to achieve that?
I tried this but I'm facing a lot of trouble. Is there a more structured way to do that in Meteor?
<div class="page">
<div id="main">
<form id="MyForm" name="MyForm" action="form.html">
<div id="map_canvas" style="float: right; height: 200px; width: 400px;"></div>
<div>
<label for="Name">
Name</label>
<input id="Name" name="Name" type="text" />
</div>
<div>
<label for="FullAddress">
Address</label>
<textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
</div>
<div>
<input id="Submit" name="Submit" value="Submit" type="button" />
</div>
</form>
</div>
</div>
<script type="text/javascript">
// The following code show execute only after the page is fully loaded
$(document).ready(function () {
if ($('#MyForm').exists()) {
// Enable jQuery Validation for the form
$("#MyForm").validate({ onkeyup: false });
// Add validation rules to the FullAddress field
$("#FullAddress").rules("add", {
fulladdress: true,
required: true,
messages: {
fulladdress: "Google cannot locate this address."
}
});
// This function will be executed when the form is submitted
function FormSubmit() {
$.submitForm = true;
if (!$('#MyForm').valid()) {
return false;
} else {
if ($("#FullAddress").data("IsChecking") == true) {
$("#FullAddress").data("SubmitForm", true);
return false;
}
alert('Form Valid! Submit!');
// return true; // Uncomment to submit the form.
return false; // Supress the form submission for test purpose.
}
}
// Attach the FormSubmit function to the Submit button
if ($('#Submit').exists()) {
$("#Submit").click(FormSubmit);
}
// Execute the ForumSubmit function when the form is submitted
$('#MyForm').submit(FormSubmit);
}
});
// Create a jQuery exists method
jQuery.fn.exists = function () { return jQuery(this).length > 0; }
// Position the Google Map
function Map(elementId, geolocation) {
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById(elementId), myOptions);
map.setCenter(geolocation);
}
// FullAddress jQuery Validator
function FullAddressValidator(value, element, paras) {
// Convert the value variable into something a bit more descriptive
var CurrentAddress = value;
// If the address is blank, then this is for the required validator to deal with.
if (value.length == 0) {
return true;
}
// If we've already validated this address, then just return the previous result
if ($(element).data("LastAddressValidated") == CurrentAddress) {
return $(element).data("IsValid");
}
// We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
$(element).data("IsChecking", true);
$(element).data("LastAddressValidated", CurrentAddress);
// Google Maps doesn't like line-breaks, remove them
CurrentAddress = CurrentAddress.replace(/\n/g, "");
// Create a new Google geocoder
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': CurrentAddress }, function (results, status) {
// The code below only gets run after a successful Google service call has completed.
// Because this is an asynchronous call, the validator has already returned a 'true' result
// to supress an error message and then cancelled the form submission. The code below
// needs to fetch the true validation from the Google service and then re-execute the
// jQuery form validator to display the error message. Futhermore, if the form was
// being submitted, the code below needs to resume that submit.
// Google reported a valid geocoded address
if (status == google.maps.GeocoderStatus.OK) {
// Get the formatted Google result
var address = results[0].formatted_address;
// Count the commas in the fomatted address.
// This doesn't look great, but it helps us understand how specific the geocoded address
// is. For example, "CA" will geocde to "California, USA".
numCommas = address.match(/,/g).length;
// A full street address will have at least 3 commas. Alternate techniques involve
// fetching the address_components returned by Google Maps. That code looks even more ugly.
if (numCommas >= 3) {
// Replace the first comma found with a line-break
address = address.replace(/, /, "\n");
// Remove USA from the address (remove this, if this is important to you)
address = address.replace(/, USA$/, "");
// Check for the map_canvas, if it exists then position the Google Map
if ($("#map_canvas").exists()) {
$("#map_canvas").show();
Map("map_canvas", results[0].geometry.location);
}
// Set the textarea value to the geocoded address
$(element).val(address);
// Cache this latest result
$(element).data("LastAddressValidated", address);
// We have a valid geocoded address
$(element).data("IsValid", true);
} else {
// Google Maps was able to geocode the address, but it wasn't specific
// enough (not enough commas) to be a valid street address.
$(element).data("IsValid", false);
}
// Otherwise the address is invalid
} else {
$(element).data("IsValid", false);
}
// We're no longer in the midst of validating
$(element).data("IsChecking", false);
// Get the parent form element for this address field
var form = $(element).parents('form:first');
// This code is being run after the validation for this field,
// if the form was being submitted before this validtor was
// called then we need to re-submit the form.
if ($(element).data("SubmitForm") == true) {
form.submit();
} else {
// Re-validate this property so we can return the result.
form.validate().element(element);
}
});
// The FullAddress validator always returns 'true' when initially called.
// The true result will be return later by the geocode function (above)
return true;
}
// Define a new jQuery Validator method
$.validator.addMethod("fulladdress", FullAddressValidator);
</script>

To achieve this in a Meteor way you should put your html into a template:
<template name="tplExample">
<div class="page">
{{somedata}}
</div>
</template>
Then you can write your events and helpers for this template in a .js file:
Template['tplExample'].events({
//this scopes just elements from tplExample
"submit #Myform": function(e){/*handle the event*/ }
})
Template['tplExample'].rendered = function(){
// The following code show execute only after the page is fully loaded
}
Template['tplExample'].helpers({
//this scopes just elements from tplExample
some_data: function(){ /*handle data validation*/}
})
Take a look here https://www.discovermeteor.com/blog/a-look-at-a-meteor-template/

Related

Fullcalendar using resources as a function with select menu

Using Fullcalendar 4, I am trying to show/hide my resources using a select menu. When the user selects one of the providers from a menu, I want to only show that one resourc's events.
Above my fullcalendar I have my select menu:
<select id="toggle_providers_calendar" class="form-control" >
<option value="1" selected>Screech Powers</option>
<option value="2">Slater</option>
</select>
I am gathering the resources I need using an ajax call on my included fullcalendar.php page. I am storing them in an object and then trying to control which resources are shown onscreen:
document.addEventListener('DOMContentLoaded', function() {
var resourceData = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
console.log(resourceData);
});
//below, set the visible resources to whatever is selected in the menu
//using 1 in order for that to show at start
var visibleResourceIds = ["1"];
//below, get the selected id when the the menu is changed and use that in the toggle resource function
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
var calendar_full = document.getElementById('calendar_full');
var calendar = new FullCalendar.Calendar(calendar_full, {
events: {
url: 'ajax_get_json.php?what=location_appointments'
},
height: 700,
resources: function(fetchInfo, successCallback, failureCallback) {
// below, I am trying to filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
...
});
// below, my toggle_providers_calendar will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
To make sure the getJSON is working, I have console.log(resourceData). The information in the console once it's gathered is:
[{id: '1', title: 'Screech Powers'}, {id: '2', title: 'Slater}]
... the above are the correct resources that can be chosen/rendered. So that seems to be okay.
On page load, no resources show at all, when resource id of '1' (Screech Powers) should be shown per my code. Well, at least, that's what I am trying to do right now.
When the menu changes, resources will show/hide, but not based on what's selected; the logic of only showing what is selected in the menu doesn't seem to be working.
I used to use a URL request for my resources: 'ajax_get_json.php?what=schedule_providers_at_location', and it worked fine! All resources show then their events properly. I am just trying to modify it by using a menu to show/hide the resources as needed.
Here's what I'm doing to make it happen so far! In case someone comes across this post ever, this will help.
Here's my code before my fullcalendar code.
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
My select menu with id 'toggle_providers_calendar' is the same as my original post. My fullcalendar resources as a function is the same too.
After the calendar is rendered, here are the changes I made to my toggle resources function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
This causes the resources to show and hide properly. If the user selects "Show All" that works too!
In order to have a default resource show on load, I add this to my fullcalendar script:
loading: function(bool) {
if (bool) {
//insert code if still loading
$('.loader').show();
} else {
$('.loader').hide();
if (initial_load) {
initial_load = false;
//code here once done loading and initial_load = true
var default_resource_to_show = "<?php echo $default_provider; ?>";
if (default_resource_to_show) {
//set the menu to that provider and trigger the change event to toggleresrource()
$('#toggle_providers_calendar').val(default_provider).change();
} else {
//pass in nothing meaning 'select all' providers for scheduler to see
toggleResource();
}
}
}
},
I am using a bool variable of initial_load to see if the page was just loaded (basically not loading data without a page refresh). The bool of initial_load = true is set outside of DOMContentLoaded
<script>
//show selected date in title box
var initial_load = true;
document.addEventListener('DOMContentLoaded', function() {
My only current problem is that when toggleResource function is called, the all day vertical time block boundaries don't line up with the rest of the scheduler. Once I start navigating, they do, but I don't understand why it looks like this on initial load or when toggleResource() is called:
Any thoughts on how to correct the alignment of the allday vertical blocks?

Removing a window event listener in Template.onDestroyed

I have two templates that each contain a Vimeo iframe player. I'm using FlowRouter to render the templates through {{> Template.dynamic template=main}} on the main layout.
In both templates I add listeners for video events in onCreated
Template.view.onCreated( function() {
var self = this;
if (window.addEventListener) {
window.addEventListener('message', function(event) {
viewMessageReceived(event, self)}, false);
} else {
window.attachEvent('onmessage', function(event){
viewMessageReceived(event, self)}, false);
}
});
and destroy them in onDestroyed
Template.view.onDestroyed( function() {
if (window.removeEventListener) {
console.log('view remove');
window.removeEventListener('message', function(event) {
viewMessageReceived(event, self)}, false);
} else {
window.detachEvent('onmessage', function(event){
viewMessageReceived(event, self)}, false);
}
});
And here is the function being called by the anonymous event handler:
function viewMessageReceived(event, self) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
return false;
}
if (self.playerOrigin === '*') {
self.playerOrigin = event.origin;
}
var data = JSON.parse(event.data);
switch (data.event) {
case "ready":
initializePlayer(self);
break;
case "playProgress":
self.playerTime.set(data.data.seconds);
if (self.duration === '*') self.duration = data.data.duration;
break;
case "play":
self.playerStatus.set("playing");
break;
case "pause":
self.playerStatus.set("paused");
break;
}
}
When I switch to a different template, onDestroyed runs and my console.log('view remove') fires, as expected.
But then when I navigate to the page that loads the other template with a video player, a Vimeo "playProgress" message arrives that is received by the event handler in the previous video template, which was supposed to have been removed a while ago. This throws an error because the previous template has been destroyed.
Uncaught TypeError: Cannot read property 'contentWindow' of undefined
which comes from the last line in this function:
function post(template, action, value) {
console.log('view action: %s value: %s', action, value);
var data = {method: action};
if (value) data.value = value;
var message = JSON.stringify(data);
template.player[0].contentWindow.postMessage(message, template.playerOrigin);
}
Each of the video-containing templates have their own .js file, so they each have a their own post function declaration. My understanding is that defining a function that way scopes the function just to the page.
It's only one message that arrives for the wrong player. After that, they arrive for the currently loaded player.
Why does the Vimeo message event arrive or get handled after I've already destroyed the template and when I've moved to another player?
A quote from the W3Schools website regarding the removeEventListener() method:
// Attach an event handler to <div>
document.getElementById("myDIV").addEventListener("mousemove", myFunction);
// Remove the event handler from <div>
document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
Note: To remove event handlers, the function specified with the
addEventListener() method must be an external function, like in the
example above (myFunction).
Anonymous functions, like "element.removeEventListener("event",
function(){ myScript });" will not work.
For this to work, you'll need to move your function definition somewhere outside of the onRendered and onDestroyed events, and just pass the function name in the add/remove event listeners.

How to catch a user input

I need to catch an user's input, precisely one specific button. I caught this in this way
Template.main.events({
'keypress input': function (e) {
if (e.charCode === 32) {
console.log("Hit");
};
}
});
and in the template it's something like this
<template name="main">
{{test_var}}
<input type="text">
</template>
It's works, but i need it without an input box on a page.
Template events are restricted to the piece of DOM contained within the template, and within that piece only form elements respond to keyboard events. To capture global keyboard events, you should use jQuery.
Template.main.rendered = function() {
$(document).on('keypress.mainTemplate', function() {
...
});
};
Template.main.destroyed = function() {
$(document).off('keypress.mainTemplate');
});

How to implement editable text in Meteor and DRY?

I have come up with a methodology for making editable text in my Meteor app. However, it does not follow the DRY paradigm and I'd like to change that but I am not too good with Javascript yet...
Suppose I have a table cell with some text and I'd like to double click it to edit it. I created a template variable to handle this:
<td class="itemName">
{{#unless editItemName}}
{{name}}
{{else}}
<input class="editItemName" type="text" value="{{name}}" style="width:100px;">
{{/unless}}
</td>
I then create an event to execute this transition on a double-click:
Template.inventoryItemDetail.events = {
'dblclick td.itemName': function (evt) {
Session.set("editItemName",true);
},
'blur input.editItemName': function () {
Session.set("editItemName",null);
},};
I also reused the ok_cancel code from the ToDo's example app (but that's sort of irrelevant):
// Returns an event_map key for attaching "ok/cancel" events to
// a text input (given by selector)
var okcancel_events = function (selector) {
return 'keyup '+selector+', keydown '+selector+', focusout '+selector;
};
// Creates an event handler for interpreting "escape", "return", and "blur"
// on a text field and calling "ok" or "cancel" callbacks.
var make_okcancel_handler = function (options) {
var ok = options.ok || function () {};
var cancel = options.cancel || function () {};
return function (evt) {
if (evt.type === "keydown" && evt.which === 27) {
// escape = cancel
cancel.call(this, evt);
evt.currentTarget.blur();
} else if (evt.type === "keyup" && evt.which === 13) {
// blur/return/enter = ok/submit if non-empty
var value = String(evt.target.value || "");
if (value) {
ok.call(this, value, evt);
evt.currentTarget.blur();
}
else {
cancel.call(this, evt);
evt.currentTarget.blur();
}
}
};
};
Template.inventoryItemDetail.events[ okcancel_events('input.editItemName') ] = make_okcancel_handler({
ok: function (value) {
Items.update(this._id, {$set: {name: value}});
}
});
Finally, I have to tie this Session variable to the template variable:
Template.inventoryItemDetail.editItemName = function () {
return Session.get("editItemName");
};
So right now, I have repeated all of this again and again for each editable text field and it all works, but it seems like terribly programming practice. I have found various editable text utilities on Github but I don't entirely understand them and none of them are for Meteor!
I'd really like to expand my knowledge of Meteor and Javascript by creating a tool that allows me to have editable text without repeating myself this ridiculous amount for each editable text field.
Thanks,
Chet
https://github.com/nate-strauser/meteor-x-editable-bootstrap for the package.
http://vitalets.github.io/x-editable/docs.html for the docs.
I just implemented this in my project and I won't ever go back to contenteditable.

How to trigger a validation on only Button Click or Input:Submit

I am using Update Panel in my asp page and I am doing JQuery Validation on Asynchronous Postback...
I just want to validate my form on only button clicks or submits...
My problem is..all my buttons are in different formviews and won't load at a time...that's why I am unable to take the button id's and use the click events..here is my code..
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(ValidateMyForm);
function ValidateMyForm(sender, args) {
var objPost = args.get_postBackElement();
if (objPost === null || objPost === undefined) return;
if (objPost.id == '<%= ((Button)(formViewinfo.FindControl("btnUpdate"))).ClientID %>') {
$('#pnlerrors').fadeOut('fast');
$('#pnlItemErrors').fadeOut('fast');
var isValid = $('#form1').validate({
errorClass: 'error',
invalidHandler: function (e, validator) {
var errors = validator.numberOfInvalids();
if (errors) {
$('#pnlerrors').html('<p> Please correct the errors </p>').fadeIn('fast');
document.location.href = '#pnlerrors';
}
}, submitHandler: function () {
}
}).form();
if (!isValid) {
CancelPostback(sender, args);
} else {
}
}
//this is for rest of buttons
else {
$('#pnlItemErrors').fadeOut('fast');
$('#pnlerrors').fadeOut('fast');
var isValid = $('#form1').validate({
errorClass: 'error',
invalidHandler: function (e, validator) {
var errors = validator.numberOfInvalids();
if (errors) {
$('#pnlerrors').fadeOut('fast');
document.location.replace('#', '#pnlItemErrors');
$('#pnlItemErrors').html('<p> Please correct the errors </p>').fadeIn('fast');
document.location.href = '#pnlItemErrors';
}
}, submitHandler: function () {
}
}).form();
if (!isValid) {
CancelPostback(sender, args);
} else {
}
}
}
All I want to do is: 2nd time validation on only button submit not for everything...I do get other postbacks on this page and those post backs also gets validated each time (I want to Avoid this)...
I don't know this approach is good or not...I am struggling with this from long time..I really appreciate you help...
On the assumption that you don't want to submit the form when someone presses the enter button, and that you only want to submit the form on pressing a submit button:
$(document).ready(
function(){
$('form').keypress(
function(event){
if (event.keyCode == '13'){
return false;
}
});
$('input:submit').click(
function(){
$(this).closest('form').submit();
});
$('form').submit(
function(){
$('#success').text('form submitted! (Not really...)');
return false;
// Just to stop the error messages
// in this demo.
});
});
There's a JS Fiddle demo, here: http://jsfiddle.net/davidThomas/5PaWz/.
If I'm mistaken in my assumptions, please leave a comment and I'll try to correct myself.
if your problem is just about finding the buttons the need to have validations then
one way of getting around this is to add a class to the buttons that you want to trigger validation, for example :
<asp:button id="btn1" cssclass="Validate"/>
then you can grab all these buttons in JQuery:
var buttons = $('.Validate');
get each button id:
$(buttons).each(function(){
var id = this.id;
});
ohh..god finally found the solution for my problem...First of all my apologizes if my question is not clear....
My validation works on asynchronous post backs...I just want validate my form on button clicks..i do have an asp.net grid view in my page..if i click on paging or something on the grid it fires validation...i want avoid this..
for this what i did is...i am capturing the postback element with the following statement.
var objPost = args.get_postBackElement();
then i am checking for type..
if (objpost.type == 'submit') { do validation }
else { don't }..
this ends my 2days struggle...
thank you very much your support and help...
Try different approach.
Use asp.Net Button with UseSubmitBehavior=true for submission
and use asp.Net Button with UserSubmitBehavior=false for buttons that you don't want them to fire the validation process that. add this following code to your form
function ValidateForm()
{
var errors ="";
if (typeof(Page_ClientValidate) == 'function')
{
if (typeof (Page_ClientValidate) == 'function') { Page_ClientValidate(); }
if (!Page_IsValid)
{
for (i = 0; i < Page_Validators.length; i++) {
var inputControl = document.getElementById(Page_Validators[i].controltovalidate);
if (!Page_Validators[i].isvalid) {
errors = errors + ";" + Page_Validators[i].errormessage;
inputControl.style.border ="solid 2px #FF0000";
}
}
}
return Page_IsValid;
}
return true;
}
$(document).ready(function(){
/*********************************************************************/
///handle form submittion and run validation prior to that
///if a textbox has required field validator, stop form submittion and
/// highlight the text box
/*********************************************************************/
$('#form1').submit(function(){
return ValidateForm();
});

Resources