I have a simple model using the Backbone.Validations plugin.
var LocationModel = Backbone.Model.extend({
validation: {
location_name: {
required : true,
msg : 'A name is required for the location'
}
} // end validation
});
var test = new LocationModel();
test.url = 'http://link-goes-here';
test.save();
It appears that on the save event, it's going ahead and saving my empty model even though the attribute "location_name" is required?
I just did a bunch of testing and the only way I could get it to consistently not send a request was by also creating defaults on the model:
var LocationModel = Backbone.Model.extend({
defaults: {
location_name: null
},
validation: {
location_name: {
required: true,
msg: 'A name is required for the location'
}
} // end validation
});
var test = new LocationModel();
test.on('validated', function() {
console.log(arguments);
});
test.url = '/echo/json';
test.save();
Here's a fiddle. If you comment out the defaults, it sends a request initially, even though the validated event is saying that it's invalid. And then fires validated again without sending the request.
Related
I am using Extjs 3.2grid.I have 5 Records.I am also stoping user to select multiple rows at a time.In grid i have button say approve.What i want is once user selects one record and clicks on approve the selected row coloumn "showRecord" will become 1 and remaing rows will become with showrecord:0
Here is my code
var proxy_surv = new Ext.data.HttpProxy({
type: 'ajax',
api : {
read : 'test/view.action?test='+testid_para+'&flag='+0,
create : 'test/create.action',
update : 'test/update.action',
destroy : 'test/delete.action'
}
});
var store_surv = new Ext.data.Store({
id : 'store_surv',
proxy : proxy_surv,
reader : reader,
autoSave : false
// <-- false would delay executing create, update, destroy
// requests until specifically told to do so with some [save]
// buton.
});
store_surv.on({
beforeload: {
fn: function (store, options) {
// Altering the proxy API should be done using the public
// method setApi.
store_surv.proxy.setApi('read', 'test/view.action?test='+testid_para+'&flag='+0);
}
}
});
And here is my logic
tbar: [{
iconCls: 'icon-user-add',
text: 'Approve',
handler: function(){
// Server hasn't returned yet for these two lines.
var survgrid=Ext.getCmp('grid_surv');
getstore =survgrid.getStore();
count =getstore.getCount();
var selected = survgrid.getSelectionModel().getSelected();
getstore.each(function(record){
if(parseInt(record.get('id'))==parseInt(selected.get('id')))
{
record.set('showRecord','1');
}
else
{
record.set('showRecord','0');
}
record.commit();
});
store_surv.save();
}
}
My problem is its not saving in database
don't use record.commit() it will say the record that all changes have been changed. it gets automatically called by store.sync(). remove record.commit() and it should work. you could also use 'record.save()' which will sync a single record.
The following screenshot shows a combined form for sign-in and sign-up:
The following module is used to render the AuthView:
MyApp.module("User", function(User, App, Backbone, Marionette, $, _) {
User.AuthView = Marionette.ItemView.extend({
className: "reveal-modal",
template: "user/auth",
ui: {
signInForm: "#signin-form",
signUpForm: "#signup-form"
},
events: {
"focus input": "onFocus"
},
onFocus: function() {
console.log("Some input field has received focus.");
},
onRender: function() {
this.signInForm = new Backbone.Form({
schema: {
signInEmail: {
type: "Text",
title: "E-Mail address"
},
signInPassword: {
type: "Password",
title: "Password"
}
}
}).render();
this.ui.signInForm.prepend(this.signInForm.el);
this.signUpForm = new Backbone.Form({
schema: {
signUpEmail: {
type: "Text",
title: "E-Mail address"
},
signUpPassword: {
type: "Password",
title: "Password"
},
signUpPasswordConfirmation: {
type: "Password",
title: "Password confirmation"
}
}
}).render();
this.ui.signUpForm.prepend(this.signUpForm.el);
}
});
});
How can I automatically focus the first field in each sub-form whenever it is rendered? The first fields would be signInEmail for the signInForm and signUpEmail for the signUpForm.
I tried to listen to focus input events. Such an event is triggered when I click into one of the input fields, not before.
Meanwhile, inspired by the current answers I came up with the following helper function:
focusFirstFormField: function(form) {
if (_.isUndefined(form)) {
throw "IllegalStateException: Form is undefined."
}
// TODO: AuthView does not focus first sign-in field.
var firstInput = form.find(':input:first');
if (_.isObject(firstInput)) {
if (firstInput.length > 0) {
firstInput = firstInput[0];
}
else {
throw "IllegalStateException: Form find returns an empty jQuery object."
}
}
_.defer(function() {
firstInput.focus();
});
}
There is still need for improvement, though.
The events object are DOM events which are generally triggered by the user so that's not what you'll likely want to use in this case.
If I'm understanding you correctly you would like to put the focus in the first input of each of the forms but since you can only have focus on one thing at a time and they are rendering together you'll have to choose one or the other.
The simplest option is to add another line at the end of onRender focusing on the input. If your input is generating an input something like this:
<input type="text" name="signInEmail">
Then you can add:
this.$('[name=signInEmail]').focus();
If not you'll have to change the selector this.$(xxxx).focus() to suit.
You can use onDomRefresh event of the view. It will be triggered after view rendered and Dom refreshed.
onDomRefresh: function() {
this.focusFirstInput();
};
focusFirstInput: function() {
this.$(':input:visible:enabled:first').focus();
};
This solution applies to general cases. However, pay attention if you are using Bootstrap. I can't get this work there. Instead, I set autofocus: 'autofocus' in the field and it works.
You can add it to onRender method.
this.ui.signInForm.find('input[type=text]:first').focus();
this.ui.signUpForm.find('input[type=text]:first').focus();
On the onRender method I do :
$(this.el).find(':input[autofocus]').focus();
And I add the autofocus="" flag onto my HTML node. This works for refresh.
I'm trying to use Backbone.localStorage as a backend to an app.
I wrote a Model called Era, and a corresponding EraView. Pressing enter in the EraView causes the model to be saved, and this is where I get the error:
Uncaught Error: A "url" property or function must be specified
urlError backbone.js:1509
_.extend.url backbone.js:515
_.result underscore-min.js:1060
Backbone.sync backbone.js:1410
Backbone.sync backbone.localStorage.js:188
_.extend.sync backbone.js:276
_.extend.save backbone.js:476
karass.EraView.Backbone.View.extend.close era.js:61
karass.EraView.Backbone.View.extend.updateOnEnter era.js:75
Here is the code to the EraView
var karass = karass || {};
// Era Item View
// the DOM element for an era item
karass.EraView = Backbone.View.extend({
tagName: 'li',
className: 'era',
template: _.template( $('#era-template').html() ),
// The DOM events specified to an item
events: {
'dblclick .edit-input': 'edit',
'keypress .edit-input': 'updateOnEnter',
//'blur .edit': 'close',
},
// The EraView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between an era and a EraView in this karass,
// we set a direct reference on the model for convenience.
initialize: function(){
_.bindAll(this);
this.model.on('change', this.render, this);
},
// Re-renders the era item to the current state of the model and
// updates the reference to the era's edit input within the view
render: function(){
this.$el.html( this.template(this.model.attributes));
this.$era_start = this.$('.era-start');
this.$era_end = this.$('.era-end');
this.$era_start.attr('disabled', true);
this.$era_end.attr('disabled', true);
return this;
},
// Switch this view into editing mode, displaying the input field
edit: function(){
this.$('.edit-input').removeAttr('disabled');
this.$el.addClass('editing');
this.$('.edit-input').addClass('editing');
},
// Close the editing mode, saving changes to the era
close: function(){
this.$('.edit-input').attr('disabled', true);
var start = this.$era_start.val().trim();
var end = this.$era_end.val().trim();
if(start && end){
this.model.save({from: start, until: end});
}
this.$el.removeClass('editing');
this.$('.edit-input').removeClass('editing');
this.trigger('close');
},
updateOnEnter: function(e){
if(e.which !== ENTER_KEY && (!this.$era_start.val().trim() || !this.$era_end.val().trim())){
return;
}
this.close();
}
});
And this is the code for the era model:
var karass = karass || {};
karass.Era = Backbone.Model.extend({
defaults: {
from: '',
until: ''
},
});
I thought I didn't need a url while using localStorage.
Edit: I forgot to mention that while this behavior occurs in the Era/EraView itself, it also occurs in the Name model, which extends Era. Name in turn belongs in a Names collection. I don't know if this makes a difference, but I figured I add it.
Edit 2: The Names collection looks like this:
karass.Names = History.extend({
model: karass.Name,
localStorage: new Backbone.LocalStorage('karass-names'),
});
Edit 3: I posted all the code on jsfiddle: http://jsfiddle.net/herrturtur/CRv6h/
You don't need an url while using localStorage. But you need to set the localStorage property on your model or on your collection (if you set the localStorage on a collection the models inside the collection will "inherit" this setting):
karass.Era = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("EraCollection"),
// the EraCollection should be an unique name within your app.
defaults: {
from: '',
until: ''
},
});
If you don't setup the localStorage property the plugin falls back to the default ajax sync so you get the uri missing exception.
I want to check if the content of a textfield has exist in the database,so I define a VTypes:
Ext.apply( Ext.form.field.VTypes,{
sampleSetExist: function(val, field) {
alert('triggered');
var result;
Ext.Ajax.request({
url: 'sampleSetExist.action',
params: {
name:val
},
async:false,
success: function(response){
var res = response.responseText;
var jsonObject = Ext.decode( res );
if( jsonObject.exist == true )
result = false;
else
result = true;
}
});
return result;
}
});
and the textfield like this:
{
fieldLabel: 'Name',
itemId: 'sampleSetName',
name: 'ssi.name',
allowBlank: false,
validateOnBlur: true,
validateOnChange: false,
vtype: 'sampleSetExist',
vtypeText: 'The name has exist!'
}
……
when the textfield blur,the validator function sampleSetExist will be triggered for three times.Why will this happen?
As mentioned in another answer, the vtype config (and also the validator function) should be used for quick local, synchronous validation (like making sure a date range end is after a date range start).
A better way to do this sort of remote validation would be the field change event to do the work. You could save an Ajax request to the field and use it to do your own validation. This should accomplish what you're trying to do on the client side, but remember that users can perform their own form submission. Make sure you re-validate the submitted values when they reaches your server.
In addition to the code below, you would probably want extra validation at submission time to make sure the user hasn't tried to submit the form before a validation request finished.
Note: This might not be the best way, but it should point you in the right direction.
// Field config
{
fieldLabel: "Name",
checkChangeBuffer: 500, // How long to wait after last change event
listeners: {
change: MyApp.onNameFieldChange
},
// Rest of config here
}
// "MyApp" is whatever namespace you're using for this form
MyApp.onNameFieldChange = function(field, newValue) {
var request = field.validationXhr;
// Cancel any existing requests
if (request) {
Ext.Ajax.abort(request);
}
// Send your params for validation
field.validationXhr = Ext.Ajax.request({
url: "sampleSetExist.action",
/* params, timeout, etc. */
success: function(response) {
var jsonResponse = Ext.decode(response.responseText);
if (jsonResponse.exist) {
// markInvalid only displays an error message. It doesn't actually
// set the field state to invalid. Extra logic may be needed.
field.markInvalid("The name " + newValue + " already exists!");
}
else {
field.clearInvalid();
}
}
});
}
You should not worry about it being invoked three times. VTypes are not designed for things requiring asking the server. This kind of validations should be performed upon form submission. VTypes suppose to return true or false and are only intended for field or record validation.
I have a custom data store which stores the information whether the store has been loaded once or not.
/**
* Custom DataStore which stores the information whether data has been loaded once or not.
*/
Ext.example.Store = Ext.extend(Ext.data.Store, {
loaded: false,
initComponent: function() {
this.superclass().initComponent.call(this);
this.addEvents('load','beforeload');
},
isLoaded : function() {
return this.loaded;
},
listeners: {
'load' : function(store,records,options) {
this.loaded = true;
}
}
});
This was working fine until recently I added a 'beforeload' event listener to an instance of this store. The 'load' listener does not get invoked now.
var accountsDataStore = new Ext.example.Store({
id : 'account',
proxy : new Ext.data.HttpProxy({
url : 'app/account/fetch/all',
method : 'post',
api : {
destroy : 'app/account/delete'
}
}),
reader : accountReader,
writer : accountWriter,
remoteSort : true,
sortInfo : {
field : 'createdOn',
direction : "DESC"
},
listeners: {
beforeload: function(store, options) {
var sort = options.params.sort;
// setting the sort parameters to match the property names in the DTO
switch(sort) {
case 'company' :
options.params.sort = 'company.name';
break;
default:
// do nothing
}
}
}
});
What could I be doing wrong here? Also please let me know your suggestions to improve
The problem is that you should never set objects on the prototype, unless you really know what it means (that it will be shared by all instances and can be overridden on a per instance basis)
In Ext JS, I only set config options on the prototype that are only for convenience and may be overridden by the caller.
Your first class Ext.example.Store puts listeners on its prototype.
Then you go and overwrite it in accountsDataStore by passing in a listeners object into the config.
To fix your problem, instead of setting listeners on the prototype, just call this.on('event') from the constructor.
/**
* Custom DataStore which stores the information whether data has been loaded once or not.
*/
Ext.example.Store = Ext.extend(Ext.data.Store, {
loaded: false,
constructor: function() {
this.superclass().constructor.call(this);
// No need to call this, you're not adding any events
// this.addEvents('load','beforeload');
this.on('load', function(store,records,options) {
this.loaded = true;
}, this);
},
isLoaded : function() {
return this.loaded;
}
});
The documentation for the "beforeload" event states:
"Fires before a request is made for a new data object. If the
beforeload handler returns false the load action will be canceled."
You could trying returning true from your beforeload listener to ensure the load action still runs.
store.totalCount
if loaded, this property return a number
else
this property is undefined
(extjs-4.1.0-rc1)
Ok, I think there is a problem with scope. Please try it this way:
listeners: {
'load' : {
fn : function(store,records,options) {
console.log(this, this.loaded);
this.loaded = true;
},
scope : this
}
}
Or you can use:
listeners: {
'load' : function(store,records,options) {
store.loaded = true;
}
}