Autoform Update : "vc.resetValidation is not a function" - meteor

I'm on
aldeed:collection2-core
aldeed:autoform
aldeed:schema-deny
npm simpl-schema
I get the error vc.resetValidation is not a function when I update a form on the user schema.
The form is effectively submitted - the update is well made.
How can I fix this error ?
Here is my query
{{#autoForm collection='Meteor.users' doc=currentUser type='update' id='accountForm'}}
{{> afFieldInput name='profile.phone'}}
{{> afFieldInput name='profile.avatar'}}
<button type='submit' class="at-btn dark">Update</button>
{{/autoForm}}
Here is the part of the autoform code where I think there is a validation issue
/// Reset array counts
arrayTracker.resetForm(formId); var vc = AutoForm.getValidationContext(formId);
if (vc) {
vc.resetValidation();
// If simpleSchema is undefined, we haven't yet rendered the form, and therefore
// there is no need to reset validation for it. No error need be thrown.
}
if (this.doc) {
event.preventDefault();
AutoForm._forceResetFormValues(formId);
}

You can call resetForm from onSubmit or in fact from onSuccess hooks.
AutoForm.addHooks(['form_id'], {
onSuccess: function(formType, result) {
this.resetForm()
}
}) ;
Documentation

Related

Meteor Autoform validateForm ignores unique

The method AutoForm.validateForm(formID) returns true although unique is true in SimpleSchema and a duplicate value is entered. Nobody else seems to have this issue so I wonder what I'm doing wrong. This is the full sample code:
common/collections.js
import SimpleSchema from 'simpl-schema';
SimpleSchema.extendOptions(['autoform']);
const afCollection = {};
Meteor.isClient && Template.registerHelper('afCollection', afCollection);
checkTable = afCollection.checkTable = new Meteor.Collection('checkTable');
// Meteor.isServer && checkTable._dropCollection();
checkTable.attachSchema(new SimpleSchema({
checkValue: {
type: String,
index:true,
unique:true,
optional:false
}
}, { tracker: Tracker }));
client/maintenance.js
AutoForm.debug();
Template.Maintenance.events({
'click .save' () {
if (AutoForm.validateForm("newOne")) {
$('form#newOne').submit() }
else {
console.log("should see error message now")
};
console.log("Saved:",checkTable.find().fetch())
}
});
client/maintenance.html
<template name="Maintenance">
<a class='save' href=#>Save</a>
{{#autoForm id='newOne' type="insert" collection=afCollection.checkTable autosave=false }}
{{> afQuickField name="checkValue"}}
{{/autoForm}}
</template>
packages:
aldeed:autoform#6.2.0
aldeed:collection2-core#2.0.4
aldeed:schema-index#2.1.1
validateForm works correctly in case of input is empty. In case of unique is violated, validateForm returns true. When you call .submit(), the error message in the template is displayed correctly and you could react on the error using an AutoForm.hook (probably, not tested).
Unfortunately this does not help in my situation, because clicking on "save" will submit several forms at once. I must ensure that all forms are error-free before the first one is submitted.
What am I missing?

Use Flow Router Param in Autoform

Friends,
I'm working on my first app in Meteor and hitting my head against the wall on something...
I have a scenario similar to a blog + comments situation where I have one collection (call it 'posts') and want to associate documents from another collection (call it 'comments').
The best way I know to pass the post._id to the comments as a "postId" field is to use the Flow Router params, since the form is on the 'post/:id' view.
But for the life of me, I cannot figure out how to get "var postId = FlowRouter.getParam('postId');" to pass to Autoform so it populates. I've tried adding it as a function in the schema, as a hook, and as a hidden field in the form on the page (obviously don't want to go that route).
Autoform is amazing and I want to use it, but may have to wire it up the hard way if I can't get this darn value to populate.
Any ideas? I've been hitting my head against the wall on this for a couple of days now.
Thanks!
First, just so we're on the same page, if you have your route is set up like this:
FlowRouter.route('/blog/:postId', {
action: function (params, queryParams) {
FlowLayout.render('layout', { body: 'postTemplate' });
},
});
You are able to call FlowRouter.getParam('postId') from inside the AutoForm hook
You'll need to use an AutoForm hook and have a complete schema. I'm using the package aldeed:collection2 for the schema set up. The postId field must be explicity declared. This code is running on both server and client.
Comments = new Mongo.Collection("comments");
Comments.attachSchema(new SimpleSchema({
comment: {
type: String,
label: "Comment"
},
postId: {
type: String
}
}));
Setting your form up like this is not what you want:
{{> quickForm collection="Comments" id="commentForm" type="insert"}}
That's no good because it will show the postId field in the HTML output. We don't want that, so you have to fully define the form like this:
{{#autoForm collection="Comments" id="commentForm" type="insert"}}
<fieldset>
{{> afQuickField name='comment' rows=6}}
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
Then add the AutoForm hook. This code is running on the client.
var commentHooks = {
before: {
insert: function(doc){
var postId = FlowRouter.getParam('postId');
doc.postId = postId;
return doc;
}
}
};
AutoForm.addHooks(['commentForm'],commentHooks);
Make sure you have your allow/deny rules set up, and it should be working fine.
I was struggling with this same use case as well, and I found this on the Meteor forums: https://forums.meteor.com/t/use-flow-router-param-in-autoform/14433/2
If you're using a schema to build your form (either with the autoform or quickform tags) then you can put it right in there.
For example:
campaignId: {
type: String,
autoform: {
value: function() {
return FlowRouter.getParam('campaignId');
},
type: "hidden"
}
},

Best way to prevent a template helper to be rerun when it is unnecessary?

I'm trying to prevent a template helper to be rerun when it is unnecessary. I made a simple application to illustrate this behavior:
Let's say I want to display some items that contain only a title and a description.
<template name="Tests">
{{#each items}}
{{> TestsItems}}
{{/each}}
</template>
<template name="TestsItems">
<div class="title">{{title}}</div>
<div class="description">{{description}}</div>
</template>
I have autopublish enabled.
Template.Tests.helpers({
items: function () {
return Items.find();
}
});
Template.TestsItems.helpers({
description: function () {
// I'm using this helper to do some updates
// on a jQuery plugin when the description field change.
// see example 1: https://github.com/avital/meteor-ui-new-rendered-callback/
console.log("The description is run");
return this.description;
}
});
When a new update is made on the title field only, you can see that the description helper is rerun. What I'm trying to achieve is to only rerun this helper when there is a new value for the description field and not every time a field has changed in the document.
As {{#constant}} and {{#isolate}} are deprecated, how can I get this behavior in the latest Meteor versions?
Note 1: Create a new subtemplate including the description does not fix the problem.
I would avoid side effects in template helpers. Instead I would use an autorun:
Template.TestItems.rendered = function () {
var _id = this.data._id;
this.autorun(function () {
// Select only the description field, so that we only
// trigger a re-run if the description field changes
var description = Items.findOne(_id, {fields: {description: 1}}).description;
// update the JQuery plugin
});
}

Meteor: how do I create a post insert hook?

I'm using autoform to create forms. I have the template below which is correctly inserting data into the collection when the form is submitted. What I want to do is insert a record into another collection when the insert into the "ContactDetails" collection has been completed successfully.
<template name="contactDetailsForm">
{{#if submitted}}
{{> quickForm collection="ContactDetails" omitFields="createdBy" doc=editingDoc id="contactDetailsForm" type="update"}}
{{else}}
{{> quickForm collection="ContactDetails" omitFields="createdBy" id="contactDetailsForm" type="insert"}}
{{/if}}
</template>
As far as I know I would need to add a hook. I'm really not sure what I'm doing with this. I'd imagine it would look something like this:
AutoForm.addHooks(['contactDetailsForm'], {
after: {
insert: function(error, result) {
if (error) {
console.log("Insert Error:", error);
} else {
console.log("Insert Result:", result);
// NOW DO INSERT INTO OTHER COLLECTION
}
}
}
});
Can anyone show me how to insert a record into another collection after an insert has been successfully completed in a different collection?
Any advice/help/examples on this would be sincerely appreciated.
The matb33:collection-hooks package is a standard way to create such hooks. First add it with
meteor add matb33:collection-hooks
Then create your hook:
ContactDetails.after.insert(function(userId, doc) {
console.log("Inserted:", this._id);
...
});
In general the hooks are not available yet - but they are in autoform:
https://github.com/aldeed/meteor-autoform#callbackshooks
If you were not using autoform I would do the insert with a Meteor method, where you could just do the after insert, after you inserted the first one.
See this https://www.discovermeteor.com/blog/meteor-methods-client-side-operations/

in Meteor, how do i update a property on only one instance of a template?

If I have an {{# each}} binding in Meteor, and I want to update a property on only one instance of the template inside the #each. How would I do that? I've tried setting a value on the "template" object inside the events map, but that doesn't seem to be reactive. I've also tried binding to a Session property, but that will cause every instance to update instead of just the one I want...
for example:
{{#each dates}}
{{> dateTemplate}}
{{/each}}
<template name="dateTemplate">
{{date}}
<span style="color: red;">{{errorMsg}}</span> <--- how do i update errorMsg?
</template>
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = 'not valid'; <--- this doesn't do anything
}
});
EDIT TO ADDRESS ANSWER BELOW:
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid';} <--- this also doesn't do anything
}
});
You don't have to use handlebars for this, because its not something that needs reactivity to pass the message through, reactive variables work best with db data, or data that would be updated by another client over the air.
You could use JQuery (included by default) to update it, it can also get a bit fancier:
<template name="dateTemplate">
{{date}}
<span style="color: red;display: none" class="errorMessage"></span>
</template>
Template.dateTemplate.events({
'click': function(event, template) {
$(template.find('.errorMessage')).html('Your Error Message').slideDown();
}
});
Ive edited it so the error is hidden by default, and slides down with an animation
I'm experimenting handling this by passing a different reactive object to each instance of the template. Then the template can bind to the reactive object (which is unique per instance) and we don't have any extra boilerplate.
It ends up looking like this:
Initial render:
Template.firstTemplateWithPoll(ContextProvider.getContext())
Template.secondTemplateWithPoll(ContextProvider.getContext())
// (I actually pass getContext an identifier so I always get the same context for the same template)
JS:
Template.poll.events = {
'click .yes' : function() {
this.reactive.set('selection', 'yes');
},
'click .no' : function() {
this.reactive.set('selection', 'no');
}
};
Template.poll.selection = function(arg) {
return this.reactive.get('selection');
}
Template:
<template name="poll">
<blockquote>
<p>
Your selection on this poll is {{selection}}
</p>
</blockquote>
<button class='yes'>YES</button>
<button class='no'>NO</button>
</template>
template.errorMsg should be a function that returns your error.
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid'; };
}
});

Resources