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?)
George Katsanos
  • 13,524
  • 16
  • 62
  • 98

5 Answers5

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@email.com'
    // }

    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@email.com'
    // }

    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);
        }
    });
}
Tarang
  • 75,157
  • 39
  • 215
  • 276
  • 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.. – George Katsanos Mar 04 '13 at 17:38
  • 1
    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 framework – Tarang Mar 04 '13 at 17:53
  • 1
    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. – David Weldon Mar 04 '13 at 18:26
  • Thanks @DavidWeldon. I have modified the code to reflect this. – Tarang Mar 04 '13 at 18:29
  • @GeorgeKatsanos I have also modified the code so there is a purely javascript version too. No jquery at all. – Tarang Mar 04 '13 at 18:29
  • @DavidWeldon is it without `[0]` because the selector is to an id, there can only be one result? – Tarang Mar 04 '13 at 18:35
  • @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](http://stackoverflow.com/questions/6653556/jquery-javascript-function-to-clear-all-the-fields-of-a-form) question. – David Weldon Mar 04 '13 at 19:03
  • some questions.. Do I need to add the jquery package manually? http://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 ? – George Katsanos Mar 04 '13 at 19:16
  • 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. – Tarang Mar 04 '13 at 19:20
  • 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' – George Katsanos Mar 04 '13 at 19:21
  • forget my last comment -forgot to change the id :-) (answer accepted, thanks..) – George Katsanos Mar 04 '13 at 19:22
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 */ });

}});
Community
  • 1
  • 1
Andrew Mao
  • 35,740
  • 23
  • 143
  • 224
  • How do you do the selector when you have a radio button? Would it be template.find( 'input:radio[name="myNameForRadio"]:checked()' ) ? – chongman Feb 20 '14 at 15:42
  • 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 ? – chongman Feb 20 '14 at 15:45
  • I figured it out. There are just different selectors. – chongman Feb 20 '14 at 15:56
  • 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. – chongman Feb 20 '14 at 16:03
  • 1
    @chongman 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. – Andrew Mao Feb 20 '14 at 16:24
  • Using `template.find(query)` is the way to go, just noting that it is the same as `$(query, event.target)`. – B M Jun 22 '15 at 10:25
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; 
    }
});
avishek gurung
  • 188
  • 2
  • 7
  • You can also just return false from the function instead of including `event.preventDefault()`. From the [docs](http://docs.meteor.com/#/full/eventmaps): "Returning false from a handler is the same as calling both stopImmediatePropagation and preventDefault on the event." – Grant Sep 06 '15 at 00:28
  • 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.](https://stackoverflow.com/questions/5921413/difference-between-e-target-and-e-currenttarget) – robertdavid Aug 30 '19 at 08:31
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.

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.

Joe
  • 11,147
  • 7
  • 49
  • 60