Submitting a form in Meteor without using extras
Asked Answered
N

5

12

I would like to have a form in my Meteor html template and on submit insert that data to my MongoDB list. My questions are:

  • Is this possible without using extra packages? Could I just add an HTML form as a template?
  • Does the on submit event work for latest Meteor?
  • I've read that we can use the click event for the submit button: Could you tell me how I would go for finding in my DOM the values of my input elements? (without using jQuery?)
Neurosis answered 4/3, 2013 at 16:4 Comment(0)
D
16

JQuery is included in meteor & very simplifying, why would you want to avoid it? Its quite long to traverse the dom manually with js

You could use javascript to submit your forms ajax style:

So you could just use normal form html in your template like usual:

// HTML
<form id="myform">
    <input type="text" name="firstname"/>
    <input type="text" name="lastname"/>
    <input type="text" name="email"/>
    <input type="submit" id="submit"/>
</form>


// Client JS
function submitme() {
    form = {};

    $.each($('#myform').serializeArray(), function() {
        form[this.name] = this.value;
    });

    // Do validation on 
    // form = {
    //     firstname: 'first name',
    //     lastname: 'last name', 
    //     email: '[email protected]'
    // }

    MyCollection.insert(form, function(err) {
        if(!err) {
            alert("Submitted!");
            $('#myform')[0].reset();
        } else {
            alert("Something is wrong");
            console.log(err);
        }
    });

}

You could bind the select button to insert the data when clicked:

Event map binding:

Template.templatename.events({
    'submit' : function(event) {
        event.preventDefault(); //prevent page refresh
        submitme();
    }
});

If you HATE jQuery & cant use it at all

// HTML
<form id="myform">
    <input id="firstname" type="text" name="firstname"/>
    <input id="lastname" type="text" name="lastname"/>
    <input id="email" type="text" name="email"/>
    <input type="submit" id="submit"/>
</form>

// Client JS
function submitme() {

    form = {
        firstname: firstname.value,
        lastname: lastname.value,
        email: email.value
    };

    // Do validation on 
    // form = {
    //     firstname: 'first name',
    //     lastname: 'last name',
    //     email: '[email protected]'
    // }

    MyCollection.insert(form, function(err) {
        if (!err) {
            alert("Submitted!");

            // Empty field values
            email.value = "";
            firstname.value = "";
            lastname.value = "";
        } else {
            alert("Something is wrong");
            console.log(err);
        }
    });
}
Devalue answered 4/3, 2013 at 17:22 Comment(11)
well if I do jQuery event and jQuery DOM traversing then I should be doing jQuery, not meteor.. right? I mean, I wanted to know if Meteor has efficient DOM traversing itself..Neurosis
I wouldn't really say meteor is a javascript framework like that, its not built to replace JQuery rather to use put everything together, its still meteor putting the data in reactively with latency compensation & jquery traversing the DOM. Its inserted instantly which jquery wouldn't do on its own. Meteor doesn't have any DOM traversing stuff, handlebars, the templating system, is also an external frameworkDevalue
If the template happens to have only one form in it you can just have a submit handler rather than click #submit. Also, you can do $('#myform')[0].reset() rather than manually resetting all form elements.Prolocutor
Thanks @DavidWeldon. I have modified the code to reflect this.Devalue
@GeorgeKatsanos I have also modified the code so there is a purely javascript version too. No jquery at all.Devalue
@DavidWeldon is it without [0] because the selector is to an id, there can only be one result?Devalue
@Akshat No, it needs the [0]. This trick is a little confusing because it mixes jQuery with a native DOM manipulation function. jQuery selectors always return an array, whereas reset is a DOM function and requires an instance of a form. See this question.Prolocutor
some questions.. Do I need to add the jquery package manually? docs.meteor.com/#jquery and 2) why do you onclick="submitme();return false;" since we add this handler and the preventdefault in the template.events ?Neurosis
Only use one of them, If you use the template handler don't use onclick= or else they'll be both be fired, I only used that to make it easy to understand. I've removed it above. If you don't have jquery you can add it with meteor add jquery in your terminal.Devalue
Actually the data are succesfully inserted but what throws an exception (console) is the reset method. Exception while delivering result of invoking '/lists/insert' TypeError {} TypeError: Object [object Object] has no method 'reset'Neurosis
forget my last comment -forgot to change the id :-) (answer accepted, thanks..)Neurosis
M
11

Just for reference, there is a slightly cleaner way to do forms in Meteor without all the global jQuery references, which I find to be messy. It makes code a lot less error prone by scoping to the template, which is provided in the event handler. Here's an example using the form described in the other answer:

Template code:

<template name="foo">
    <form id="myform">
        <input type="text" name="firstname"/>
        <input type="text" name="lastname"/>
        <input type="text" name="email"/>
        <input type="submit" id="submit"/>
    </form>
</template>

Event handler:

Template.foo.events({'submit form' : function(event, template) {
    event.preventDefault();

    firstname = template.find("input[name=firstname]");
    lastname = template.find("input[name=lastname]");   
    email = template.find("input[name=email]");

    // Do form validation

    var data = {
      firstname: firstname.value,
      lastname: lastname.value,
      email: email.value
    };

    email.value="";
    firstname.value="";
    lastname.value="";

    MyCollection.insert(data, function(err) { /* handle error */ });

}});
Mullinax answered 27/12, 2013 at 3:7 Comment(6)
How do you do the selector when you have a radio button? Would it be template.find( 'input:radio[name="myNameForRadio"]:checked()' ) ?Boylan
also, using the Discover Meteor book, they use $(e.target).find('[name=firstname]').val() ; can you please explain the difference between using A) $(e.target) vs B) template ?Boylan
I figured it out. There are just different selectors.Boylan
I figured it out. There are different selectors. $(e.target).find(' ... ' ) uses the .value property, while template uses the val() method. Do you agree? (I'm fairly new and trying to make sure I get terminology correct). Note that $(target) doesn't work either.Boylan
@Boylan this is just the difference between the raw javascript selector and the additional functionality that jQuery provides. Make sure to distinguish between which one you are using.Mullinax
Using template.find(query) is the way to go, just noting that it is the same as $(query, event.target).Imf
P
8

The most simple and easiest solution of all.

// HTML
<template name="formTemplate">
    <form>
        <input type="text" name="firstname"/>
        <input type="text" name="lastname"/>
        <input type="submit" id="submit"/>
    </form>
</template>

// JavaScript
Template.formTemplate.events({
    'submit form': function(event, template) {
        event.preventDefault(); // prevent page reload
        var firstname = event.target.firstname.value;
        var lastname = event.target.lastname.value; 
    }
});
Psia answered 31/1, 2015 at 5:57 Comment(2)
You can also just return false from the function instead of including event.preventDefault(). From the docs: "Returning false from a handler is the same as calling both stopImmediatePropagation and preventDefault on the event."Nones
I like this for common cases. But also I use currentTarget to make sure I'm handling the actual form submissions listener rather than a clicked element which is as I understand it and as discussed here.Confidant
S
1

Wanted to share my way it's real simple:

html:

<form id="update-profile">
  <div class="form-group">
    <input type="text" required class="form-control" name="name" placeholder="Full Name" value="{{profile.name}}">
  </div>
  ...

JS

Template.profileEditModal.events({
      'submit form': function (e, t) {
        e.preventDefault();
        var doc = {};

        t.$('input').each(function () {
          if (this.value) {
            doc[this.name] = this.value;
          }
        });
});

Results in nice and clean Object {name: "...", email: "..."}

And if you're using SimpleSchema do check(doc, Schemas.updateProfile); over ther server for validation.

Snowball answered 12/10, 2015 at 13:24 Comment(0)
A
0

This isn't exactly answering your question. But here's how I'd do it.

  1. meteor add aldeed:autoform

  2. sample schema you already have...

    Books = new Mongo.Collection("books");
    Books.attachSchema(new SimpleSchema({
      title: {
        type: String,
        label: "Title",
        max: 200
      },
      author: {
        type: String,
        label: "Author"
      },
      copies: {
        type: Number,
        label: "Number of copies",
        min: 0
      },
      lastCheckedOut: {
        type: Date,
        label: "Last date this book was checked out",
        optional: true
      },
      summary: {
        type: String,
        label: "Brief summary",
        optional: true,
        max: 1000
      }
    }));

3. books.tpl.jade (from mquandalle:jade)

+quickForm collection="Books" id="insertBookForm" type="method" meteormethod="insertBook"

4.

Meteor.methods({
  'insertBook': function (doc) {
      // server side validation, insert, error etc...
    }
})

This way you get client side validation for free, bootstrap styling for free, customise field visibility or styles if you need to. For 5 minutes of development, this is a pretty good return. I'd use the powerful packages that others have put time and thought into and build rapidly on top of that.

Aldose answered 13/6, 2015 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.