I'm trying to log an error when an attribute of a variable is changed, but the validate is never being triggered, this is my code:
Person = Backbone.Model.extend({
initialize: function() {
console.log("hello world");
this.bind('change:name', function() {
console.log(this.get('name') + ' is now the value for the name');
});
this.bind('error', function(model, error) {
console.log(error);
});
},
defaults: {
name: 'Bob Hope',
height: 'unknown'
},
validate: function(attributes) {
if(attributes.name == 'blank'){
return 'no';
}
}
});
var person = new Person();
person.set({name: 'blank'});
I have even tried called set like this:
person.set({name: 'blank'}, {validate: true});
but that doesn't work either, I'm using version 1.0.0.
As per the documentation:
By default validate is called before save, but can also be called
before set if {validate:true} is passed.
Also, the event that is triggered is invalid, not error, so try this:
this.bind('invalid', function(model, error) {
console.log(error);
});
Related
I am trying to fetch an entry in a collection with:
client/views/home.js:
criticalCrewNumber = ConfigValues.find({
name: 'criticalCrewNumber'
}).fetch()[0].value;
But I'm getting error:
Uncaught TypeError: Cannot read property 'value' of undefined
If I run the code in the browser console, the desired value is returned as a string.
I have tried various things, e.g. using findOne; placing the code elsewhere in the app; using iron-router waitOn for the subscription to come, etc. Every attempt so far has failed as I end up with undefined.
Here's how the collection is defined, published and subscribed to:
lib/config/admin_config.js:
ConfigValues = new Mongo.Collection("configValues");
ConfigValues.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Name",
max: 200
},
value: {
type: String,
label: "Value",
max: 200
}
}));
both/collections/eventsCollection.js:
if (Meteor.isClient) {
Meteor.subscribe('events');
Meteor.subscribe('config');
};
server/lib/collections.js
```
Meteor.publish('events', function () {
return Events.find();
});
Meteor.publish('config', function () {
return ConfigValues.find();
});
```
Does anyone know what's going on? Thanks.
Consider using ReactiveVar (and Meteor.subscribe callbacks):
criticalCrewNumber = new ReactiveVar();
Meteor.subscribe('config', {
onReady: function () {
var config = ConfigValues.findOne({name: 'criticalCrewNumber'});
if (config) {
criticalCrewNumber.set(config.value);
} else {
console.error('No config value.');
}
},
onStop: function (error) {
if (error) {
console.error(error);
}
}
});
I'm designing an abstract superclass in javascript using dojo. The purpose of the class is to define the contract for any UI element that can enable the user to select an entity with a unique identifier. So far this is my code.
define(
[
"dojo/Evented",
"dojo/_base/declare"
],
function(Evented, declare){
var NotImplementedException = "Method not implemented";
return declare("EntitySelector", Evented, {
//it's implementation should change the entity selected by the UI
select: function(id){
throw NotImplementedException;
},
//it's implemantation should populate the UI with the data to be selected.
setData: function(dataStore){
throw NotImplementedException;
}
});
});
I also need that subclasses fire an onSelect event, so I can respond to the user actually selecting an entity.
Is there a way (other than documentation) to indicate that subclasses should fire the onSelect event on their implementation?
Fo example, in Java you usually define a public void addEventListener(ListenerInterface) method to indicate that subclasses should fire an event. In C# you can declare an event EventHandler to achieve something similar.
Is there a way in dojo or general Javascript to achieve this?
You could use something like the code bellow.
It rely on the widget lifecycle provided by _WidgetBase and check whether or not some method are implemented.
Additionally it automatically add the "onSelect" event when the "select" method is fired.
Another approach is to hook on the on method and check if an event is attached (see how it is for the foo event.
require([
"dojo/Evented",
"dojo/aspect",
"dijit/_WidgetBase",
"dojo/_base/lang",
"dojo/_base/declare"
], function(Evented, aspect, _WidgetBase, lang, declare) {
var NotImplementedException = "Method not implemented";
var EntitySelector = declare([_WidgetBase, Evented], {
hasOnFooEvent: false,
postMixInProperties: function() {
if (typeof this.select !== 'function') {
throw NotImplementedException;
} else {
this.own(aspect.after(this, 'select', lang.hitch(this, 'emit', 'onSelect')))
}
if (typeof this.setData !== 'function') {
throw NotImplementedException;
}
this.inherited(arguments);
},
on: function(eventName) {
if(eventName === 'foo') {
this.hasOnFooEvent = true;
}
this.inherited(arguments);
},
postCreate: function() {
if(!this.hasOnFooEvent) {
throw NotImplementedException;
}
this.inherited(arguments);
}
});
var NotOkWidget = declare(EntitySelector, {});
var OkWidget = declare(EntitySelector, {
postCreate: function() {
this.on('foo', function() {});
this.inherited(arguments);
},
select: function() {},
setData: function() {}
});
try {
var a = new NotOkWidget();
} catch (e) {
console.log('Implementation error on NotOkWidget');
}
try {
var a = new OkWidget();
a.on('onSelect', function() { console.log('onSelect event is fired'); });
a.select();
} catch (e) {
console.log('Implementation error on OkWidget');
}
});
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>
I'm learning Backbone and want to "mock" the results of a .fetch() call within a model. I do not want to use a testing library or actually hit an external service.
Basically I have a setting in my model, where if this.options.mock === true, then just use an internal JSON object as the "result" of the fetch. Else, actually hit the API with a real AJAX request.
However, this doesn't seem to work. My view successfully renders with the model data when I hit the actual API ("real" fetch), but not whenever I try and pass in fake data.
Is there a way to fake a Fetch response in Backbone, without bringing in a testing library like Sinon?
here is the complete model (at least the relevant portions of it). Basically, the model fetches data, and formats it for a template. and then the view which owns the model renders it out.
'use strict';
(function (app, $, Backbone) {
app.Models.contentModel = Backbone.Model.extend({
/**
* Initializes model. Fetches data from API.
* #param {Object} options Configuration settings.
*/
initialize: function (options) {
var that = this;
that.set({
'template': options.template,
'mock': options.mock || false
});
$.when(this.retrieveData()).then(function (data) {
that.formatDataForTemplate(data);
}, function () {
console.error('failed!');
});
},
retrieveData: function () {
var that = this, deferred = $.Deferred();
if (typeof fbs_settings !== 'undefined' && fbs_settings.preview === 'true') {
deferred.resolve(fbs_settings.data);
}
else if (that.get('mock')) {
console.info('in mock block');
var mock = {
'title': 'Test Title',
'description': 'test description',
'position': 1,
'byline': 'Author'
};
deferred.resolve(mock);
}
else {
// hit API like normal.
console.info('in ajax block');
that.fetch({
success: function (collection, response) {
deferred.resolve(response.promotedContent.contentPositions[0]);
},
error: function(collection, response) {
console.error('error: fetch failed for contentModel.');
deferred.resolve();
}
});
}
return deferred.promise();
},
/**
* Formats data on a per-template basis.
* #return {[type]} [description]
*/
formatDataForTemplate: function (data) {
if (this.get('template') === 'welcomead_default') {
this.set({
'title': data.title,
'description': data.description,
'byline': data.author
});
}
// trigger the data formatted event for the view to render.
this.trigger('dataFormatted');
}
});
})(window.app, window.jQuery, window.Backbone);
Relevant bit from the view (ContentView):
this.model = new app.Models.contentModel({template: this.templateName});
this.listenTo(this.model, 'dataFormatted', this.render);
Is the data being set so fast that the listener hasn't been set up yet?
You can override the fetch function like this.
var MockedModel = Backbone.Model.extend({
initialize: function(attr, options) {
if (options.mock) {
this.fetch = this.fakeFetch;
}
},
url: 'http://someUrlThatWIllNeverBeCalled.com',
fakeFetch: function(options) {
var self = this
this.set({
'title': 'Test Title',
'description': 'test description',
'position': 1,
'byline': 'Author'
});
if (typeof options.success === 'function') {
options.success(self, {}, {})
}
}
});
var mockedModel = new MockedModel(null, {
mock: true
})
mockedModel.fetch({
success: function(model, xhr) {
alert(model.get('title'));
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Problem here isn't with the actual implementation of retrieveData but with the way it's being called. When you resolve the deferred before returning you're basically making it instant. This leads to formatDataForTemplate being called while your model is still initializing.
So when you do
this.model = new app.Models.contentModel({template: this.templateName});
this.listenTo(this.model, 'dataFormatted', this.render);
The dataFormatted event ends up being triggered before the listener has registered.
One solution is to use a timeout which should work with just
setTimeout(function() {
deferred.resolve(mock);
});
as that will delay the resolve untill the next round of the event loop when the listener is in place.
Another solution, not involving the setTimeout would be to not call retrieveData during model initialization but rather let the view do it after it has attached its listeners.
this.model = new app.Models.contentModel({template: this.templateName});
this.listenTo(this.model, 'dataFormatted', this.render);
this.model.retrieveData();
I would prefer the latter but if this is just about mocking data to work offline it doesn't really matter in my opinion.
Unrelated to that it's worth noting that the actual signature for initialize on a model is new Model([attributes], [options]) so your initialize should probably look like this
initialize: function (attributes, options) {
var that = this;
that.set({
'template': options.template,
'mock': options.mock || false
});
Just for the sake of readability. That again means that since you are passing only one object you should not need to call set at all.
I'm using the semantic-ui modal to allow users to insert data. It has an onApprove callback which allows you to return false to keep the modal open if there are any problems. My data is inserted into a DB, which returns false if there's any error. What's the best way of keeping the modal open if there's an error during this async operation?
Here's my code (coffeescript):
$('#verification-modal')
.modal('setting', {
detachable: false,
onApprove: validateVerificationForm
closable: false
})
validateVerificationForm = () ->
formData = $('.form').serializeArray()
formatted = format($formData);
ID_Details.insert(formatted, (errs, id) ->
if errs
false
else
true
Obviously the anonymous function is returning true/false into the context of the function. What's the best way to return it to the modal?
You can use a local reactive variable:
var data = new ReactiveDict();
Template.modalTemplate.created = function() {
data.set('isError', false);
};
Template.modalTemplate.helpers({
isError: function() {
return data.get('isError');
},
});
var yourMethodWithAsync = function() {
...
async(..., function(error) {
if(error) {
data.set('isError', true);
}
...
});
};
This one is wired.
This fires from a grid toolbar button click:
// fires when the client hits the add attachment button.
onAddAttachmentClick: function () {
var uploadAttachmentsWindow = new Nipendo.ProformaInvoice.Attachment.UploadWindow({
invoice: this.invoice,
maxFileSizeInMB: this.maxFileSizeInMB
});
uploadAttachmentsWindow.on('uploadcomplete', function (win, message) {
if (message.msg !== 'success') {
return;
}
win.close();
var store = this.getStore();
store.setBaseParam('useCache', false);
store.load();
this.fireEvent(
'attachmentuploaded',
this.invoice.ProformaInvoiceNumber,
this.invoice.VendorSiteID,
this.invoice.CustomerSiteID);
}, this);
uploadAttachmentsWindow.show();
} // eo onAddAttachmentClick
This is what happens on the uploadcomplete event:
this.uploadBtn.on('click', function () {
var form = this.uploadForm.getForm();
if (!form.isValid()) {
return;
}
form.submit({
url: 'XXX.ashx',
waitMsg: Nipendo.Localization.UploadingAttachment,
scope: this,
success: function (form, action) {
this.fireEvent('uploadcomplete', this, {
msg: 'success',
response: action.response
});
},
failure: function (form, action) {
switch (action.failureType) {
case Ext.form.Action.CLIENT_INVALID:
this.fireEvent('uploadcomplete', this, {
msg: 'Form fields may not be submitted with invalid values'
});
break;
case Ext.form.Action.CONNECT_FAILURE:
this.fireEvent('uploadcomplete', this, {
msg: 'Ajax communication failed'
});
break;
case Ext.form.Action.SERVER_INVALID:
Ext.Msg.alert(action.result.title, action.result.message);
this.fireEvent('uploadcomplete', this, {
msg: action.result.message
});
break;
}
}
});
}, this);
On IE 8 I am getting this error in the debugger:
I have no idea what object is missing... from my check they are all defined.
Any idea anyone?
Notice that I have an event firing from a listener (I am suspecting it to be the root of the problem).
It is hard to see but the error occuers in ext-all.js in the fire method.
I have found the answer in : https://stackoverflow.com/a/3584887/395890
Problem was I was listing to events is 2 different windows, that is not possible in Ext.
What I have done to solv it was to call the opner window from the pop up window to notify about changes.