Backbone JS model fetch not updating attributes automatically - backbone.js

I'm creating a model to fetch user profile data and then use it but the problem is every time it fetch for data (success response text JSON is available) the model doesn't updated. So I have to set it up manually. This is my code:
UserTest = Backbone.Model.extend({
defaults: {
userid: null,
full_name: null,
photo: null,
sec_access: null
},
urlRoot: 'data/userprofile.php',
parse: function (response) {
if (response.photo == null || response.photo == '') response.photo ='images/default-usericon.png';
}
});
var usertest = new UserTest;
usertest.fetch().then(function(user){
usertest.set('userid',user.userid);
usertest.set('full_name',user.full_name);
usertest.set('photo',user.photo);
usertest.set('sec_access',user.sec_access);
console.log(usertest.get('userid'));
});
is there a better way? or am I doing it wrong?

You need to return the data from parse:
parse: function (response) {
if (response.photo == null || response.photo == ''){
response.photo ='images/default-usericon.png';
}
return response; // This is very important
}

Related

Transfer Items to and from users Using AngularJS and Firebase

Ok, so I'm working with AngularJS and Firebase and trying to create a simple exchange between two users. Right now my data structure is set up under "users/uId/" and then their email, date they joined, and gold.
Under gold (users/uId/gold) I have "sent" which captures the amount, time and to whom (email). This is the code snippet below. It also updates their total gold.
Now I'm stuck updating the person they're sending the gold to. I capture the email address, but everything under scope relates to the current logged in user. How would I update the new users users/uId/gold/received with the amount, time and email who it was from, along with updating their total gold?
I feel like I might be going about this the wrong way, any help would be appreciated, thanks!
ledger.controller('TransferController', function (
$scope, $firebase, $routeParams, $location, $rootScope, FIREBASE_URL) {
$scope.whichuser = $routeParams.uId;
$scope.goldsends = goldsendList;
var ref = new Firebase(FIREBASE_URL + '/users/' + $scope.whichuser + '/gold/' + '/sent/');
var hopperRef = new Firebase(FIREBASE_URL + '/users/' + $scope.whichuser + '/gold/');
var usersRef = ref.child("users");
var goldsendList = $firebase(ref).$asArray();
$scope.sendGold = function () {
var sendgoldObj = $firebase(ref); //this var has to match the sendgoldObj.$push var down below, and that's it
var myData = {
amount: $scope.user.amount,
email: $scope.user.email,
date: Firebase.ServerValue.TIMESTAMP
};
sendgoldObj.$push(myData).then(function () {
// $location.path('/myledger/'); //page redirect
}); //data sent to firebase.
if ($scope.currentUser.gold.total - Math.abs($scope.user.amount) > 0) { //
var hopperRefff = hopperRef.child("gold");
hopperRef.update({
"total": $scope.currentUser.gold.total - $scope.user.amount
}); //update total gold
var receive = new Firebase(FIREBASE_URL);
ref.child('users').orderByChild('email').equalTo(emailAddress).once('value', function (snap) {
console.log(snap.name() + (snap.val() === null ? ' DOES NOT' : ' does') + ' exist');
}); //trying to find user to send gold to
} //if user has enough gold statement
else {
return {
scope: {
errormessage: 'You don\'t have enough money',
}
};
console.log("not enough money!");
} //else note enough gold statement
} //sendgold
}); //TransferController
You could store the users by email where the # is replaced by _ and a . is replaced with -
So you have a JSON structure like this in Firebase
users: {
"bob_hoskins-com": {
email: "bob#hoskins.com",
date: "09-09-1999",
gold: {
...
}
}
}
However, I don't think this is a great approach for this problem.
I would create a node service that is observing a requests Firebase location on each user for added children. The node service will then do the calculations and write the data to the correct paths, then can delete the request once processed.
So you would have rules on your Firebase like this
{
"rules": {
"$userId": {
"requests": {
".read": "auth != null && $userId == auth.id",
".write": "auth != null && $userId == auth.id"
},
"responses": {
".read": "auth != null && $userId == auth.id",
".write": "auth != null && $userId == auth.id"
}
}
}
Here is some request code
var Firebase = require('firebase');
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
function guid() {
return s4() + s4() + s4() + s4();
}
var _ref = new Firebase('https://YOUR_FIREBASE.firebaseio.com/');
//Log me in
var guid = guid();
var FirebaseTokenGenerator = require("firebase-token-generator");
var tokenGenerator = new FirebaseTokenGenerator("YOUR_TOKEN");
var TOKEN = tokenGenerator.createToken({uid: guid, user: "node server"},{admin: true});
_ref.authWithCustomToken(TOKEN, function (error) {
if(error) {
console.log("Login Failed!", error);
} else {
console.log("Login Succeeded!", guid);
}
});
_ref.on('child_added', function (user) {
var requests = user.ref().child('requests');
requests.on('child_added', function(req) {
handleRequest(req);
});
});
var handleRequest = function (request) {
// Process the request
// Write stuff back to Firebase
// Delete the request
request.ref().remove();
};
Setting a value to the user is just a variation of checking if that user exists. Once you have a snapshot, you can get back to a ref by calling ref
ref.child('users').orderByChild('email').equalTo(emailAddress).once('value', function (snap) {
snap.ref().update({ "total": snap.val().total + amount });
});
Not that this is just a sample, so you'll probably have to update it for your actual data structure.
Update
The above will get you the value of the users node.
You either need to capture the once('child_added' or forEach over the on('value'. I'll give an example of both.
Listening to child_added:
ref.child('users').orderByChild('email').equalTo(emailAddress).once('child_added', function (snap) {
snap.ref().update({ "total": snap.val().total + amount });
});
An example of looping over the value:
ref.child('users').orderByChild('email').equalTo(emailAddress).once('value', function (snap) {
snap.forEach(function(childsnap) {
childsnap.ref().update({ "total": snap.val().total + amount });
});
Here's a jsbin with both samples: http://jsbin.com/fenavu/1/edit?js,console. Note that the code here writes out the ref.toString(), which gives you the full URL of the node (since every piece of data in Firebase has its own unique URL). That can be a handy way to figure out what URL your node maps to.

How to write structured data with JSON writer?

How can I include hasOne associated model data in the JSON POST?
Structured data is required by my web API in the form of:
{
id: 1234,
name: 'Aaron Smith',
address: {
address1: '1925 Isaac Newton Sq',
address2: 'Suite 300',
city: 'Reston',
state: 'VA',
zip: 20190
}
}
#nonino
I think I know how to do it but I am also having a similar problem. I can't actually get my associations to give me the associated data. Anyway from what I have scrounged on the internet make a custom writer like this or just in the default writers getRecordData: function(record,operation)
Here is my custom writer
Ext.define('Wakanda.writer', {
extend: 'Ext.data.writer.Json',
// alternateClassName: 'SimplyFundraising.data.WakandaWriter',
alias: 'writer.wakanda',
writeAllFields: false,
getRecordData: function(record,operation) {
debugger;
Ext.apply(record.data,record.getAssociatedData());
debugger;
var isPhantom = record.phantom === true,
writeAll = this.writeAllFields || isPhantom,
nameProperty = this.nameProperty,
fields = record.fields,
data = {},
changes,
name,
field,
key;
if (writeAll) {
// console.log("getRecordData1", this, arguments);
fields.each(function(field){
if (field.persist) {
debugger;
name = field[nameProperty] || field.name;
data[name] = record.get(field.name);
} else {
}
});
} else {
changes = record.getChanges();
debugger;
// console.log("getRecordData2", this, arguments, changes);
for (key in changes) {
if (changes.hasOwnProperty(key)) {
field = fields.get(key);
name = field[nameProperty] || field.name;
data[name] = changes[key];
}
}
if (!isPhantom) {
debugger;
data[record.idProperty] = record.getId();
if(operation.action !== 'destroy'){
data[record.stampProperty] = record.get(record.stampProperty);
}
}
}
return {'__ENTITIES': [data]};
}
});
The key I think is in the getRecordData where I have a statement Ext.apply(record.data,record.getAssociatedData()); If record.getAssociatedData does indeed return your data then the Ext.apply statement will merge your current record.data with your record.getAssociatedData into 1 json file. At least this is what I hope happens. Can't test until I get my associations setup correctly.
Hope this helps,
Dan
getRecordData: function(record,operation) {
debugger;
Ext.apply(record.data,record.getAssociatedData());
debugger;

how can I update a model with custom idAttribute

in my simple backbone application, I am trying to update a model and every time it send a put request instead of post.
Well, this is my model named categoryModel
define(['Backbone'], function (Backbone) {
var CategoryModel = Backbone.Model.extend({
defaults: {
ID: '',
Name: 'Empty',
TagID: '0',
GID: '0'
},
idAttribute: "ID",
initialize: function () {
if (!this.get('Name')) {
this.set({ 'Name': this.defaults.Name });
}
}
});
return CategoryModel;
});
this is the collection
define(['Backbone','../../models/categories/categoryModel'], function (Backbone, categoryModel) {
var CategoryCollection = Backbone.Collection.extend({
url: '/parentcategory/Actions',
model: categoryModel
});
return new CategoryCollection;
});
here are my methods in the view
on a keychange event
createNewItem: function (e) {
var $this = $(e.currentTarget);
$('#selectedCategoryName').html($this.val());
//it creates a new model
globals.NewCategory = new CategoryModel({ Name: $this.val() });
}
on handleDrop event
handleDropEvent: function (event, ui) {
var draggable = ui.draggable;
//check if name has set
if (!globals.NewCategory) {
alert("Please write a category name");
$('#createNewCategory').focus();
return;
}
//get itemID
var itemID = draggable.attr("id").split('_')[1];
var itemDesc = draggable.attr("id").split('_')[0];
//check items category
if (itemDesc == "Tag") {
//check if tagID already exists
if (globals.NewCategory.TagID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("TagID", itemID);
} else if (itemDesc == "gTag") {
if (globals.NewCategory.GID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("GID", itemID);
}
categoriesCollection.create(globals.NewCategory, {
silent: true,
wait: true,
success: function (model, response) {
model.set("ID", response);
alert(model.id);
}
});
}
The categoriesCollection.create is called twice. Firstly for setting the TagID (on a success request it gets an ID ) and secondly for setting the GID.
Since the ID has been set, shouldn't had sent a POST request instead of PUT on the second call?
What am I doing wrong?
Thanks
The standard behaviour is to send a POST if the model is new ( doesn't have an ID attributed ) and send a PUT if the model id is set.
In your case it's working as designed, if you want it to use POST to send UPDATES you have to override Backbone.sync to work as you need, but I think it's easier for you to make your backend RESTful and create a PUT listener controller method for updates.
Another thing, if I got it right you are using create() to update models in your collection, I would advise you not to do that and instead use the save() directly in the model you want to update, the code will be a lot more readable.
Cheers.

Backbone.js: Collection.Get(id) -> Ignore case

I have the following model and collection:
var UserModel = Backbone.Model.extend({
url: 'api/user',
idAttribute:'username',
defaults: {
username:'',
password:'',
email:'',
tags:''
}
});
var UserCollection= Backbone.Collection.extend({
url: 'api/user',
model: UserModel
});
When I retrieve a user from the collection using:
var myUser = collection.get(username);
the username has to be in the correct case, otherwise I just get null as a result.
Is there a way to tell backbone to ignore the case for certain operations like this on?
Sure, you just need to change the relevant code. It is on lines 240-242 of backbone.js (for documented 0.9.2 version):
get: function(attr) {
return this.attributes[attr];
},
Change it something like to:
get: function(attr) {
// will skip if null or undefined -- http://stackoverflow.com/questions/5113374/javascript-check-if-variable-exists-which-method-is-better
if (this.attributes[attr] != null) {
return this.attributes[attr];
}
// and then try to return for capitalized version -- http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
else {
return this.attributes[attr.charAt(0).toUpperCase() + attr.slice(1)];
}
},
for collection changing
get: function(id) {
if (id == null) return void 0;
return this._byId[id.id != null ? id.id : id];
},
to something like this might work:
get: function(id) {
if (id == null) return void 0;
var firstCase = this._byId[id.id != null ? id.id : id];
if (firstCase != null) {
return firstCase;
}
else {
return this._byId[capitalize(id.id) != null ? capitalize(id.id) : capitalize(id)];
}
},

How to show data stored in local storage in my table

I am creating a contact Manager using backbone.js,this is my code
$(document).ready(function() {
var Contact=Backbone.Model.extend({
defaults: {
fname : '',
lname : '',
phoneno : ''
}
});
var ContactList=Backbone.Collection.extend({
model : Contact,
localStorage: new Store("ContactList-backbone")
});
var ContactView=Backbone.View.extend({
el : $('div#contactmanager'),
events: {
'click #additems' : 'add'
},
initialize: function() {
this.render();
this.collection = new ContactList();
},
add : function() {
s1=$('#fname').val();
s2=$('#lname').val();
s3=$('#phoneno').val();
if(s1 =="" || s2=="" || s3=="")
{
alert("Enter values in Textfield");
}
else
{
$('#tlist').append("<tr><td>"+s1+"</td><td>"+s2+"</td><td>"+s3+"</td> </tr>");
cont=new Contact({fname:s1,lname:s2,phoneno:s3});
this.collection.add(cont);
cont.save();
}
},
render : function() {
$(this.el).append("<label><b>First Name</b></label><input id= 'fname' type='text' placeholder='Write ur first name'></input>");
$(this.el).append("<br><label><b>Last Name</b></label><input id= 'lname' type='text' placeholder='Write ur last name'></input>");
$(this.el).append("<br><label><b>Phone Number</b></label><input id= 'phoneno' type='text' placeholder='Write ur phone number'></input>");
$(this.el).append("<br><button id='additems'>ADD</button>");
var showdata=localStorage.getItem('ContactList-backbone',this.model);
console.log(showdata,"showdata");
}
return this;
},
});
var contactManager=new ContactView();
});
This is how I used localstorage
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
function guid() {
return (S4());
};
var Store = function(name)
{
this.name = name;
var store = localStorage.getItem(this.name);
this.data = (store && JSON.parse(store)) || {};
};
_.extend(Store.prototype,
{
save: function() {
localStorage.setItem(this.name, JSON.stringify(this.data));
},
create: function(model) {
if (!model.id) model.id = model.attributes.id = guid();
this.data[model.id] = model;
this.save();
return model;
},
Backbone.sync = function(method, model, options) {
var resp;
var store = model.localStorage || model.collection.localStorage;
switch (method) {
case "create": resp = store.create(model); break;
//I am using only create
}
if (resp) {
options.success(resp);
}
else {
options.error("Record not found");
}
};
The data is getting stored in local storage.
But I can't figure out how to show this data in my table when the page is reloded.
For eg: Iwant to show first name,lname and phone no in table ;
I am new to backbone so plz do help me
Basically you will want to bind the add event in your collection which gets will get called for each item that is being added to the collection and then in the function your binding it to add the code to add the rows to your table. Also you will want to remove the code that is in your current add method that adds the row since it will now be generated when the item gets added to your collection.
Using your code as a base something along the lines of
var ContactView=Backbone.View.extend({
el : $('div#contactmanager'),
events: {
'click #additems' : 'add'
},
initialize: function() {
this.render();
this.collection = new ContactList();
this.collection.bind('add', this.addContact, this);
},
addContact: function(contact) {
//this will get called when reading from local storage as well as when you just add a
//model to the collection
$('#table').append($('<tr><td>' + contect.get('name') + </td></tr>'));
}
Another point being that you have already have underscore.js on your page (since its a requirement for backbone.js) you may want to consider moving your code to generate html to a underscore.js template.
http://documentcloud.github.com/underscore/#template
since you're only using create, you're never going to hit read. Replace your switch statement with by adding a read method
switch (method)
{
case "read":
resp = model.id != undefined ? store.find(model) : store.findAll();
break;
case "create":
resp = store.create(model);
break;
}

Resources