Local-storage backbone models - backbone.js

I am trying to use, https://github.com/jeromegn/Backbone.localStorage, to store models. I ve got a JSON and I am fetch data with backbone and I am trying to local store the fetched data. The first variable of json file is a time key called tstamp. How can I store and retrieve data based on tstamp attribute?? My code:
// Backbone model Creation for highlight
var HighlightModel = Backbone.Model.extend({
defaults: {
tstamp: "1234",
att: "",
},
initialize: function () {
}
});
//Backbone model initialization
highlight = new HighlightModel();
var HighlightList = Backbone.Collection.extend({
model: HighlightModel,
localStorage: new Backbone.LocalStorage("highlightList"),
url: 'data.json'
});
var HighlightView = Backbone.View.extend({
el: "#highlights",
template: _.template($('#highlightTemplate').html()),
render: function (eventName) {
_.each(this.model.models, function (highlight) {
var highlightTemplate = this.template(highlight.toJSON());
//push data to obj for highlight script
mp = highlight.toJSON();
// Add data to DOM element
$(this.el).html(highlightTemplate);
}, this);
return this; // .remove(); to stop displaying
}
});
var highlights = new HighlightList([highlight]);
var highlightsView = new HighlightView({
model: highlights
});
// Fetching data from server every n seconds
setInterval(function () {
highlights.fetch({
reset: true
});
highlight.add(sentiments);
highlights.save();
}, htCycle); // Time in milliseconds
highlights.bind('reset', function () {
highlightsView.render();
console.log('render');
});
EDIT:
I change my code a little bit, now i have at local storage just store the default model {"tstamp":"1234","att":"","id":"4fb1b437-0e37-8eb7-ed3c-cbd9d0dcff98"}. I want to store the fetched data from server to localstorage.

Related

Getting view to update on save using Backbone.js

I am learning Backbone.js and as a trial project I am creating a little WordPress user management application. So far my code shows a listing of all WordPress users and it has a form which enables you to add new users to the application.
This all works fine however when you add a new user the listing of users doesn't update automatically, you need to refresh the page to see the new user added which isn't ideal and defeats one of the benefits of Backbone.js!
I have a model for a user and then a collection which compiles all the users. I have a view which outputs the users into a ul and I have a view which renders the form. How do I make my code work so when the .save method is called the view which contains the users updates with the new user? Or is there another way to approach this?
//define the model which sets the defaults for each user
var UserModel = Backbone.Model.extend({
defaults: {
"username": "",
"first_name": "",
"last_name": "",
"email": "",
"password": "",
},
initialize: function(){
},
urlRoot: 'http://localhost/development/wp-json/wp/v2/users'
});
//define the base URL for ajax calls
var baseURL = 'http://localhost/development/wp-json/wp/v2/';
//function to define username and password
function authenticationDetails(){
var user = "myUserName";
var pass = "myPassword";
var token = btoa(user+':'+pass);
return 'Basic ' + token;
}
//add basic authorisation header to all API requests
Backbone.$.ajaxSetup({
headers: {'Authorization':authenticationDetails()}
});
//create a collection which returns the data
var UsersCollection = Backbone.Collection.extend(
{
model: UserModel,
// Url to request when fetch() is called
url: baseURL + 'users?context=edit',
parse: function(response) {
return response;
},
initialize: function(){
}
});
// Define the View
UserView = Backbone.View.extend({
model: UserModel,
initialize: function() {
// create a collection
this.collection = new UsersCollection;
// Fetch the collection and call render() method
var that = this;
this.collection.fetch({
success: function () {
that.render();
}
});
},
// Use an external template
template: _.template($('#UserTemplate').html()),
render: function() {
// Fill the html with the template and the collection
$(this.el).html(this.template({ users: this.collection.toJSON() }));
return this;
},
});
var userListing = new UserView({
// define the el where the view will render
el: $('#user-listing')
});
NewUserFormView = Backbone.View.extend({
initialize: function() {
this.render();
},
// Use an external template
template: _.template($('#NewUserTemplate').html()),
render: function() {
// Fill the html with the template and the collection
$(this.el).html(this.template());
return this;
},
events: {
'click .create-user':'addNewUser'
},
addNewUser: function(){
var newFirstName = $('.first-name').val();
var newLastName = $('.last-name').val();
var newEmail = $('.email').val();
var newPassword = $('.password').val();
var newUserName = newFirstName.toLowerCase();
var myNewUser = new UserModel({username:newUserName,first_name:newFirstName,last_name:newLastName,email:newEmail,password:newPassword});
console.log(myNewUser);
myNewUser.save({}, {
success: function (model, respose, options) {
console.log("The model has been saved to the server");
},
error: function (model, xhr, options) {
console.log("Something went wrong while saving the model");
}
});
}
});
var userForm = new NewUserFormView({
// define the el where the view will render
el: $('#new-user-form')
});
All backbone objects (models, collections, views) throw events, some of which would be relevant to what you want. Models throw change events when their .set methods are used, and Collections throw add or update events... a complete list is here.
Once you know which events are already being thrown, you can listen to them and react. For example, use listenTo - in your view's initialize, you can add:
this.listenTo(this.collection, 'add', this.render);
That will cause your view to rerender whenever a model is added to your collection. You can also cause models, collections, whatever, to throw custom events using trigger from anywhere in the code.
EDIT: For the specific case of getting your user listing view to rerender when a new user is added using the form, here are the steps you can take... In the initialize method of your UserView, after the initialize the collection, add:
this.listenTo(this.collection, 'add', this.render);
Then in your form view... assuming you want to wait until the save is complete on your server, in the addNewUser method, in the success callback of your save, add:
userlisting.collection.add(model);
This will work, since the instance of your UserView is in the global scope. Hope this one works for you!

Passing values from collection to view

Hi here is a backbonejs / Parse.com code that tries to get list of firstNames from a class in Parse. The goal is to have the view "pluck" function correctly access data pulled from database.
Here is the model:
var Subscribers = Parse.Object.extend({
className: "Subscribers"
});
Here is the collection that correctly does its job when I hardcode objects instances. Of course I don't want to have the firstNames hardcoded here but pulled from Parse.com backend. How should I replace this code to have the data correctly pulled from server ? Should I use a fetch ? I tried but unsucessfully.
var DoopizCollection = Parse.Collection.extend({
model: Subscribers
}
);
//var doopizlist = new DoopizCollection([
// {firstName: "bob"}, //hardcoded instances : this works.
// {firstName : "luke"} ]);
var doopizlist = new DoopizCollection();
doopizlist .fetch({
success: function(doopizlist ) {
collection.each(function(object) {
console.warn(object);
});
},
error: function(doopizlist , error) {
console.log("error")
}
});
And here is the view:
var DoopizView = Parse.View.extend({
el: '#container',
render: function() {
var html = '';
html = this.collection.pluck('firstName');
$(this.el).html(html);
}
});
var doopizView = new DoopizView({
collection: doopizlist
});
doopizView.render();

Backbone Router -- load data

EDIT: Got it working, but it seems wrong.
I ending up adding a listener to the sync event on the main app view, then render a player. I also added a global variable PgaPlayersApp.CurrentPlayer.
Am I going about this the wrong way? What is the correct way to do this? Is there a reason I can't use reset: true and then listen for the reset event? (It doesn't work)
ROUTER:
// js/router.js
var PgaPlayersApp = PgaPlayersApp || {};
var Router = Backbone.Router.extend({
routes:{
'player/:id': 'loadPlayer'
},
loadPlayer: function(id)
{
PgaPlayersApp.CurrentPlayer.set('id', id);
PgaPlayersApp.CurrentPlayer.fetch();
}
});
PgaPlayersApp.Router = new Router();
Backbone.history.start();
VIEW:
//js/views/app.js
var PgaPlayersApp = PgaPlayersApp || {};
PgaPlayersApp.AppView = Backbone.View.extend({
el: '#pga_players_profile_app',
initialize: function()
{
this.listenTo(PgaPlayersApp.Players, 'reset', this.addAll);
this.listenTo(PgaPlayersApp.CurrentPlayer, 'sync', this.loadPlayer);
PgaPlayersApp.Players.fetch({reset: true});
},
...
loadPlayer: function()
{
new PgaPlayersApp.PlayerCardView({ model: PgaPlayersApp.CurrentPlayer }).render();
}
});
Backbone.js is a library that doesn't really enforce how you'd like to structure your App (Or the relationship between your Controller, Model, Router, etc.)
Below is one of the many ways to do it.
Couple highlights:
Router kicks off the fetch process.
When model has been fetched, Router then asks the View to render data (Instead of having the View listening to change events from the Model.)
This assumes that PlayerCardView is a "read only" view, as the View doesn't listen to change events from the Model. Depending on your use case, this might not be desirable, so it ultimately depends on how you'd like to handle it.
Here are some sample code:
(function (export) {
var App = export.App = {};
// Stores state/current views of the App
App.state = {};
App.state.currentPlayer = null;
// Model containing the player
App.PlayerModel = Backbone.Model.extend({});
// Single Player View (Assuming you have a collection view for list of players)
App.PlayerCardView = Backbone.View.extend({
model: App.PlayerModel,
template: _.template('<%= id %>'),
render: function(parentEl) {
// Render player
this.$el.html(this.template(this.model.toJSON()));
// Append player view to parent container
if (parentEl) {
parentEl.append(this.$el);
}
return this;
}
// Don't forget to clean up as well!
});
// Router
App.Router = Backbone.Router.extend({
routes: {
'player/:id': 'showPlayer'
},
showPlayer: function(id) {
// Unload current player view, if necessary
// Construct model
var player = App.state.currentPlayer = new App.Player({
id: id
});
// Pass model to the new player view
var view = App.state.currentPlayerView = new App.PlayerCardView({
model: App.state.currentPlayer
});
// At this time, you should probably show some loading indicator as you're fetching data from the server
// Fetch data
player.fetch({
success: function() {
// This would be called when data has been fetched from the server.
// Render player on screen
view.render($('#parentContainerId'));
}
});
}
});
// Initializes the App
App.init = function() {
// Kick off router
App.state.router = new App.Router();
export.Backbone.history.start();
};
})(window);
// Start the app!
window.App.init();
Gist: https://gist.github.com/dashk/5770073

Connection between model and collection in backbone and parse.com

i'm trying to connect model and collection using parse.com but i'm confused. I'm tring to fetch by collection using backbone and javascript api parse.com but compare this error:POST https://api.parse.com/1/classes 404 (Not Found).
Model:
var Person = Backbone.Model.extend({
defaults:{
},
initialize:function(){
console.log("inperson");
this.validate();
this.send();
},
validate:function(){
console.log("validate");
},
send:function(){
var user = new Parse.User();
user.set("username", this.get("username"));
user.set("password", this.get("password"));
user.set("email", this.get("email"));
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
}
});
return Person;
});
Collection:
var Usercollection = Parse.Collection.extend({
model:Person,
initialize:function(){
}
});
return Usercollection;
});
and finally the view that call the colletion and fetch:
var HomeView = Backbone.View.extend({
template: Handlebars.compile(template),
events: {
},
initialize: function() {
console.log("inhomeview");
var amici = new Usercollection();
amici.fetch({
success: function(collection) {
amici.each(function(object) {
console.warn(object);
});
},
error: function(amici, error) {
// The collection could not be retrieved.
}
});
},
render: function() {
}
});
return HomeView;
});
Cant you just swap the backbone collection and model to Parse's ones? (You only used the Parse type of the collection, not the model!)
Try switch that Backbone model to a Parse.Object .
Step by step below:
First of all Lets create a new app on Parse.com, mine is called FunkyAppartments.
Insert the script tag for loading Parse javascript lib into index.html or whathever:
<script src="http://www.parsecdn.com/js/parse-1.5.0.min.js"></script>
Switch the backbone model and collection to use parse types instead (and rename the fetch method if you have extended backbones, since we do not want to overide the one of parse):
//var Appartment = Backbone.Model.extend(); Backbone wo. Parse.com
var Appartment = Parse.Object.extend("Appartment");
//var Appartments = Backbone.Collection.extend({ Backbone wo. Parse.com
var Appartments = Parse.Collection.extend({
model: Appartment,
loadAppartments: function(callback){
debugger;
this.query = new Parse.Query(Appartment);
this.fetch();
}
});
I added a debugger tag in the load appartments so that developer tools breaks in the middle of the controller, here I have access to the Appartment private type of the controller, hence i can store some data on the parse server and verify by pasting the below in the developer tools console.
var testAppartment = new Appartment();
testAppartment.save({name: "foobars"}).then(function(object) {
alert("yay! it worked");
});
Yei, the data shows up in the parse.com UI for the app we just added there. And more importantly it shows up in our frontend. That was easy!
UPDATE: PROBLEMS W BACKBONE 1.2.1, MARIONETTE 2.4.2, UNDERSCORE 1.8.3
I noticed that I actually had been using old versions of marionette, backbone and underscore.js. An initial update appeared to break the application.
After some research i found that it was the parse part that did not return objects that would successfully render. Hence I changed the collection type back to an extension of: Backbone.collection instead of Parse.collection.
I also had to override the query method, since the objects would not save on the correct id, updating an object resulted in a new object being added instead of an old one being updated.
var Apartment = Parse.Object.extend('Appartment');
var Apartments = Backbone.Collection.extend({
model: Apartment,
query: new Parse.Query(Apartment),
initialize: function(){
MyApp.vent.on('search:param', function(param){self.search(param); });
var self = this;
this.query.find({
success: function(results){
self.reset();
results.forEach(function(result){
result.attributes.id__ = result.id
var ap = new Apartment(result.attributes);
self.add(ap);
});
}
});
}
});
I added an attribute: id__ to hold the parse id (naming it just id did not work since it backbone interfered with it, making it disappear).
Finally in saving the model to parse i utilized id__ as id in the save call:
var ApartmentEditView = Backbone.Marionette.ItemView.extend({
template: "#apartment-edit-template",
className: "apartmentDetail",
events: {
"click .store": "storeEdit",
"click .close": "closeEdit"
},
storeEdit: function(){
var priceNum = Number($('#price_field').val().replace(/\s/g, ''));
this.model.set({
id: this.model.attributes.id__,
name:$('#name_field').val(),
price:priceNum,
description:$('#desc_field').val(),
url:$('#url_field').val()
});
this.model.save();
this.closeEdit();
},
closeEdit: function(){
var detailView = new ApartmentDetailView({model: this.model});
MyApp.Subletting.layout.details.show(detailView);
}
});
Now the object is updated correctly in the database.

Update backbone model on change of global variable?

I have a Collection of Venue objects which all have their own lat/long properties, using this and the users position I can calculate the distance between user and each venue.
My issue is that I can't only do this when the Venue objects are created so need to trigger this calculation when the position variable is updated, either by watching the position variable or by triggering a function, I'm not having much success with either method.
window.App = {};
// Venue Object
App.Venue = Backbone.Model.extend({
urlRoot: '/rest/venue',
defaults: {
distance: ''
},
initialize: function(){
console.log(App.position);
this.set('distance', getDistance(App.position.coords.latitude, App.position.coords.longitude, this.get('latitude'), this.get('longitude')));
},
events: {
// Doesn't seem to work
App.position.on('change', function() { console.log('change event'); })
},
updateDistance: function() {
console.log('updateDistance');
}
});
// Venues Collection Object
App.Venues = Backbone.Collection.extend({
url: '/rest/venues',
model: App.Venue,
comparator: function(venue) {
return venue.get('name');
}
});
$(document).ready(function(){
// Setup Model
App.venues = new App.Venues();
App.venues.fetch();
navigator.geolocation.watchPosition(gotPosition);
function gotPosition(position) {
console.log(position);
App.position = position;
// Somehow trigger updateDistance function on all model objects?
}
});
What is the correct approach here?
There are two ways of dealing with this.
Position is a Backbone.Model
If your position is a backbone model as opposed to a simple variable then you could do something like:
// Give the position to each venue
App.venues = new App.Venues({position: position}); //doesn't matter if the position variable is just empty right now.
in your App.Venue model initialize method:
App.Venue = Backbone.Model.extend({
...
initialize: function(options) {
this.position = options.position //save the reference
this.listenTo(this.position, "change", positionChanged) //now your venue model is watching this position object. any change and positionChanged method will be called
},
positionChanged: function (model) {
// position updated
}
Global Event Aggregator
So incase for some reason you don't have position as Backbone model, then you could setup your own event aggregator by extending Backbone.Events module:
App.vent = _.extend({}, Backbone.Events);
Whenever position is updated, you trigger an event:
function gotPosition(position) {
console.log(position);
App.position = position;
App.vent.trigger("position:updated") // you could name this event anything.
}
In your Venue model you listen to the events:
App.Venue = Backbone.Model.extend({
...
initialize: function(options) {
App.vent.on("position:updated", this.positionChanged)
},
I would prefer the first method !

Resources