Backbone persistant view with persistant data - javascript

I have a navigation bar that is pretty much the only piece of persistant template in my web application (in that is always there regardless of route).
In that navbar I show a Login link if the user is not logged in, and if they are I show their name, the problem I having is that I can't get the nav to keep it's state. The uses logins,
runLogin: function(e) {
e.preventDefault();
var element = $(e.currentTarget),
self = this;
$.when($.ajax({
url: 'http://app-api.dev/api/oauth/access_token?grant_type=password&client_id=1&client_secret=xyz',
method: 'POST',
data: element.serialize(),
dataType: 'json',
success: function(data) {
// Set the cookie, this will need to be sent in every singal request going foward.
$.cookie('access_token', data.access_token);
// So we have the access token we use this to populate our user details
},
error: function(response) {
// We can build a universal error view that would be quite and pretty easy to do.
}
})).done(function() {
$.ajax({
url: 'http://app-api.dev/api/users/me',
dataType: 'json',
method: 'GET',
success:function(user) {
self.model.set(user);
self.launchDashboard();
}
});
})
},
launchDashboard: function() {
localStorage.setItem('user', '');
localStorage.setItem('user', JSON.stringify(this.me));
App.Routes.Application.navigate('dashboard', { trigger: true } );
}
Basically I login, request my details from the API, and use those to set a model, the model I am setting has already been passed in to the navigation view, and has a change listener on it.
showUserDetails: function() {
this.loggedInUserMenu = new App.Views.navigationViewLoggedIn({
model : this.model
});
},
the above fires this,
App.Views.navigationViewLoggedIn = Backbone.View.extend({
el: '.navbar-right',
template: _.template( $('#tpl-navigation-logged-in').html() ),
events: {
},
initialize: function() {
this.render();
},
render: function() {
this.$('.li-login').replaceWith( this.template({
user: this.model.toJSON()
}));
}
});
Router
initialize: function() {
Pops.Models.AuthUser = new Pops.Models.User;
this.navigation_bar = new Pops.Views.navigationView({
model: Pops.Models.AuthUser
});
},
On successful login the loggedInNavigation is visisble, and I am at app-client.dev/#dashboard however if I navigate to `app-client.dev/#groups I lose the loggedInNavigation and it reverts back to just saying "login".
Why?
=== Edit I ahve attached the method that creates the main nav it is done on the initialize of the routes. The method that does the view update is all the in the previous code, login sets the model that is int he navigation view, listening for a change ===

Related

How do I update a javascript variable from controller?

I'm getting the model from the view with this:
<script>
Module.getModel(#Html.Raw(Json.Encode(Model)))
</script>
Which calls this:
var getModel = function(model) {
vModel= model;
};
This works to initially get the model, but I need to update one of the model values via dialog ajax.
var loc = $("#Location").val();
$.ajax({
url: $("#root").attr("href") + "Home/SetLocation",
context: $(this),
type: 'POST',
data: { location: loc },
success: function() {
$(this).dialog("close");
reloadGrid();
},
error: function() {
alert("There was a problem updating your Location.");
}
});
The controller action saves the location to profile and redirects to "Index" action:
public ActionResult SetLocation(string location)
{
_dashboardTasks.SaveLocationToProfile(location);
return RedirectToAction("Index");
}
Which updates the model with the new profile information and returns it to the view:
public ActionResult Index(HomeViewModel homeViewModel)
{
SetUserLocation(homeViewModel);
return View(homeViewModel);
}
At this point the breakpoint on the first code block is hit again, but the function for it, getModel, is never called and the javascript model is never updated. So the success function which calls for the grid to reload, reuses the old data instead of the new data.
How can I update vModel with the value changed in the controller?
Do I have to add a declaration to the success function explicitly redefining the value?
success: function() {
$(this).dialog("close");
vModel.Location = loc;
reloadGrid();
},
This works, but I don't understand why the model is not being updated with the current code.

using backbone with third party api

I'm trying to use backbone to grab hold of an instagram feed. This doesn't require authenticating the user, it is pulling a public feed available through:
https://api.instagram.com/v1/users/<user_id>/media/recent/?client_id=<client_id>
I've gotten as far as outputting the JSON response into the console, but I'm unable to make it display on my page.
In the code below, I use fetchData to grab the feed, and I'd like to eventually get it to a point where render outputs everything stylized on #social. However, despite setting the feed property to the JSON response, render still returns an empty object. console.log in fetchData however displays the proper information.
var social = {}
social.Instagram = Backbone.Model.extend();
social.InstagramFeed = Backbone.Collection.extend({
model: social.Instagram,
url: 'https://api.instagram.com/v1/users/<user_id>/media/recent/?client_id=<client_id>',
parse: function(response) {
return response.results;
},
sync: function(method, model, options) {
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: this.url,
processData: false
}, options);
return $.ajax(params);
}
});
social.InstagramView = Backbone.View.extend({
el: '#social',
feed: {},
initialize: function() {
this.collection = new social.InstagramFeed();
this.fetchData();
this.render();
},
render: function() {
console.log(this.feed);
},
fetchData: function() {
this.collection.fetch({
success: function(collection, response) {
// console.log(response);
feed = response;
// console.log(this.feed);
},
error: function() {
console.log("failed to find instagram feed...");
}
});
}
});
social.instagramview = new social.InstagramView;
I've tried to output the information using just the fetchData function however this.el.append(response) results in a notice saying that el is undefined.
Your render method is called before the fetching has completed. You should bind to the sync event of the collection and call render in the event handler.
social.InstagramView = Backbone.View.extend({
el: '#social',
feed: {},
initialize: function() {
this.collection = new social.InstagramFeed();
this.fetchData();
this.collection.on('sync', function(){
this.render();
}, this);
// this.render();
},
...
})
Quoting Backbone.js documentation : sync event is fired :
when a model or collection has been successfully synced with the server.

create instance of model on AJAX call success

I have a User model:
var UserModel = Backbone.Model.extend({
defaults: {
handle: '',
email: '',
uuid: '',
userpic: '',
tokenlogin: ''
}
});
I also have a collection called UserSignIn, although I'm not sure why:
var UserSignIn = Backbone.Collection.extend({ model: UserModel });
And inside of my SignInView view, I have the following function...
signIn: function(e) {
e.preventDefault();
this.collection.fetch({
type: 'POST',
url: 'http://localhost/app/api/User.php',
dataType: "json",
data: $.param({
req: "REQUSERSIGNIN",
platform: "WEB",
useremail: $('#userSignIn #userEmail').val(),
userpass: $('#userSignIn #userPassword').val()
}),
success: function(data) {
// In here, I'd like to create an
// instance of the model which I'd
// like to pass around my app.
var user = new UserModel({
handle: data.HANDLE,
email: data.EMAIL,
uuid: data.UUIDUSER,
userpic: data.USERPIC,
tokenlogin: data.TOKENLOGIN
});
}
});
}
As you can see, all I am trying to do is create an instance of a User on success of the BackBone.fetch() function.
I'd like to understand how to then pass around this new "user" UserModel() instance around my app. When I try to console.log(user) I get a "ReferenceError: user is not defined" when clearly I just created it in the success callback of the fetch function.
Can someone explain to me why?
you have to insert it into your collection if you wanna follow the right way in backbone.
I think that you can do this:
into your initialize in the view insert this:
initialize: function(){
//..... your code in initialize
this.userModel = null;
this.collection = new UserCollection();
},
signIn: function(e) {
e.preventDefault();
var here = this;
this.collection.fetch({
type: 'POST',
url: 'http://localhost/app/api/User.php',
dataType: "json",
data: $.param({
req: "REQUSERSIGNIN",
platform: "WEB",
useremail: $('#userSignIn #userEmail').val(),
userpass: $('#userSignIn #userPassword').val()
}),
success: function(data) {
var user = {handle: data.HANDLE,email: data.EMAIL,uuid: data.UUIDUSER,userpic: data.USERPIC,tokenlogin: data.TOKENLOGIN};
here.userModel = new UserModel(user);
here.collection.addUser(here.userModel);
}
});
}
You UserCollection must be something like this:
var UserCollection = Backbone.Collection.extend({
model: UserModel,
initialize:function(){
console.log('Initialized User collection');
},
addUser: function(users, options) {
return this.add(users, options);
}
});
To console each element of your collection you can try this (if you run this code inside your success function use here instead of this):
this.collection.each(function(user, index) {
console.log(user);
//if you want to set a value of your model:
user.set('email', 'yournewemail#email.it');
//if you want to get some value
user.get('email');
});
The variable user is scoped only to within the success function of your SignInView view, so you can't console.log(user) which is looking for a user variable from the global scope. You could put console.log(user) right after creating the user within the success function to see that it is created, since this would find the local variable user.
To access it from the app, you could also declare var user; outside the fetch function and then simply set it within the fetch function.

Difference between $.ajax() and Backbone's .fetch() or .save() functions?

I'm trying to set data on my user model via the "signIn" function in my view:
initialize: function() {
console.log('Sign in view initialized');
this.render();
this.userModel = new UserModel();
this.collection = new UserCollection();
},
signIn: function(e) {
e.preventDefault();
var self = this;
$.ajax({
type: 'POST',
url: 'http://localhost/app/api/User.php',
dataType: "json",
data: $.param({
req: "REQUSERSIGNIN",
platform: "WEB",
useremail: $('#userSignIn #userEmail').val(),
userpass: $('#userSignIn #userPassword').val()
}),
success: function(response) {
self.userModel.set({
handle: response.HANDLE,
email: response.EMAIL,
uuid: response.UUIDUSER,
userpic: response.USERPIC,
tokenlogin: response.TOKENLOGIN
});
console.log(self.userModel.get("tokenlogin"));
}
});
},
I've read that the .fetch() or .save() is the backbone way of doing things, but I can't seem to set my UserModel when I use fetch. When I do it the jQuery.ajax() way though, it acts as desired.
Can someone explain to me the difference between doing it in $.ajax() or doing it via .fetch etc.
edit:
Here is my model code
var UserModel = Backbone.Model.extend({
defaults: {
handle: '',
email: '',
uuid: '',
userpic: '',
tokenlogin: ''
},
});
You should be able to do this by overriding your model's fetch method:
UserModel = Backbone.model.extend({
defaults: {
// ....
},
// set url for model (assumes app root is http://localhost/app)
url: '/api/User.php',
// pass custom parameters on fetch
fetch: function (options) {
var options = _.clone(options);
// set our custom parameters
options.req = "REQUSERSIGNIN";
options.platform = "WEB";
options.useremail = $('#userSignIn #userEmail').val();
options.userpass = $('#userSignIn #userPassword').val();
// call the Backbone method, which in turn calls $.ajax
Backbone.sync.apply(this, options);
},
});
Your model's Parse method should handle the response from the server just fine, since it seems like there is a one to one (response.handle will match model.attributes.handle). Without seeing the actual response it is a bit hard to know.

Infinite scroll backbone view

I would like to have an infinite/endless scroll data rendering from a JSON feed. I am interested in accomplishing something similar to Pinterest or Google Reader using Backbone/Underscore/jQuery.
How do I apply the infiniScroll.js module to my backbone view? The goal is to fetch and append the next page's ("page" URL parameter) tweets when you scroll near the end of the page. Problem: when reaching the bottom of page, same JSON page feed is fetched. How to change the page parameter in the URL to be &page=2, etc.
Demo: http://dl.dropbox.com/u/19974044/test.html OR http://jsfiddle.net/k4rPP/3/
// Define the model
Tweet = Backbone.Model.extend();
// Define the collection
Tweets = Backbone.Collection.extend({
model: Tweet,
// Url to request when fetch() is called
url: 'https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&trim_user=false&count=10&screen_name=cnn&page=1&callback=?',
parse: function (response) {
return response;
},
// Overwrite the sync method to pass over the Same Origin Policy
sync: function (method, model, options) {
var that = this;
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: that.url,
processData: false
}, options);
return $.ajax(params);
}
});
// Define the View
TweetsView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
// create a collection
this.collection = new Tweets;
// Fetch the collection and call render() method
var that = this;
this.collection.fetch({
success: function () {
that.render();
}
});
// infiniScroll.js integration
this.infiniScroll = new Backbone.InfiniScroll(this.collection, {success: this.appendRender, param:'page', includePage:true});
},
// Use an extern template
template: _.template($('#tweetsTemplate').html()),
render: function () {
// Fill the html with the template and the collection
$(this.el).html(this.template({
tweets: this.collection.toJSON()
}));
}
});
var app = new TweetsView({
// define the el where the view will render
el: $('body')
});​
The url attribute can be specified as a function rather than a string. So you could replace it with something like this:
...
currentPage: 0,
url: function() {
this.currentPage++;
return 'https://path.to.url/?page=' + this.currentPage;
},
...

Categories

Resources