Seems backbone validate has some issue in executing.
Person = Backbone.Model.extend({
defaults:{
name:'Jagadeesh',
age:28,
child:'Bhuvan'
},
initialize: function(){
this.bind('error',function(model,error){
console.log('error');
console.log(error);
});
},
validate:function(attributes){
if(attributes.age < 0){
console.log('You cant be negative years old');
return "You can't be negative years old";
}
}
});
person.set({name:"Jags", age:-1}
when I assign age - '-1', validate function is not throwing error.
"Set" doesn't execute validate autoamtically. From the docs:
By default validate is called before save, but can also be called before set if {validate:true} is passed.
Call
person.set({name:"Jags", age:-1}, {validate: true})
if you want it validated.
See http://backbonejs.org/#Model-validate
Related
I`m building a simple backbone application, and have a problem with success callback function in my View.
Here is a code
var EditUser = Backbone.View.extend({
el: '.page',
render: function(option){
var that = this;
if(option.id){
that.user = new User({id : option.id});
that.user.fetch({
success:function(user){
var template = _.template($("#edit-user-template").html());
that.$el.html(template({user: user}));
}
});
}else{
var template = _.template($('#edit-user-template').html());
that.$el.html(template({user: null}));
}
},
events:{
'submit .edit-user-form': 'saveUser',
'click .delete': 'deleteUser'
},
saveUser: function(ev){
var userDetails = $(ev.currentTarget).serializeObject();
var user = new User();
user.save(userDetails,{success: function(){
router.navigate('',{trigger:true});
},
error: function(e){console.log(e);}
});
return false;
},
deleteUser:function(ev){
this.user.destroy({
success: function(){
router.navigate('',{trigger:true});
}
})
return false;
},
wait:true
});
On the SaveUser function,query send to the server correct, but after this, success callback function is not called, for navigating to the app home page.
The same problem appear with deleteUser method.
Any ideas what is the problem? Thanks!
It could be related to the response type from your server, the expected response is a JSON object that will be set on your attributes, but if the response is different as "text" for example, the parse fails.
Here is a fiddle for demo using Mock request
https://jsfiddle.net/gvazq82/rdLmz2L2/1/:
$.mockjax({
url: "hello.php",
responseTime: 0,
//responseText: 'A text response from mock ajax'
responseText: '{"a": "a"}'
});
In this example, the error function is been called that is not happening in your case, Is it possible your app defines some default behavior for "Ajax" calls?.
I need more information to be able to determinate this issue, but hope this give you some guidance with your problem.
I have already referred to this: create/update user story using rally app sdk
Here's my code:
_update_iteration_of_parent: function(pOID, iteration){ //iteration is the OID of iteration to be added.
console.log("Updating Iteration ",'/iteration/'+iteration);
var me = this;
Rally.data.ModelFactory.getModel({
type: 'User Story',
success: function (model){
var that = this;
//console.log("objectid #",objectid," latestpsi ",latestpsi);
this.model = model;
var id = pOID;
console.log("_readRecord ",id);
this.model.load(id,{
fetch: ['Name','Iteration'],
callback: function (record, operation){
//console.log('name .. ', record.get('Name'));
if(operation.wasSuccessful()){
console.log('Iteration ',record.get('Iteration'));
record.set('Iteration','/iteration/'+iteration);
record.save({
callback: function(record,operation){
if(operation.wasSuccessful()){
console.log("Operation Successful");
}
else
console.log("Not");
},
scope: this,
});
}
},
scope: this
});
}
});
}
I am not able to update the iteration, and it always logs "Not" indicating it was not a success when record.set('Iteration','/iteration/'+iteration) is called. There is no problem in getting the values for pOID and iteration.
Your method is called _update_iteration_of_parent which indicates that you are trying to set Iteration on a parent story. If this is true, that explains the failure to set iteration. It is not possible to schedule a parent (epic) story for Iteration in UI or API.
There is a Marionette.js view which acts as a login form. The following code sample shows the relevant code parts including an error I already fixed:
MyApp.module("User", function(User, App, Backbone, Marionette, $, _) {
User.LoginView = Marionette.ItemView.extend({
className: "reveal-modal",
template: "user/login",
ui: {
signInForm: "#signin-form"
},
events: {
"submit #signin-form": "onSignInFormSubmit"
},
onRender: function() {
var self = this;
var $el = this.$el;
// [...] Render schema
_.defer(function(){
$el.reveal({
closeOnBackgroundClick: false,
closed: function(){
self.close(); // <-- This is incorrect. Do not close the ItemView directly!
}
});
});
},
onSignInFormSubmit: function(event) {
event.preventDefault();
var errors = this.signInForm.validate();
var data = this.signInForm.getValue();
// [...] Notify that data has been submitted.
},
hideForm: function() {
this.$el.trigger("reveal:close");
}
});
});
I noticed a major mistake in my implementation. In the callback function closed of Reveal I decided to close the ItemView directly which is wrong as you can read in the documentation of Marionette.js:
View implements a close method, which is called by the region managers automatically.
Bugfix: Instead close() should be called on the region. I fixed this error.
Now I ask myself how can I actually write a test which covers the problem. I use Jasmine for testing. I noticed that the event handler onSignInFormSubmit is no longer called after I have incorrectly closed the ItemView and try to re-submit the form.
Here is a first draft of the test which unfortunately does fail also with the bugfix:
it("should call the submit handler for the sign-in form", function() {
spyOn(userController.loginView, "onSignInFormSubmit");
spyOn(userController.loginView.signInForm, "validate").andCallFake(function(params) {
return null;
});
userController.loginView.hideForm();
userController.loginView.ui.signInForm.trigger("submit");
expect(userController.loginView.onSignInFormSubmit).toHaveBeenCalled();
});
Maybe one could also test if the event handler is registered such as:
expect(userController.loginView.events["submit #signin-form"]).toEqual("onSignInFormSubmit");
I've made view to listen to model changes. When there is change in model render function will be called and alert window will be prompted. But it is coming twice that means render function is calling twice because of two change events.
WineDetails View
app.WineView = Backbone.View.extend({
template:_.template($('#tpl-wine-details').html()),
initialize:function () {
this.model.bind("change", this.render, this);
},
render:function (eventName) {
if(eventName)alert("changed")
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
events:{
"change input":"change",
"click .save":"saveWine",
"click .delete":"deleteWine"
},
change:function (event) {
var target = event.target;
console.log('changing ' + target.id + ' from: ' + target.defaultValue + ' to: ' + target.value);
// You could change your model on the spot, like this:
// var change = {};
// change[target.name] = target.value;
// this.model.set(change);
},
saveWine:function () {
this.model.set({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
if (this.model.isNew()) {
var self = this;
app.router.wineList.create(this.model,{wait:true,success:function(){
app.router.navigate('wines/'+self.model.id,false);
}});//add event,request event on collection will be triggered
} else {
this.model.save();//change event,request event on model will be triggered
}
return false;
},
onClose:function()
{
alert("onclose");
this.model.unbind("change",this.render);
}
And its not because of zombie view because i've this following code
Backbone.View.prototype.close=function()
{
alert("closing view "+this);
if(this.beforeClose){
this.beforeClose();
}
this.remove();
this.unbind();
if(this.onClose){
this.onClose();
}
}
please tell me what is wrong in this code. Thank u :)
So, as you didn't provide the information regarding your Model#save call, I'll assume it's the one within your view. I'll also assume the problem doesn't come from zombie views because you're following an outdated method for that. I'll make a guess here about what's probably happening:
this.model.set({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
// ...
this.model.save();
Ok, the first part (the set method) will trigger a first change event.
The second part, the save method may trigger another change. Another set will indeed be done with the attributes sent back from the server.
Possible solution to a possible problem:
save can be passed attributes, and a wait flag to postpone the use of the set method until the server responds:
this.model.save({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
}, {wait: true});
You can also try it by creating always a new instance of your model like :
var wine = new WineModel({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
And then save it like :
wine.save(null, success: function(model){
// do your call action on call back
},
beforeSend: function() {
// before save
}
error: function(model, errors) {
// on error occurred
});
I'm just getting started with Jasmine and trying to set up some tests for the first time. I have a Backbone collection. I figured I would get my collection as part of the beforeEach() method, then perform tests against it.
I have a test json object that I used while I prototyped my app, so rather than mocking an call, I'd prefer to reuse that object for testing.
Here's my code so far (and it is failing).
describe("Vehicle collection", function() {
beforeEach(function() {
this.vehicleCollection = new Incentives.VehiclesCollection();
this.vehicleCollection.url = '../../json/20121029.json';
this.vehicleCollection.fetch();
console.log(this.vehicleCollection);
});
it("should contain models", function() {
expect(this.vehicleCollection.length).toEqual(36);
console.log(this.vehicleCollection.length); // returns 0
});
});
When I console.log in the beforeEach method -- the console look like this ...
d {length: 0, models: Array[0], _byId: Object, _byCid: Object, url: "../../json/20121029.json"}
Curiously when I expand the object (small triangle) in Chrome Developer Tools -- my collection is completely populated with an Array of vehicle models, etc. But still my test fails:
Error: Expected 0 to equal 36
I'm wondering if I need to leverage the "waitsFor()" method?
UPDATE (with working code)
Thanks for the help!
#deven98602 -- you got me on the right track. Ultimately, this "waitsFor()" implementation finally worked. I hope this code helps others! Leave comments if this is a poor technique. Thanks!
describe("A Vehicle collection", function() {
it("should contain models", function() {
var result;
var vehicleCollection = new Incentives.VehiclesCollection();
vehicleCollection.url = '/json/20121029.json';
getCollection();
waitsFor(function() {
return result === true;
}, "to retrive all vehicles from json", 3000);
runs(function() {
expect(vehicleCollection.length).toEqual(36);
});
function getCollection() {
vehicleCollection.fetch({
success : function(){
result = true;
},
error : function () {
result = false;
}
});
}
});
});
Just glancing at your code, it looks to me like fetch has not yet populated the collection when you run the expectation.
You can use the return value from fetch to defer the expectation until the response is received using waitsFor and runs:
beforeEach(function() {
this.vehicleCollection = new Incentives.VehiclesCollection();
this.vehicleCollection.url = '../../json/20121029.json';
var deferred = this.vehicleCollection.fetch();
waitsFor(function() { return deferred.done() && true });
});
it("should contain models", function() {
runs(function() {
expect(this.vehicleCollection.length).toEqual(36);
});
});
I haven't actually tried this can't guarantee that it will work as-is, but the solution will look something like this. See this article for more on asynchronous testing with Jasmine.
the collection.fetch() is asyn call that accepts success and error callbacks
var result;
this.collection.fetch({success : function(){
result = true;
}})
waitsFor(function() {
return response !== undefined;
}, 'must be set to true', 1000);
runs(function() {
expect(this.vehicleCollection.length).toEqual(36);
console.log(this.vehicleCollection.length); // returns 0
});