I am new to Meteor js and I am trying to create a form following the official guide http://guide.meteor.com/methods.html#method-form. It suggests to use mdg:validated-method package and aldeed:simple-schema for validation which are based on mdg:validation-error to return validation error messages to the client. The guide suggests this code then to handle validation
Invoices.methods.insert.call(data, (err, res) => {
if (err) {
if (err.error === 'validation-error') {
// Initialize error object
const errors = {
email: [],
description: [],
amount: []
};
// Go through validation errors returned from Method
err.details.forEach((fieldError) => {
// XXX i18n
errors[fieldError.name].push(fieldError.type);
});
// Update ReactiveDict, errors will show up in the UI
instance.errors.set(errors);
}
}
});
but the problem is that only fieldError.type, fieldError.name and first human readable message from simple-schema are available in err.error. I use translated messages and field labels in simple-schema to get nice understandable validation error messages. So getting just object property name with "required" is unacceptable, especially in the case when message includes min/max constraints for example. I could not find any way to get simple-schema's validation context to retrieve the full list of human readable errors.
So my question is can I get full error messages on the client and how?
Or maybe there are better ways to achieve what I am trying to do?
Thanks in advance
Hi! Recently I have encountered with the same problem.
So I just create pull request with some code enhancement to solve this issue. Now when you submit form and call validated-method from client side like following:
yourMethodName.call(data, (err) => {
if (err.error === 'validation-error') {
console.dir(err, 'error ')
}
});
You will see error object in the console:
{
"errorType": "ClientError",
"name": "ClientError",
"details": [
{
"name": "firstName",
"type": "required",
"message": "First name is required"
},
{
"name": "lastName",
"type": "required",
"message": "Last name is required"
},
{
"name": "phone",
"type": "required",
"message": "Phone is required"
},
{
"name": "email",
"type": "required",
"message": "Email is required"
}
],
"error": "validation-error"
}
So I just copy that from my console output.
In my case, the method is as follows:
export const yourMethodName = new ValidatedMethod({
name: 'my.awesome.method',
validate: new SimpleSchema({
firstName: { type: String },
lastName: { type: String },
phone: { type: Number },
email: { type: String, regEx: SimpleSchema.RegEx.Email }
}).validator({ clean: true }),
run(doc) {
YourCollection.insert(doc);
}
});
If my pull request will be accepted, you can easy use node-simple-schema (it is the next version of meteor-simple-schema).
If it won't you can use my fork.
Hope this helps!
EDIT: Now this feature available in official node-simple-schema package.
Related
In my my, I am attempting to serve dynamic form controls via JSON to a form builder similar to the dynamic forms tutorial at angular.io. I am having trouble operating on JSON data returned from an http.get() request. I can console.log the whole object, but if I try to log a sub-object, I get "undefined" in the console.
Here is my component file:
export class TestComponent implements OnInit {
dataSource: Observable<any>;
questions: QuestionBase<any>[] = [];
results: QuestionBase<any>[] = [];
constructor(private http: HttpClient) {
this.dataSource = this.http.get('/questions');
}
ngOnInit() {
this.dataSource.subscribe(
data => {
this.results = data['contactInfo'];
console.log(this.results[0].controlType);
this.results.forEach((item: QuestionBase<any>) => {
if(item.controlType == 'text') {
console.log(item.controlType);
this.questions.push(new TextQuestion(item));
}
else if(item.controlType == 'dropdown') {
console.log(item.controlType);
this.questions.push(new DropdownQuestion(item));
}
});
console.log(this.questions);
},
err => {
console.log("Cannot get JSON data. Error Code: %s, URL: %s",
err.status, err.url)
},
() => console.log('Done')
);
}
}
In the data => {} callback, if I console.log(request), the console displays the entire object just fine. I can even read sub-objects in the template. But, if I try to console.log or do any other thing with sub-objects as in the code above, e.g. the forEach() method, I get "undefined" on the console. What am I doing wrong?
Edit: Here is the JSON snippet:
{
"contactInfo": [
{
"key": "firstName",
"globalLabel": "First Name",
"controlType": "text",
"required": "true",
"order": "1"
},
{
"key": "lastName",
"globalLabel": "Last Name",
"controlType": "text",
"required": "true",
"order": "2"
},
{
"key": "streetAddress",
"globalLabel": "Street Address",
"controlType": "text",
"required": "true",
"order": "3"
}, ...}]
Edit #2 I had failed to restart my Node server after updating the JSON file to read "controlType" over an earlier version that used simply "type." I figured the server would automatically serve the newly edited and saved file, but it seems as though one must restart the server in order to do this. ISSUE CLOSED
I'm working on request error handling in a node.js server application. I have defined a callback function handling these errors:
app.use(function errorHandler(err, req, res, next) {
res.send(err, {status: err, message: 'error'});
}
);
which is fine for me as a developer, as it prints a stack trace like this:
{
"status": {
"stack": "Error\\\n at MongooseError.ValidationError (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/error/validation.js:22:16)\\\n at model.Document.invalidate (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/document.js:1216:32)\\\n at /home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/document.js:1090:18\\\n at validate (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/schematype.js:653:7)\\\n at /home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/schematype.js:681:9\\\n at Array.forEach (native)\\\n at SchemaString.SchemaType.doValidate (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/schematype.js:658:19)\\\n at /home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/document.js:1088:11\\\n at process._tickCallback (node.js:415:13)",
"message": "User validation failed",
"name": "ValidationError",
"errors": {
"email": {
"properties": {
"regexp": {},
"type": "regexp",
"message": "Path `{PATH}` is invalid ({VALUE}).",
"path": "email",
"value": "test#exa#mple.com"
},
"stack": "Error\\\n at MongooseError.ValidatorError (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/error/validator.js:25:16)\\\n at validate (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/schematype.js:652:13)\\\n at /home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/schematype.js:681:9\\\n at Array.forEach (native)\\\n at SchemaString.SchemaType.doValidate (/home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/schematype.js:658:19)\\\n at /home/osboxes/skipodium_rest_server/node_modules/mongoose/lib/document.js:1088:11\\\n at process._tickCallback (node.js:415:13)",
"message": "Path `email` is invalid (test#exa#mple.com).",
"name": "ValidatorError",
"kind": "regexp",
"path": "email",
"value": "test#exa#mple.com"
}
}
},
"message": "error"
}
However, I'd like to display it in a neat, user-friendly format for the production version, without leaking the entire stack trace. Now I could specify the error status and message in every request handling function, but there is still specific information, like above, that the entered email is invalid, and I don't feel like typing it by hand for every single field that is checked by validator. Is there any existing boilerplate that will do the job for me?
Mongoose validation errors are a pain to handle. My general approach is to only take the first error (let the user deal with one at a time) and send the path and message since the rest won't really contribute much additional meaning to a non-developer.
first = err.errors[Object.keys(err.errors)[0]]
res.send({
path: first.path,
message: first.message
});
I'd also recommend having a custom API-style error format that you stick with for all of your errors- it will make maintainability much easier.
I have a set of pre-defined error templates that I rely on- here's one.
// If the client missed a required parameter
exports.missingParam = function (res, domain, param) {
res.status(400).send({
status: "failed",
errors: [
{
status: "400",
domain: domain,
reason: "required",
message: "Required parameter: "+param,
locationType: "parameter",
location: param
}
]
});
}
I'm trying to run a simple query against my Google Cloud datastore using google-api-nodejs-client. I want to query for all entities matching a given kind. When I run this query using the "Try it now" tool it works fine:
Request
POST https://www.googleapis.com/datastore/v1beta2/datasets/healthier-staging/runQuery?key={YOUR_API_KEY}
{
"query": {
"kinds": [
{
"name": "Subscriber"
}
]
}
}
Response
200 OK
{
"batch": {
"entityResultType": "FULL",
"entityResults": [
{
"entity": {
"key": {
"partitionId": {
"datasetId": "s~healthier-staging"
},
"path": [
{
"kind": "Subscriber",
"name": "+1215XXXXXXX"
}
]
},
"properties": {
...
I'm able to authenticate using my credentials, create a transaction, etc. so I know it's not an authentication issue.
Here's the code I'm trying to run in Node:
this.datastore.runQuery({
datasetId: 'healthier-staging',
query: {
kinds: [{name: 'Subscriber'}]
},
}, (function(err, result) {
if (err) {
console.error(err);
return;
}
}).bind(this));
When I try to run the same query using the Node module, I get this error:
{ [Error: one of fields Query.query and Query.gql_query must be set]
code: 400,
errors:
[ { domain: 'global',
reason: 'INVALID_ARGUMENT',
message: 'one of fields Query.query and Query.gql_query must be set' } ] }
This doesn't make sense, since I've specified the query field. I've tried all sorts of things: removing datasetId (produces an error about needing datasetId), using gql_query instead (same error), encapsulating the datasetId inside a transaction and passing that along inside readOptions, etc.
Is this a bug or am I doing something silly?
Thanks!
I mentioned this on your other StackOverflow question, but your request should be included in the resource section:
this.datastore.runQuery({
datasetId: 'healthier-staging',
resource: {
query: {
kinds: [{name: 'Subscriber'}]
},
},
}, (function(err, result) {
if (err) {
console.error(err);
return;
}
}).bind(this));
HELP!
I'm not sure what's going on, but my login page isn't working. It simply reloads even though I'm entering valid user/password.
I think the problem is it's getting stuck on issues with my data-structure, security-rules, and app.js, but I'm at a loss.
I was provided a sinatra/ruby simple api to work with users & groups (just a small project).
here's the site:
https://starter-vicks9985.firebaseapp.com/index.html
here's the code:
$.post/("https://starter-vicks9985.firebaseapp.com/main.rb",
{
"name": "admin",
"email": "admin#example.com",
"password": "secret",
"admin": true,
"role-value": 99,
}
), console.log("success");
{
"rules": {
".read": true,
"users": {
"$user": {
//can add a message if authenticated
".write": "auth.uid === $user"
}
},
"rooms": {
"$room": {
"users": {
// can write to the users list only if ADMINISTRATOR
"$user": {
"write":"newData.parent().child(auth.uid).val() === 99"
}
}
}
},
"messages": {
"$room": {
"$message": {
//can add a message if they are a MEMBER (if there was message/chat capability)
".write": "(!data.exists() && newData.exists() && root.child('rooms/' + $room + '/users/' + auth.uid).val() >= 10)"
}
}
}
}
}
$(document).ready(function() {
/**
*Set initial firebase ref. Use set to write in first admin user.
*/
var ref = new Firebase("https://starter-vicks9985.firebaseio.com/");
ref.set({
"name": "Admin",
"email": "admin#example.com",
"password": "secret",
"admin": true
});
/** Get email address from loginform, format email, get password
* Firebase keys cannot have a period (.) in them, so this converts the emails to valid keys
*/
var emailAddress = function emailToKey(emailAddress){
return btoa(emailAddress);
};
var password = document.getElementById('password');
/**
* Authorize user with email and password, passing in values from form.
*/
ref.authWithPassword({
email : emailAddress,
password : password,
}, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
return authData;
}
});
/**
* If user is logged in (valid), redirect to user profile
*/
ref.onAuth(function(authData) {
window.open="https://starter-vicks9985.firebaseio.com/userprofile/userprofile.html";
})
});
Like #Kato said, this is a code dump so please consider creating an mcve. Although, first check out my comments below.
The Code You Posted
After glancing at your code, I see some errors that I will point out:
1. Your jQuery post syntax is incorrect, and wouldn't work even if it was correct.
Most importantly, you are making a post request to a Ruby file. Firebase Hosting is not a server, it is hosting for static files.
See this answer by Frank. He says:
Firebase hosting is a product to serve so-called static application, which consist only of files that the client interprets. Firebase's servers will not interpret any code that you upload. So Firebase hosting is not suited to host your Ruby-on-Rails application.
To quote Firebase hosting's documentation:
We deliver all your static content (html, js, images, etc)
That being said, take a look at the jQuery documentation for $.post().
See my comments on your code:
$.post/("https://starter-vicks9985.firebaseapp.com/main.rb",
// ^ What is this '/'?
{
"name": "admin",
"email": "admin#example.com",
"password": "secret",
"admin": true,
"role-value": 99,
}
), console.log("success");
// ^ You are closing the function call, 'console.log' falls outside of it.
What it should look like:
$.post("https://starter-vicks9985.firebaseapp.com/main.rb", {
"name": "admin",
"email": "admin#example.com",
"password": "secret",
"admin": true,
"role-value": 99,
}, function() {
console.log("success");
});
2. What's even going on with the login functions?
Assuming you fix Uncaught SyntaxError: Unexpected token { in data-structure.js:14...
If you take a look at the console, you will see Uncaught Error: Firebase.authWithPassword failed: First argument must contain the key "email" with type "string".
That is because you are passing a function, emailAddress to .authWithPassword().
You declare emailAddress() like so:
var emailAddress = function emailToKey(emailAddress){
return btoa(emailAddress);
};
So the email parameter in the following is being passed emailAddress(), not a string that is an email address.
ref.authWithPassword({
email : emailAddress,
password : password,
}, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
return authData;
}
});
Most importantly, all of these login functions are being called immediately after the page loads. Nothing in your code (app.js) waits for, and responds to, the submission of the form.
The Code on Your Website
I also went on your page, looked at your source code, and found some more issues.
1. Error in form HTML in index.html
<section class="loginform cf">
<form id= "login" form name="login" form type= "submit" accept-charset="utf-8">
<!-- extra^space ^duplicate "form"^ ^again, space -->
...
</form>
</section>
2. Syntax Errors in data-structures.js
Again, you have the same errors here as I described above ('/' and closing parentheses), but the object that you're passing the post is incorrectly formatted
$.post/("https://starter-vicks9985.firebaseapp.com/main.rb",
{
"users" //missing ':' after users
//the following objects do not have keys - should be {"users":{"someUserKey1":{...},"someUserKey2":{...}}} etc.
{
"name": "admin",
"email": "...",
"password": "...",
"admin": true,
"role-value": 99,
},
{
"name": "aaa",
...
},
{
"name": "bbb",
...
}
},
), console.log("success");
And the same things apply for the post call for "groups".
I hope that provides some clarity.
I would suggest reading over other answers here on StackOverflow, like:
jQuery AJAX submit form
jQuery form submit
And search for more answers like:
https://stackoverflow.com/search?q=jquery+form+submit
In conclusion, I'd suggest doing some more research and reading documentation :)
can you please tell me how to show different message on required and invalid ?In other word .make a form from json using plugin .In that there are some required parameter .and some I need to validate example "Email".When user press "submit" button if user did not fill the field it show "this field is required" .and if user fill the value but not same patten than it say "invalid value".can we show different message as different situation .
i used this plugin
https://github.com/Textalk/angular-schema-form/blob/master/docs/index.md#validation-messages
and I make this plunker
http://plnkr.co/edit/ZNJO3x3IqajjdMNStJMF?p=preview
angular.module('test',['schemaForm']).controller('FormController', function($scope,$http){
$scope.schema = {
type: "object",
properties: {
name: { type: "string", minLength: 2, title: "Name", description: "Name or alias" ,required:true,"default": "dddd"},
"student": { type: "string", title: "studentname", description: "Name or student" ,required:false},
"email": {
"title": "Email",
"type": "string",
"pattern":"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*",
"description": "Email will be used for evil.",
required:true
},
title: {
type: "string",
required:true,
enum: ['dr','jr','sir','mrs','mr','NaN','dj'],
}
}
};
$scope.form = [
"*",
{
type: "submit",
title: "Save"
}
];
As you may read from the official angular-schema-form documentation
Per default all error messages comes from the schema validator tv4, this might or might not work for you. If you supply a validationMessage property in the form definition, and if its value is a string that will be used instead on any validation error.
If you need more fine grained control you can supply an object instead with keys matching the error codes of tv4. See tv4.errorCodes
Ex.
{
key: "address.street",
validationMessage: {
tv4.errorCodes.STRING_LENGTH_SHORT: "Address is too short, man.",
"default": "Just write a proper address, will you?" //Special catch all error message
}
}
You can also set a global validationMessage in formDefaults see Global Options.
So for more error messages, visit this link
Here is a great blog article that should answer your question: http://www.thebhwgroup.com/blog/2014/08/designing-html-forms-angularjs-part-1/
In particular, check out part 3 "Validation Messages and Styling".
From the article,
<span class="validation-message" ng-show="contactForm.firstName.$error.maxlength">Max length 20</span>
Where the ng-form is named contactForm and the element in question is named firstName. This puts the form on the scope and you can then traverse that to get the errors, and show an error message specifically for each error.