I have a model like this
Ext.define('app.model.TeamEmpAssignment', {
extend : 'Ext.data.Model',
config : {
idProperty : 'teamEmpId',
fields : [{
name : 'teamEmpId',
type : 'int'
}, {
name : 'actName'
}, {
name : 'beginDateTime'
}, {
name : 'endDateTime'
},
],
validations : [{
field : 'beginDateTime',
type : 'presence',
message : 'Effective Begin Date Time required'
}, {
field : 'endDateTime',
type : 'presence',
message : 'Effective End Date Time required'
},
],
}
});
I have to write a validation to compare that endDateTime > startDateTime
I am trying sencha touch 2.3.1
I know this is an old question, but I ran into it while dealing with the same problem and wanted to share my approach.
I created a method in the model, which does the custom validation, something like:
Ext.define('app.model.TeamEmpAssignment', {
extend : 'Ext.data.Model',
config : {
// config here
},
checkDates: function (errors) {
// assuming the dates are timestamps
if (this.get('beginDateTime') > this.get('endDateTime')) {
errors.add(Ext.create('Ext.data.Error', {
field : 'beginDateTime',
message: 'Begin date can\'t be after end date'
}));
}
}
});
You will notice that checkDates has one argument - errors. This argument is actually the Ext.data.Errors object return by the validate method of the model. So we can do something like:
var record = Ext.create('Ext.data.Errors');
// set some values here
var errors = record.validate();
record.checkDates(errors);
console.log(errors.isValid());
We can use isValid, because all it does is check whether the Ext.data.Errors collection has items.
I am currently using it like this, however it will be quite easy to override the validate method, so you don't have to call the custom validation externaly.
Ext.define('app.model.TeamEmpAssignment', {
extend : 'Ext.data.Model',
config : {
// config here
},
checkDates: function (errors) {
// assuming the dates are timestamps
if (this.get('beginDateTime') > this.get('endDateTime')) {
errors.add(Ext.create('Ext.data.Error', {
field : 'beginDateTime',
message: 'Begin date can\'t be after end date'
}));
}
},
validate: function () {
var errors = this.callParent(arguments);
this.checkDates(errors);
return errors;
}
});
The validation types come from the Ext.data.validations singleton. You can add your own validators here, however the method does not supply the all the fields in the model (which it sounds like you will need). We would advise moving your validators to a different spot (outside the model), maybe on the related form component (after a load or before a save).
Related
I've got an input field that looks like the following :
<tr v-for="(item, index) in collection">
...
<input
type="checkbox"
v-model="item.activated"
#change="toggleSwitch(item.resource_url, 'activated', item)">
>
...
</tr>
The collection is an array containing several keys, activated is one of them. activated is equal to 1 or 0 as the data is coming from a mysql database. The problem is that the input field is always set to true in this case, even if the activated is equal to 1 or 0.
Now, I tried writing the v-model like so to fix the issue :
v-model="!!+item.activated"
as by adding !!+ I'd convert the integer value to a boolean and use that. That fixes the issue, but creates another. The other issue I get by doing so is when I try to change my checked input I get an error :
[Vue warn]: Cannot set reactive property on undefined, null, or primitive value: false
admin.js:120238 TypeError: Cannot use 'in' operator to search for 'activated' in false
The toggleSwitch method looks like this :
toggleSwitch: function toggleSwitch(url, col, row) {
var _this8 = this;
axios.post(url, row).then(function (response) {
_this8.$notify({ type: 'success' });
}, function (error) {
row[col] = !row[col];
_this8.$notify({ type: 'error' });
});
},
I'm new to Vue.js, any idea how to debug this and where could my problem be coming from? I'll gladly give any additional info.
Edit :
Here's my component
Vue.component('profile-edit-profile-form', {
mixins: [AppForm],
data: function() {
return {
form: {
...
activated: false ,
...
}
}
}
});
If you use AJAX to populate your collection, then you should convert your 0 and 1 strings to booleans in your AJAX callback before injecting them into your component. Or even better you could convert them directly from your controller, by the way you directly get true|false
data.forEach(function(entry) {
if(entry.hasOwnProperty("activated"))
entry.activated = !!+entry.activated
});
my recommendation is:
Database column "activated" tinyint(1)
in laravel model use $cast array to cast "activated" to "boolean"
in vue use native type boolean for form.activated with true and false
Laravel Model:
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'minimum' => 'float',
'maximum' => 'float',
'step' => 'float',
'minItems' => 'integer',
'maxItems' => 'integer',
'uniqueItems' => 'boolean',
];
Vue:
<b-form-radio-group id="uniqueItems" v-model="formData.uniqueItems" :options="optionsBoolean" name="uniqueItems" :/>
optionsBoolean (){
return [
{ text: 'Yes'), value: true },
{ text: 'No'), value: false }
]
}
I attempted to create a model in sequelize (say has 3 attributes, attrA, B, and C) with some custom validation logic. This tutorial helped me get most of it set up:
const Model = Sequelize.define('model', {
attrA: { type: Sequelize.STRING },
attrB: { type: Sequelize.STRING },
attrC: { type: Sequelize.STRING },
}, {
validate: {
someValidationLogic() {
// Do something with attrA,B,C
// if (this.attrA ... this.attrB ... this.attrC) throw new Error..
}
}
})
In the application logic however, only say, 2 out of the 3 attributes (A and B) need to be updated:
Model.update(
{
attrA: 'foo',
attrB: 'bar'
}, {
where: {
id: 1,
},
returning: true,
})
This results in that when the custom validation logic being called, in the this object accessed in the function, only attrA and attrB are defined in this, and attrC remained undefined. This causes the validation logic to fail because attrC cannot be read. Is there any way I can get the object visible from someValidationLogic() to have all attributes populated? Or should this "validation" shouldn't have been validation logic at all and should've been done on the application level?
Your validation logic could take in account the possibility of attrC not being defined :
validate: {
someValidationLogic() {
if (this.attrA !== 'undefined' && this.attrA === 'forbidden value' ) {
// throw new Error
}
}
}
But if your validation includes checking the provided values against current database values, then you would better handle this in the application layer : first recover the current database record, manipulate it as needed, then save it to database.
I'm trying to resolve this issue but no matter what I try (based on several suggestions solutions found here as well), I can never make it work.
I would like the Jquery validation plugin to validate automatically all the generated fields from a form. My problem is that it will only work on the first generated field; the validation of the subsequent ones will just be a duplicate of the first.
Here's the pertinent html code:
<form class="someFormClass" method="post">
<span>
<input class="calendarName" name="description" value="<?= value_from_php ?>">
<input class="calendarName" name="description" value="<?= value_from_php ?>">
</span>
</form>
And here's the jQuery validation code:
$(function () {
$('form').each(function () {
$(this).validate({
errorElement: "div",
rules: {
description: {
required: true,
remote: {
url: "calendar/calendar_available/",
type: "post",
data: {
name: function () {
return $(".calendarName").val();
}
}
}
}
},
messages: {
description: {
required: "Description field can't be blank !",
remote: "This calendar already exists."
}
}
});
});
So, as stated, the plug-in behaves properly for the first field. But if I check the values posted in Chrome's Network, the "name" key created in the jQuery validation will always send the value of the first input.
I tried many things (trying to implement on more level of ".each" method in the validation, trying to generate dynamically a specific id for each field to point on (instead of a class), trying to modify the plugin code as suggested here (How to validate array of inputs using validate plugin jquery), but it didn't work.
I think there's something I don't grasp about the logic here.
UPDATE :
So, one of the reasons of my problem is that jQuery validation absolutely requires input with different names. See : Jquery Validation with multiple textboxes with same name and corresponding checkboxes with same name
So, I made a script to generate a different name for each input with the intention to dynamically create validation rules based on those names, following this suggestion : https://stackoverflow.com/a/2700420/3504492
My validation script now look like this :
$(function() {
var rules = new Object();
var messages = new Object();
$('input[name*=description_]:text').each(function() {
var currentName = $("input[name="+this.name+"]").val();
rules[this.name] = {
description: {
required: true,
remote: {
url: "calendar/calendar_available/",
type: "post",
data: currentName
}
}
},
color: {required: true}
};
messages[this.name] = {
description: {
required: "Description field can't be blank !",
remote: "This calendar already exists."
},
color: {required: "Color field can't be blank !"}
};
});
$('form').each(function () {
$(this).validate({
errorElement: "div",
rules: rules,
messages: messages
});
}) });
This almost works. Almost because if I limit the rules et messages to the required keys, it will display the validation each field (if I add the specific name to the message string, it will display on the proper field). But with a most complex rule like mine (with a remote key containing various keys for instance), I get a " Cannot read property 'call' of undefined. Exception occurred when checking element , check the 'description' method." error in the Console.
My guess is that the "description" declaration in the "rules" definition should be dynamic too (the current "name" field being visited).
Any suggestion?
While trying to execute the following code on show() we get an exception that the links attribute cannot find the model either if it is specified by class or if it is specified by entityName.
Ext.define('myapp.view.film.FilmsViewController', {
//extend: 'myapp.view.base.ViewController',
extend: 'Ext.app.ViewController',
alias: 'controller.films',
onAdd: function(button, event, options) {
this.createDialog(null)
},
createDialog: function(record) {
var me = this;
var view = me.getView(); //here is film panel
me.isEdit = !!record; //convert record to boolean
me.dialog = view.add({ //#3
xtype: 'filmwindow',
viewModel: { //#4
data: { //#5
title: record ? 'Edit: ' + record.get('title') : 'Add New Film',
},
links: { //#6
currentFilm: record || { //#7
//type: 'Film',
type: 'myapp.model.film.Film',
create: true
}
}
},
//session: true
});
me.dialog.show();
},
If we comment the links part of the code the rest is working ok.
Here is the interesting part of the exception:
[E] Ext.app.ViewModel.getRecord(): Invalid model name: myapp.model.film.Film
log # ext-all-rtl-debug.js?_dc=1446847440066:9121
Ext.apply.raise # ext-all-rtl-debug.js?_dc=1446847440066:2606
Ext.raise # ext-all-rtl-debug.js?_dc=1446847440066:2691
Ext.define.privates.getRecord # ext-all-rtl-debug.js?_dc=1446847440066:99865
Ext.define.linkTo # ext-all-rtl-debug.js?_dc=1446847440066:99748
Ext.define.privates.applyLinks # ext-all-rtl-debug.js?_dc=1446847440066:100120
If you dive into the source code you will find that the if statement that checks whether myapp.model.film.Film is a class fails..
After spending more than an entire day and using our wildest imagination we managed to figure out what is going on:
First of all check this link: https://www.sencha.com/forum/showthread.php?299699-Any-use-of-a-model-schema-breaks-Tree-model-even-if-not-extending.&p=1118964&viewfull=1#post1118964
You will find out that if you use more than one schema in your source code for no apparent reason these schemas conflict with each other and you are forced to provide a unique schema id.
Now this custom configuration should be propagated to all other configurations meaning that ViewModels will NOT work unless you specify the schema id that is going to be used.
In other words view model will only work if you add a schema like this:
viewModel: {
schema: "youruniqueschemaid",
data: {
title: record ? 'Edit: ' + record.get('title') : 'Add New Film',
},
links: {
currentFilm: record || {
//type: 'Film',
type: 'myapp.model.film.Film',
create: true
}
}
}
Yes the type attribute inside the links could not be more misleading!
You can also use the, shorter version, type: "Film" if you have set the entityName attribute inside the model as Film.
Refactor now
What Sencha should have done instead is force all developers to set the schema explicitly inside a ViewModel and use null if the model is not setup using a schema.
Of course as you can understand solving such an issue could not be done by diving into documentation nor diving inside the source code but rather using a wild guess of what kind of crazy conventions have been used.
In general the framework should be more explicit.
I seek something of this nature
//validation rules in model "User"
attributes: {
age: {
required: true,
type: 'numeric'
}
},
//now in controller, i want to be able to do this
Recipe.validate({age: 'An invalid age because it is a string. I except a validation error as response'});
Problem is, it doesn't work.. it complains about beforeValidate not being available, e.t.c
You need to pass a callback into .validate:
Recipe.validate({age: 'blah'}, function(err){
if (err && err.invalidAttributes) {
console.log(err.invalidAttributes);
} else {
// model is valid
}
});