I would like to be able to validate single fields at a time in my forms using backbone forms and backbone validation, however I am having problems getting this to work if I put my requirements in the model validation, rather than the schema.
My model is:
class Event extends Backbone.Model
url: ->
'/events' + (if #isNew() then '' else '/' + #id)
validation:
title:
required: true
start_date:
required: true
end_date:
required: true
schema: ->
title:
type: "Text"
start_date:
type: "DateTime"
title: "Start Date"
DateEditor: "DatePicker"
end_date:
type: "DateTime"
title: "End Date"
DateEditor: "DatePicker"
The code in my View that uses these is
class Events extends Backbone.View
...
initialize: ->
#form = new Backbone.Form(model: #model).render()
Backbone.Validation.bind #form;
validateField: (field) ->
#form.on "#{field}:change", (form, fieldEditor) =>
#form.fields[field].validate()
render: ->
...
#validateField for field of #form.fields
...
However, my problem is that it only seems to validate if I move the required: true into the schema like so:
schema: ->
title:
type: "Text"
validators: ["required"]
However I'd rather not do this since, backbone.validation has a broader range of built-in validators which I would like to make use of outside this example.
I've noticed that backbone-forms states
There are 2 levels of validation: schema validators and the regular built-in Backbone model validation. Backbone Forms will run both when either form.commit() or form.validate() are called.
However I'm not sure it's running the regular validation when I validate the individual fields? The reasoning behind wanting to do this is that when a user starts creating an event I do not want to validate fields that they are yet to fill in.
Is there any way I can still validate individual fields without having to move the validation into the schema?
Thanks in advance.
======= UPDATE =======
On looking at the form editors source code for individual fields, the validate function is as so:
validate: function() {
var $el = this.$el,
error = null,
value = this.getValue(),
formValues = this.form ? this.form.getValue() : {},
validators = this.validators,
getValidator = Form.helpers.getValidator;
if (validators) {
//Run through validators until an error is found
_.every(validators, function(validator) {
error = getValidator(validator)(value, formValues);
return error ? false : true;
});
}
return error;
},
It does not appear to use the model validation, so I wonder how I can incorporate this?
Related
I have a form that is populated by data received from an API request to my backend.
I am using v-model to bind the data to the fields (for example):
<input type="text" v-model="fields.name">
Everything works just fine. But when it comes to Buefy datepicker I get the following warning:
Invalid prop: type check failed for prop "value". Expected Date, got String.
This is correct since this is the value I get back from Laravel is "2019-02-01 00:00:00". I am trying to parse this String to a Date using the Buefy property date-parser but with no luck:
<b-datepicker
:date-parser="(date) => new Date(Date.parse(date))"
v-model="fields.budget_date"
:first-day-of-week="1"
placeholder="DD/MM/YYYY"
name="order_date"
editable>
</b-datepicker>
Update:
This is the data object:
data() {
return {
csrf: document.querySelector('meta[name="csrf-token"]').content,
fields: {},
errors: {},
success: false,
loaded: true,
loading: false,
}
Then I use Axios.get to fetch the data from the server and assign them to the fields object like so:
this.fields = response.data;
This is how is see the fields.budget_date in Vue DevTools:
Any idea how to overcome this? Thank you in advance.
I just had this problem in a wrapper component around Buefy's b-datepicker component.
The solution can be derived from christostsang's answer which is to pass in the initial value wrapped in new Date().
My wrapper component prop type looks like this:
initialValue: {
type: Date,
required: false,
default: () => undefined,
},
and it is used like this:
<my-datepicker
:initial-value="new Date(something.renews_on)"
></my-datepicker>
I'm using mostly the default props from b-datepicker, but my wrapper component is using this:
<b-datepicker
v-model="value"
:initial-value="initialValue"
:placeholder="placeholder"
:name="name"
:date-formatter="dateFormatter"
:date-parser="dateParser"
:date-creator="dateCreator"
... etc
...
dateFormatter: {
type: Function,
required: false,
default: date => date.toLocaleDateString(),
},
dateParser: {
type: Function,
required: false,
default: date => new Date(Date.parse(date)),
},
dateCreator: {
type: Function,
required: false,
default: () => new Date(),
},
You can investigate the default props here: https://buefy.org/documentation/datepicker
Finally figured it out.
The warning is pretty clear: Don't use a string here, use a Date object.
So after getting the response from the server, I parsed the string value into Date object and then bind it to v-model:
this.fields.budget_date = new Date(this.fields.budget_date)
So now I get this into Vue DevTools:
As you can see the budget_date is of the correct Date format, unlike created_at which is a string.
Parser function (:date-parser) gives you the correct Date object during user date selection.
What I wanted was to set the v-model value based on the data stored in my database. And for the b-datepicker to work that needs to be Date object, not String.
Filter parameter value i.e name value is not included when i was performing filter operation in my API call as written below
_dc:1427270031651
counrtyId:2
custId:1
id:
name:
page:1
start:0
limit:10
sort:[{"property" : "id", "direction" : "desc"}]
This is my store
Ext.define('MyDesktop.store.DirectoriesStore', {
extend: 'Ext.data.Store',
requires:'MyDesktop.model.DirectoriesNumberModel',
model: 'MyDesktop.model.DirectoriesModel',
storeId:'DirectoriesStore',
autoLoad : {
params : {
start : 0,
limit : '10'
}
},
pageSize : 10,
remoteSort : true,
sorters : [{
property : 'id',
direction : 'desc'
}],
remoteFilter : true,
proxy:{
type:'ajax',
url:'./configuration/directory/get',
reader:{
type: 'json',
root: 'data',
totalProperty: 'total'
},
extraParams: {
'countryId': '',
'custId': ''
}
},
autoLoad:false
});
and this is my view
Ext.require([
'Ext.ux.grid.FiltersFeature'
]);
var filters = {
ftype: 'filters',
local: true,
features: [{type: 'integer',dataIndex: 'name'},
{type: 'string',dataIndex: 'description'},
{type: 'string',dataIndex: 'fileName'}]
};
and I added it grid as follows:
features: [filters],
Filters are working but In API call filters are not calling
Please anyone help me.
You have remote filtering disabled in your store.
Change remoteFilter: false to remoteFilter: true and try again.
From the docs:
remoteFilter : Boolean
true to defer any filtering operation to the server. If false,
filtering is done locally on the client.
It also appears that your specifically telling your application to apply the filters locally with the local:true config.
The problem is, the params are not being sent to the ajax call. But hey, try changing the page after you loaded your store. Then try sorting again. You will see that the params are now being sent along in the Ajax request.
My current workaround is to imitate a changepage via gridStore.loadPage(page, [options]). You should also change it back to first page again after that.
After you change the page, sort will be working for all following requests.
This of course is just a workaround and we may have different requirements. My grid is calling store.load() after render so I attached the change page there.
Hope you have some hints.
NOTE: THIS IS ONLY A WORKAROUND!
I'm getting this error:
[E] Ext.data.Session.checkModelType(): Unable to use anonymous models
in a Session
when trying to use a Session when binding a Grid with a Store via ViewModel:
ViewModel:
Ext.define('App.view.gridViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.gridview',
stores:{
gridstore: {
model: 'gridView',
autoLoad: true,
//This triggers the Exception:
session: true,
listeners: {
beforeload: function(store, operation, eOpts) {
var oProxy = this.getProxy();
oProxy.setExtraParams({
tableName: 'dbo.SomeTable'
, identityKey: "id"
, primaryKey: ["id"]
, mode: "readonly"
, lang: "es"
, output: 'json'
});
}
}
}
}
});
View:
Ext.define('App.view.gridView', {
extend: 'Ext.form.Panel',
//...
viewModel: {
type: 'gridview'
},
controller: 'gridview',
// Create a session for this view
session:true,
items: [{
xtype: 'grid',
reference: 'myGrid',
bind: '{gridstore}',
columns: [
//...
]
}]
//...
});
Model's data is fetch through a Proxy:
Model:
Ext.define("App.model.gridView", {
extend: 'Ext.data.Model',
schema: {
namespace: 'App.model'
},
proxy: {
//proxy remote api stuff......
}.
idProperty: 'id'.
primaryKeys: 'id'.
fields: [
//fields
]
});
I have no idea what an anonymous model is and I haven't found anything related in the web, any ideas?
Thanks in advance!
The reason seems to be that in my Server's response I have a JSON Object called "metaData", which collides with the one available in JSON Reader:
Response MetaData
The server can return metadata in its response, in addition to the
record data, that describe attributes of the data set itself or are
used to reconfigure the Reader. To pass metadata in the response you
simply add a metaData attribute to the root of the response data. The
metaData attribute can contain anything, but supports a specific set
of properties that are handled by the Reader if they are present:
http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Ext.data.reader.Json
The curious thing is that I don't use any of the available metaData options for JSON Reader, nothing related to any anonymous model, therefore this might be considered a bug
have the next params of object:
var mongoose = require (PATH);
var Schema = mongoose.Schema;
var schema - new Schema ({
barcode:{
type:number,
required:true,
unique:true
},...});
i want to validate 'barcode', which will be no less and no more than 14 characters;
for this i write the code below:
schema.path('barcode').validate(function(barcode){
return barcode.length == 13;
}, 'sorry, the error occurred, be careful while typing, 14 characters only!");
exports.Item = mongoose.model('Item', schema);
But when i implement this schema to the concrete object, this validation does not play any role. I mean, that i can type any length of number and no any error occur!
Checkout mongoose-validator. It integrates with mongoose to support custom validation. You can use it something like this.
var validate = require('mongoose-validator').validate;
var BarcodeSchema = new Schema({
code: {
type: String,
required: true,
unique: true,
validate: validate('len', 13)
}
});
I am attempting to develop a Mobile app using Kendo Mobile's MVVM and JayData Data Access Library. I have run into an issue that I have worked on for about a week now with no luck. I am looking for a simple example consisting of Kendo two way binding using a model that is created by JayData's (asKendoDataSource) having an inverseProperty navigation property. I am not sure that JayData kendo.js Module supports models containing inverseProperty and in the process of testing, even after getting the data to save with the relationship, retrieval of the same record does not pull the relational data back into the viewmodel.
Can anyone provided a simple example of Saving and Retrieving such a model using the webSql provider?
Any help is greatly appreciated.
JayData Models (simplified):
//Expense Category Model
$data.Entity.extend('data.types.ExpenseCategory', {
Id: { type: 'Edm.Guid', key: true },
CategoryName: { type: 'string', required: true, minLength: 3, maxLength: 26 },
Expenses: { type: Array, elementType: "data.types.Expense", inverseProperty: "Category" }
});
//Expense Model
$data.Entity.extend('data.types.Expense', {
Id: { type: 'Edm.Guid', key: true },
ExpenseDescription: { type: 'string', required: true },
Category: { type: "data.types.ExpenseCategory", inverseProperty: "Expenses" }
});
// Entity Context
$data.EntityContext.extend('data.types.DbContext',
{
ExpenseCategories: { type: $data.EntitySet, elementType: data.types.ExpenseCategory },
Expenses: { type: $data.EntitySet, elementType: data.types.Expense },
});
// Database Context
data.context = new data.types.DbContext({ name: 'sqLite', databaseName: 'cDb' });
Kendo Viewmodel (simplified):
views.expenseCategoryPicker = kendo.observable({
app: null,
categories: db.context.ExpenseCategories.asKendoDataSource(),
expense: null,
itemClick: function(sender) {
var expense = views.expenseCategoryPicker.expense;
expense.set('Category', sender.data);
...add/update logic
expense.sync();
},
loadExpense: function(dataItem) {
views.expenseCategoryPicker.set('expense', undefined);
views.expenseCategoryPicker.set('expense', dataItem);
},
});
EDIT
I figured out why the data won't save and a work around. There is a bug in JayData's kendo.js module when using the Kendo MMVM binding and inverseProperty relationships. They(JayData) simply don't support their own Attach method when an object relationship is being set through their Kendo module. Therefor when you call Kendo's SET on the model passing in the related Object the Entity state on the Object being passed in is set to 20 (New) and JayData tries to create a new record and in my case failing due to primary key conflict. The Attach method sets the Entity state to unmodified.
I know there is probably a more elegant way to fix this in JayData's code, but for me simply adding the following line right before I use the Kendo set method to set the object relationship allows the record to be saved without error.
itemClick: function(sender) {
var expense = views.expenseCategoryPicker.expense;
//manually set the state so SQL won't try to create a new record
sender.data.innerInstance()._entityState = 10;
expense.set('Category', sender.data);
...
Subsequent reads require the Include('model') method to load the relational data as mentioned by Robesz (THANK YOU)
It would be nice to see JayData fix the data save issue in their Kendo module.
JayData doesn't load the related entities by default, you have to use the include() operator:
data.context.Expenses.include('Category').toArray(...)
The parameter of the include should be the name of the navigation property.