Backbone is not sending attributes along with REST request - javascript

I am currently working on a backbone application, where you have to specify a guest name, in order to enter.
Guest = Backbone.Model.extend({
urlRoot : ftd.settings.rest + '/guest',
defaults: {
name : '',
},
validate: function( attr ) {
var errors = [];
if(attr.name.length < 4) {
errors.push({message : "You nickname, must be atleast 4 chars", name : 'guestNickname'});
}
if(errors.length > 0) {
return errors;
}
}
});
return Guest;
So on my frontpage the user sets a username and a new guest is instantiated, this is my front page view that handles the guest creation.
createGuest: function( ev ) {
ev.preventDefault();
// Get nickname.
var guest = new Guest();
guest.bind( 'error', function( model, errors ) {
_.each( errors, function( err ) {
$('input[name=' + err.name + ']').addClass('invalid');
// add a meesage somewhere, using err.message
}, this );
});
guest.save({'name' : $('input[name="guestNickname"]').val()}, {
success:function(model, response) {
console.log('Successfully saved!');
},
error: function(model, error) {
console.log(model);
console.log(error.error());
}
});
},
My problem is that, when backbone makes the request, it sends a OPTIONS request without the specified name, i even checked the packet in Wireshark, what am i doing wrong?
Bonus question: Why is backbone sending a OPTIONS request?

Apparently Backbone (or really your browser) sends an OPTIONS request during a backbone save with side effects because it tries to emulate "true" REST including PUT and DELETE requests, and not all servers support those commands.
According to this:
http://backbonejs.org/#Sync-emulateHTTP
If you set
Backbone.emulateHTTP = true;
then it will just try to POST.

Related

Getting messages in chat history to display in messenger Pubnub

I have come to post this question after 2 days of torture not being able to understand how I can actually publish the historic messages stored on my pubnub storage account. To try and understand it at its most basic I have made a chat app and used the history function as described in the SDK but still every time I refresh the page the messages are lost. I have tried the backfill and the restore attributes in subscribe with no luck. All I want to do is click refresh on chrome and see the messages still there.
<div><input id=input placeholder=you-chat-here /></div>
Chat Output
<div id=box></div>
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.4.0.min.js"></script>
<script>(function(){
var pubnub = new PubNub({ publishKey : 'demo', subscribeKey : 'demo' });
function $(id) { return document.getElementById(id); }
var box = $('box'), input = $('input'), channel = 'chat';
pubnub.addListener({
message: function(obj) {
box.innerHTML = (''+obj.message).replace( /[<>]/g, '' ) + '<br>' + box.innerHTML
}});
pubnub.history({
channel: 'chat',
reverse: true, // Setting to true will traverse the time line in reverse starting with the oldest message first.
count: 100, // how many items to fetch
callback : function(msgs) {
pubnub.each( msgs[0], chat );
}
},
function (status, response) {
// handle status, response
console.log("messages successfully retreived")
});
pubnub.subscribe({channels:[channel],
restore: true,
backfill: true,
ssl: true});
input.addEventListener('keyup', function(e) {
if ((e.keyCode || e.charCode) === 13) {
pubnub.publish({channel : channel, message : input.value,x : (input.value='')});
}
});
})();
</script>
</body>
EDIT: updated link that was broken. New version of history function is called fetchMessages.
I think your history code is not correct. No need for the callback as your code response will be in the function argument. This example is from the JavaScript SDK docs.
// deprecated function
pubnub.history(
{
channel: 'chat',
},
function (status, response) {
var msgs = response.messages;
if (msgs != undefined && msgs.length > 0) {
// if msgs were retrieved, do something useful
console.log(msgs);
}
}
);
// latest function (response output format has changed)
pubnub.fetchMessages(
{
channels: ['chat']
},
(status, response) => {
console.log(msgs);
}
);

Backbone model is not saved

var elementUrlRoot = api_url + '/elements';
var elementModel = Backbone.Model.extend({
'idAttribute': '_id' //mongoDB
, 'urlRoot': elementUrlRoot
, defaults: {
"signature": "",
"group": 0
}//defaults
});
var elementCollection = Backbone.Collection.extend({
model: elementModel
, 'url': elementUrlRoot
});
var testmodel = new elementModel({DOM_id: 111});
testmodel.save({signature: "test"},
{
error: function (model, response, options) {
console.log('test model save error:', response);
},
success: function () {
console.log('test model save success');
}
}
);
My backbone model is not saved to the server when I update it.
I have set the urlRoot attribute of the Model (which according to the documentation should not be necessary). But there are still no HTTP requests being issued.
Update:
I have added a success method in the callback. It is being executed.
But there are no requests being sent to the server.
Update:
I found the error. I had added this code to save a whole collection.
Backbone.Collection.prototype.syncCollection = function (options) {
console.log('syncing the collection');
Backbone.sync("create", this, options);
};
It worked and I was able to save collections with it.
But it seems to have caused a problem with saving individual models. Requests are issued when I removed it.
Your urlRoot is needed because your model is not part of a collection.
Try unquoting your urlRoot attribute on the left side of the assignment
http://backbonejs.org/#Model-urlRoot

EmberJS: Customize the REST URL of a related model

In the Ember Documentation, it mentions this:
An adapter is an object that knows about your particular server
backend and is responsible for translating requests for and changes to
records into the appropriate calls to your server.
For example, if your application asks for a person record with an ID
of 1, how should Ember Data load it? Is the URL /person/1 or /resources/people/1?
In my example, I have a User model with related Messages. When the Ember store retrieves related messages for a user, it generates a query in the form /messages/?ids=18 using something like this:
user = this.store.find('user', params.user_id);
user.get('messages');
I'd like to load these messages with a URL like /users/2/messages/.
I looked through Ember's documentation on adapters and wasn't able to find anything to implement this specifically. How do I customize the URL?
You can create a message adapter that builds the right url for the end point.
import Ember from 'ember';
import DS from 'ember-data';
export default DS.ActiveModelAdapter.extend({
find: function(store, type, id) {
return this.ajax(this.buildURL(type.typeKey, id), 'GET');
},
createRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
return this.ajax(this.buildURL(type.typeKey, null), "POST", {data: data});
},
buildURL: function(type, id) {
var url = [],
host = Ember.get(this, 'host'),
prefix = this.urlPrefix(),
user_id = this.container.lookup('controller:user').get('id');
// append user and id to the url
url.push('users/' + user_id);
if (type) { url.push(this.pathForType(type)); }
if (id) { url.push(id); }
if (prefix) { url.unshift(prefix); }
url = url.join('/');
if (!host && url) { url = '/' + url; }
return url;
}
});
I hope this is helpful.
Cheers

Fetch BackboneJS model from id (I think)

I'm responsible to make a cache-layer, that uses local storage, that is between the server and the client to lower the bandwidth and server load.
I have two models, 'model' and 'modelContent'. These are connected to each other with an ID.
model is populated as it should be with all the parameters that are in the model.
modelContent is not populated when its content is fully fetched from the server.
I want to wait until the modelContent has it's attribute 'body' fully populated and then add it to the cache. Right now modelContent's 'body'-attribute is just an empty string (which is the default value).
I have created a base-model that model and modelContent inherits from that contains an override for sync for the read-method. The reason for this is that I read http://engineering.linkedin.com/mobile/linkedin-ipad-using-local-storage-snappy-mobile-apps and that is the way I want it to work but with two models that belong together.
I'm very new at BackboneJS and Grails (which the project uses as well) so I'm hoping someone can point me in the right direction.
My base-model:
var basic_model = Backbone.Model.extend({
sync: function(method, model, options) {
if ( method === 'read' ) {
uid = this.get("UID");
if ( uid ) {
var success = options.success;
options.success = function(resp, status, xhr) {
console.log(resp);
}
} else {
Backbone.sync(method, model, options);
}
} else {
Backbone.sync(method, model, options);
}
}
modelContent:
var MailContentItem = basic_model.extend({
urlRoot: project_webroot +'modelContent',
idAttribute: "UID",
defaults: function() {
return _.extend({}, basic_model.prototype.defaults, {
isHTML: false,
body: ""
});
}
});

meteor users not synchronize published sub fields of profile

Working on my social app I've found a strange behavior in the collection Meteor.users, this problem does not occur with other Collections using the same methodologies
I would like to have an initial list of users downloading a minimum number of information for everyone and when I open the panel to a specific user I subscribe a different showing more information if the specified user is a friend of mine.
But after subscribe the client collection Meteor.users is not updated!
CLIENT
Meteor.startup(function() {
Meteor.subscribe('usersByIds', Meteor.user().profile.friends, function() {
//... make users list panel using minimal fields
});
//performed when click on a user
function userLoadInfo(userId) {
Meteor.subscribe('userById', userId, function() {
var userProfile = Meteor.users.findOne(userId).profile;
//...
//make template user panel using full or minimal user fields
//...
//BUT NOT WORK!
//HERE Meteor.users.findOne(userId) keep minial user fields!!
//then if userId is my friend!
});
}
});
SERVER
//return minimal user fields
getUsersByIds = function(usersIds) {
return Meteor.users.find({_id: {$in: usersIds} },
{
fields: {
'profile.username':1,
'profile.avatar_url':1
}
});
};
//return all user fields
getFriendById = function(userId) {
return Meteor.users.find({_id: userId},
{
fields: {
'profile.username':1,
'profile.avatar_url':1
//ADDITIONAL FIELDS
'profile.online':1,
'profile.favorites':1,
'profile.friends':1
}
});
};
//Publish all users, with minimal fields
Meteor.publish('usersByIds', function(userId) {
if(!this.userId) return null;
return getUsersByIds( [userId] );
});
//Publish user, IF IS FRIEND full fields
Meteor.publish('userById', function(userId) {
if(!this.userId) return null;
var userCur = getFriendById(userId),
userProfile = userCur.fetch()[0].profile;
if(userProfile.friends.indexOf(this.userId) != -1) //I'm in his friends list
{
console.log('userdById IS FRIEND');
return userCur; //all fields
}
else
return getUsersByIds( [userId] ); //minimal fields
});
This is a limitation or bug in DDP. See this.
A workaround is to move data out of users.profile.
Like this:
//limited publish
Meteor.publish( 'basicData', function( reqId ){
if ( this.userId ) {
return Meteor.users.find({_id: reqId },{
fields: { 'profile.username':1,'profile.avatar_url':1}
});
}
else {
this.ready();
}
});
//friend Publish
Meteor.publish( 'friendData', function( reqId ){
if ( this.userId ) {
return Meteor.users.find( {_id: reqId, 'friendProfile.friends': this.userId }, {
fields: {
'friendProfile.online':1,
'friendProfile.favorites':1,
'friendProfile.friends':1
}
});
}
else {
this.ready();
}
});
//example user
var someUser = {
_id: "abcd",
profile: {
username: "abcd",
avatar_url: "http://pic.jpg"
},
friendProfile: {
friends: ['bcde', 'cdef' ],
online: true,
favorites: ['stuff', 'otherStuff' ]
}
}
As given in a comment, this link reveals your problem. The current DDP Protocol does not allow publishing of subdocuments. One way to get around this is to create a separate collection with your data but a better way would probably to just remove some of the data and make it a direct object off of your user.
The best way to do this is add the data to your user's profile upon insert and then in the onCreateUser move the data onto the user directly:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
if (options.profile.publicData) {
user.publicData = options.profile.publicData;
delete options.profile.publicData;
}
user.profile = options.profile;
}
return user;
});
If you are allowing clients to perform user inserts make sure you validate the data better though. This way you can have the online, favorites, and friends in the profile and publish that specifically when you want it. You can then have username and avatar_url in the publicData object directly on the user and just always publish all-the-time.

Categories