Meteor Autoform validateForm ignores unique - meteor

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?

Related

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

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

With Iron Router, Find() and FindOne()

I loaded Iron:Router with my Meteor, and now I'm having difficulties loading both all of a collection, and one specific entry.
What I'm trying to do: In the Nav Bar, I want to list all of a user's previous entries. I got that working great. It lists each into a drop down, provides a proper link, and loads up only what the user has previous input. In the body it's suppose to load up specific information for each entry.
Whats not working: It's either not giving me a findOne() where the params._id equal the id in the route, or it's not loading anything. The location is correct. It's just not loading the info like it should.
UPDATE: I moved some things around and got the 'name' field to print out, still not able to get it to verify owner just yet. Console prints out: "Exception in template helper: ReferenceError: currentCharacter is not defined" Replacing with current code.
What am I doing wrong? Below is the code:
Route:
this.route('character/:_id', {
template: 'character',
subscriptions: function() {
return Meteor.subscribe("characterlist");
return Meteor.subscribe("characterlist", this.params._id);
},
data: function () {
var routeid = this.params._id;
return {
currentCharacter: CharList.findOne({_id: routeid}),
characterlist: CharList.find({'owner':Meteor.userId()})
};
}
});
Template Helper Class:
Template.character.helpers({
characterlist: function () {
return CharList.find({}, {sort: {createdAt: -1}});
},
currentCharacter: function () {
return CharList.findOne({'_id':Router.current().params._id});
},
isOwner: function () {
return currentCharacter.owner === Meteor.userId();
}
});
HTML:
<template name='character'>
<div class="container-body">
{{#if currentUser}}
<div class="well">
{{currentCharacter.name}}
{{#with currentCharacter}}
{{#if isOwner}}
<p>Character: {{name}}</p>
{{else}}
<p>You are not approved to make spends for this character.</p>
{{/if}}
{{/with}}
</div>
{{else}}
<h4>Please log in.</h4>
{{/if}}
</div>
</template>
Let me sigh metaphorically out loud online. Like usual when I ask for help I find what i did wrong.
It needs to get "this" object and pull the "owner" type out. Once it has "this.owner" it can verify the user is of course the correct owner. I was trying to be smart and didn't realize it was asking for the wrong information for four hours! Code below:
isOwner: function () {
return this.owner === Meteor.userId();
}

Ember Data Only Load Async attributes when rendered to Handlebars Template

I'd like to only have Ember load associated data when it's needed and rendered to the template. I thought setting async to true would do that, but it simply loads the data asynchronously as soon as the parent object is rendered. Alternatively, I'm trying to only load it when an action has occurred.
JS Bin of code is here (non-working, but shows general idea):
http://jsbin.com/tekaju/2/edit
For those not caring to check it out, gist is:
Models.Visit = DS.Model.extend({
visitors: DS.hasMany("user", { async: true })
});
VdAdmin.VisitController = Ember.ObjectController.extend({
showVisitors: false,
showVisitorsObserver: function(value){
showVisitors = value;
return value;
}.observes('showVisitors')
actions: {
loadVisitors: function(){
this.set('showVisitorsObserver', true);
}
}
});
<button ... {{action 'loadVisitors'}}>Click to Load</button>
{{#if showVisitorsObserver }}
I don't want to asynchronously grab related `visitors` data until `showVisitors` is true
{{visitors.length}}
{{/if}}
You shouldn't be watching an observer, it doesn't return values. It's used for reaction, not as a property. You should instead watch showVisitors.
App.ItemController = Em.ObjectController.extend({
showVisitors:false,
actions: {
loadVisitors: function(){
this.set('showVisitors', true);
}
}
});
{{#if showVisitors }}
I don't want to asynchronously grab related `visitors` data until `showVisitors` is true
{{visitors.length}}
{{/if}}
Example: http://emberjs.jsbin.com/qabaf/4/edit

Defined two meteor local collections and helpers exactly the same. One helper works. The other doesn't

I create these two local collections (the code is actually written one after the other exactly like below):
ShoppingCartCollection = new Meteor.Collection(null);
CurrentPricesCollection = new Meteor.Collection(null);
Inside Template.myTemplate.rendered I add some initial info into these collections (again, code is one after the other):
ShoppingCartCollection.insert({"sqft" : "not yet entered"});
CurrentPricesCollection.insert({"hdrPhotos" : 100});
I've got these two global helpers in helpers.js (defined one after the other)
Handlebars.registerHelper("shoppingCart", function() {
return ShoppingCartCollection.findOne();
});
Handlebars.registerHelper("currentPrice", function() {
return CurrentPricesCollection.findOne();
});
When I load the page I immediately run these commands in the console:
> ShoppingCartCollection.findOne();
Object {sqft: "not yet entered", _id: "xcNmqJvMqqD5j7wwn"}
> CurrentPricesCollection.findOne();
Object {hdrPhotos: 100, _id: "LP38E3MZgzuYjvSec"}
In my template I use these helpers, but...
{{currentPrice.hdrPhotos}} //displays nothing
{{shoppingCart.sqft}} //displays "not yet entered"
How... what... ? How can this be? Are there some kind of gotchas that I could be missing? Some kind of dependency or load order that I'm not aware of?
The code you posted is working fine here.
Suggest comparing this code to the exact details of what you are doing. Also, look
for other problems, typos, etc.
Below is the exact test procedure I used:
From nothing, at the linux console:
meteor create sodebug
Note that this will produce files for a "hello world" type program.
Check the version:
meteor --version
Release 0.8.1.1
Edit sodebug/sodebug.js:
if (Meteor.isClient) {
// code autogenerated by meteor create
Template.hello.greeting = function () {
return "Welcome to sodebug.";
};
Template.hello.events({
'click input': function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
// add your code here
ShoppingCartCollection = new Meteor.Collection(null);
CurrentPricesCollection = new Meteor.Collection(null);
ShoppingCartCollection.insert({"sqft" : "not yet entered"});
CurrentPricesCollection.insert({"hdrPhotos" : 100});
Handlebars.registerHelper("shoppingCart", function() {
return ShoppingCartCollection.findOne();
});
Handlebars.registerHelper("currentPrice", function() {
return CurrentPricesCollection.findOne();
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
Edit sodebug.html:
<head>
<title>sodebug</title>
</head>
<body>
{{> hello}}
{{> T1 }}
{{> T2 }}
</body>
<template name="T1">
<p>
{{shoppingCart.sqft}}
</p>
</template>
<template name="T2">
<p>
{{currentPrice.hdrPhotos}}
</p>
</template>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
Run: meteor run
Manual tests:
Fire up chromium browser at localhost:3000
Check web browser console for collections data. PASS
Check web browser screen for templates data. PASS
Reorder templates in sodebug.html file, check web browser screen. PASS

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