Kendo MVVM create new record with remote datasource - mvvm

I think I'm missing something simple, but I can't find any examples showing how to do this... also, please forgive me if some of the terminology I'm using is wrong.
I simply want to use an HTML form that is bound to a Kendo Observable object to create a new record in my remote datasource.
All the examples I've seen show how to edit existing records, but that's not what I'm looking for (at least not at the moment).
I've created a Fiddle http://jsfiddle.net/matbeard/fYfYz/2/ with a simple cut-down version of what I've got so far. Obviously it won't actually save the record as the create URL doesn't point anywhere, but I'm confident I can handle that.
Can somebody please point me in the right direction? Thanks.
And because I can't post a question without it, here's some code copied from the Fiddle:
var model = kendo.data.Model.define({
id: "id",
fields: {
id: { type: 'number' },
field1: { type: 'string' },
field2: { type: 'string' },
field3: { type: 'string' }
}
});
var viewModel = kendo.observable({
dataSource: new kendo.data.DataSource({
type: 'json',
transport: {
create: {
url: '/myurl/create',
dataType: 'json',
type: 'post'
}
},
schema: {
data: 'data',
model: model
}
});
});
kendo.bind($("#my-form"), viewModel);

Lets do it slightly different...
Your form does not need to (should) be bound to the and object containing the DataSource since you are actually not saving the dataSource but one record.
So, you should define the model as:
var Model = kendo.data.Model.define({
id: "id",
fields: {
id: { type: 'number' },
field1: { type: 'string' },
field2: { type: 'string' },
field3: { type: 'string' }
}
});
The DataSource now becomes an object per-se:
var dataSource = new kendo.data.DataSource({
type: 'json',
transport: {
create: "/myurl"
},
schema: {
model: Model
}
});
And your observable object has data element that is an instance of the Model defined (new Model()).
var viewModel = kendo.observable({
data: new Model(),
mySave: function(e){
console.log("this", this.data);
dataSource.add(this.data);
e.preventDefault();
}
});
So, your form should be now something like:
<form id="my-form">
<input name="field1" data-bind="value:data.field1" type="text" />
<input name="field2" data-bind="value:data.field2" type="text" />
<input name="field3" data-bind="value:data.field3" type="text" />
<button data-bind="click: mySave">Save</button>
</form>
Your JSFiddle modiefied http://jsfiddle.net/6LHx3/4/

Related

Mongoose Schema planning: using ObjectID reference or using array of type: [otherSchema]?

I'm currently planning out the database structure for an app I'm building, and something in this linked answer raised some questions for me.
In the structure that Shivam proposed, he sometimes references another collection directly, in other words within one Schema he defines the type of a field to be an array of another schema type. Example:
import { Schema } from "mongoose";
import { QuestionSchema } from "./question-schema";
const mongoose = require('mongoose');
export const QuestionSetSchema: Schema = new Schema({
questionSet: {
type: [QuestionSchema],
validate: {
validator: function(value: any) {
return value.length === 12;
},
message: 'Question set must be 12.'
}
},
}, {
timestamps: true
});
In other cases, he only uses an ObjectID reference to another schema/collection:
export const CandidateSchema: Schema = new Schema({
name: String,
email: String, // you can store other candidate related information here.
totalAttempt: {
type: Number,
default: 0,
validate: {
validator: function(value: number) {
return value === 3;
},
message: 'You have already done three attempts.'
}
},
candidateQuestionAnswers: {
type: [Schema.Types.ObjectId],
ref: 'CandidateQuesAnswer'
}
}, {
timestamps: true
});
What are the use cases for each of the above? Does the "type:[otherSchema]" method actually embed instances of that collection or does it only provide their properties to the Schema they are called from?

Validating Mongo Cursor in Meteor Template

The code below shows how to validate a Mongo.Cursor. My Question is, why would someone want to validate a Mongo.cursor. It's a bit abstract for me. I mean if I manipulate data and click on a button, I understand the reason for validation, but on a Mongo.Cursor?
Template.Lists_show.onCreated(function listShowOnCreated() {
this.autorun(() => {
new SimpleSchema({
list: { type: Function },
todosReady: { type: Boolean },
todos: { type: Mongo.Cursor },
}).validate(Template.currentData());
});

Using objects as options in Autoform

In my Stacks schema i have a dimensions property defined as such:
dimensions: {
type: [String],
autoform: {
options: function() {
return Dimensions.find().map(function(d) {
return { label: d.name, value: d._id };
});
}
}
}
This works really well, and using Mongol I'm able to see that an attempt to insert data through the form worked well (in this case I chose two dimensions to insert)
However what I really what is data that stores the actual dimension object rather than it's key. Something like this:
[
To try to achieve this I changed type:[String] to type:[DimensionSchema] and value: d._id to value: d. The thinking here that I'm telling the form that I am expecting an object and am now returning the object itself.
However when I run this I get the following error in my console.
Meteor does not currently support objects other than ObjectID as ids
Poking around a little bit and changing type:[DimensionSchema] to type: DimensionSchema I see some new errors in the console (presumably they get buried when the type is an array
So it appears that autoform is trying to take the value I want stored in the database and trying to use that as an id. Any thoughts on the best way to do this?.
For reference here is my DimensionSchema
export const DimensionSchema = new SimpleSchema({
name: {
type: String,
label: "Name"
},
value: {
type: Number,
decimal: true,
label: "Value",
min: 0
},
tol: {
type: Number,
decimal: true,
label: "Tolerance"
},
author: {
type: String,
label: "Author",
autoValue: function() {
return this.userId
},
autoform: {
type: "hidden"
}
},
createdAt: {
type: Date,
label: "Created At",
autoValue: function() {
return new Date()
},
autoform: {
type: "hidden"
}
}
})
According to my experience and aldeed himself in this issue, autoform is not very friendly to fields that are arrays of objects.
I would generally advise against embedding this data in such a way. It makes the data more difficult to maintain in case a dimension document is modified in the future.
alternatives
You can use a package like publish-composite to create a reactive-join in a publication, while only embedding the _ids in the stack documents.
You can use something like the PeerDB package to do the de-normalization for you, which will also update nested documents for you. Take into account that it comes with a learning curve.
Manually code the specific forms that cannot be easily created with AutoForm. This gives you maximum control and sometimes it is easier than all of the tinkering.
if you insist on using AutoForm
While it may be possible to create a custom input type (via AutoForm.addInputType()), I would not recommend it. It would require you to create a template and modify the data in its valueOut method and it would not be very easy to generate edit forms.
Since this is a specific use case, I believe that the best approach is to use a slightly modified schema and handle the data in a Meteor method.
Define a schema with an array of strings:
export const StacksSchemaSubset = new SimpleSchema({
desc: {
type: String
},
...
dimensions: {
type: [String],
autoform: {
options: function() {
return Dimensions.find().map(function(d) {
return { label: d.name, value: d._id };
});
}
}
}
});
Then, render a quickForm, specifying a schema and a method:
<template name="StacksForm">
{{> quickForm
schema=reducedSchema
id="createStack"
type="method"
meteormethod="createStack"
omitFields="createdAt"
}}
</template>
And define the appropriate helper to deliver the schema:
Template.StacksForm.helpers({
reducedSchema() {
return StacksSchemaSubset;
}
});
And on the server, define the method and mutate the data before inserting.
Meteor.methods({
createStack(data) {
// validate data
const dims = Dimensions.find({_id: {$in: data.dimensions}}).fetch(); // specify fields if needed
data.dimensions = dims;
Stacks.insert(data);
}
});
The only thing i can advise at this moment (if the values doesnt support object type), is to convert object into string(i.e. serialized string) and set that as the value for "dimensions" key (instead of object) and save that into DB.
And while getting back from db, just unserialize that value (string) into object again.

Error trying to update document using autoForm with Schema in Meteor

Error message:
"Uncaught Error: After filtering out keys not in the schema, your modifier is now empty"
Using autoform with collection2 and simple schema in Meteor.
The schema:
Injuries = new Mongo.Collection('injuries');
Rehab = new SimpleSchema({
exercise: {
type: String,
label: "Rehab Exercise"
},
sets: {
type: Number,
label: "Sets"
},
duration: {
type: Number,
label: "Set Duration (in Minutes)"
},
date: {
type: String,
label: "Date of Rehab Exercise"
},
rehabnotes: {
type: String,
label: "Notes: i.e. 70% Intensity During Sprints",
max: 200
},
injuryid:{
type: String,
}
});
Injuries.attachSchema(new SimpleSchema({
player: {
type: String,
label: "Player",
max: 50
},
injury: {
type: String,
label: "Injury"
},
notes: {
type: String,
label: "Notes",
max: 200
},
injurydate: {
type: Date,
label: "Date of Injury",
},
rehab: {
type: [Rehab],
optional: true
}
}));
And the Form Code in the Template:
{{#autoForm collection="Injuries" schema="Rehab" id="insertRehabForm" type="update"}}
<fieldset>
{{> afQuickField name='exercise' options=options}}
{{> afQuickField name='sets'}}
{{> afQuickField name='duration'}}
{{> afQuickField name='date'}}
{{> afQuickField name='rehabnotes' rows=6}}
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
I can insert documents just fine with the autoform on the home page, using this custom form on the individual document page I receive the error on submission.
I have one collection hook setup up for before submissions, but this looks like it is just a schema error, perhaps the Rehab array that I have set up on the original Injuries schema is messing this up? The searches I've done for this have all been about the "Type" parameter in the schema not matching what is expected, but I've checked those here and they look good. Suggestions?
Based on the AutoForm's docs: the schema attribute is required if collection attribute is not set, however, even if collection is set AutoForm will still use the provided schema attribute to generate (only applicable to QuickForm) and validate the form (applicable to both AutoForm and QuickForm).
What happened in your case is that since both attributes (schema and collection) are provided, AutoForm first validates the form fields against the Rehab schema and when it succeed, it tries to insert the values of those fields (exercise, sets, duration, date, rehabnotes) to your Injuries collection, which does not have those keys in its own schema (it only has player, injury, notes, injurydate and rehab).
From your requirements, it seems like setting the AutoForm type to update-pushArray is the best possible solution. Check the docs and the example for usage.

Remove undesirable fields from being returned in sails

I am new to SailsJS and stuck in Data Model as follows:
I have a User model as follows:
module.exports = {
attributes: {
firstName: {
type: 'string'
},
email: {
type: 'email',
required: true
},
password: {
type: 'String'
},
passwordSalt: {
type: 'String'
},
projects:{
collection: 'ProjectMember',
via: 'userId'
}
}
};
Task Model :
module.exports = {
taskName: {
type: 'String'
},
userId: {
model: 'User'
}
};
In Task model, it is getting all fields from User table which is not required while task data is rendered. I was planning to create one more model called TinyUser which stores only required fields to be shown when task data is rendered.
But TinyUser should just refer User table and get required data from it rather than we creating all data for TinyUser manually when user data is created.
Is there any way this can be achieved in Sails?
Thanks in Advance..
I'm not sure about your question, but this will return a list of required attributes for any model
_.find(sails.models.<YOUR MODEL>._attributes, function(attr){return attr.required})
If your intent it to simply remove undesirable fields you can override the toJSON / toObject methods
see
https://github.com/balderdashy/waterline-docs/blob/master/models.md#toobjecttojson-instance-methods
User.find({select:['firstName', 'email']}).exec()