I wanted to see how i could save a model to server using model.save() method when urlRoot is specified on the extended model, but ajax request never fires when i ask for model.fetch() or do model.save(). note: Is hope this is possible without using Collection i suppose?.
HTML
<div id="placeholder"></div>
<script type="text/template" id="view_template">
Hello <%= name %>, here is your script <%= script %>
</script>
Model
window["model"] = Backbone.Model.extend({
initialize: function () {
console.log("CREATED");
},
defaults:{
name:"Please enter your name",
script:"Hello World"
},
urlRoot: "index.aspx",
validate: function (attrs) {
},
sync: function (method, model, success, error) {
console.log("SYNCING", arguments);
}
});
View
window["view"] = Backbone.View.extend({
template:_.template($("#view_template").html()),
initialize: function () {
console.log("INITIALISED VIEW");
this.model.bind("change","render",this);
},
render: function (model) {
console.log("RENDERING");
$(this.el).append(this.template(model));
return this;
}
});
Application
$("document").ready(function () {
var myModel = new model({
name: "Stack Overflow",
script: "alert('Hi SO')"
});
var myView = new view({
model: myModel,
el: $("#placeholder")
});
console.log("SAVING");
myModel.save();
console.log("FETCHING");
myModel.fetch();
});
as you can see in application i call save & fetch but as per documentation this should fire ajax request with POST -> SAVE & GET -> FETCH. But all it does is log's arguments into console in the sync function.
I think the only reason you are not seeing any Ajax requests is that you have overridden the Model.sync method. Normally you would only do this if you wanted to replace the default Ajax syncing implemented in Backbone.sync. See the following line in Model.fetch in backbone.js:
return (this.sync || Backbone.sync).call(this, 'read', this, options);
I did a quick test with your code and I am seeing the Ajax requests if I rename your Model.sync method.
Related
I am making a small application form using Backbone.js and CodeIgniter but I am having problem with connection to controller.
Can anyone please provide me the full code for that?
My controller name is verfylogin. I have taken username and password from the user, and I have to pass it to the controller.
$(function(){
var Credentials = Backbone.Model.extend({});
var LoginView = Backbone.View.extend({
el: $("#login-form"),
events: {
"click #login": "login"
},
initialize: function(){
var self = this;
this.username = $("#username");
this.password = $("#password");
this.username.change(function(e){
self.model.set({username: $(e.currentTarget).val()});
});
this.password.change(function(e){
self.model.set({password: $(e.currentTarget).val()});
});
},
login: function(){
var username= this.model.get('username');
var password = this.model.get('password');
console.log(username,password);
}
});
window.LoginView = new LoginView({model: new Credentials()});
First, please read the Backbone documentation, look at the examples and test them to really understand how it works. Also, take a look at the Backbone.js tag wiki page.
It's irrelevant what is used for the API (backend), Backbone communicates with the backbend through a REST API which consist of URLs.
To link a Backbone model with a endpoint URL, override the urlRoot property.
var Credentials = Backbone.Model.extend({
// Specify the endpoint URL here
urlRoot: "api/endpoint" // relative
// urlRoot: "http://example.com/api/endpoint" // or absolute
});
Then, use the Backbone view's events hash to manage events within the view. Avoid manually binding jQuery events.
var LoginView = Backbone.View.extend({
events: {
"click #login": "login",
"change #username": "onUsernameChange",
"change #password": "onPasswordChange"
},
initialize: function() {
// create the model here
this.model = new Credentials();
// cache jQuery object within the view
this.$username = this.$("#username");
this.$password = this.$("#password");
},
login: function() {
// just call save to make a post request with the data.
this.model.save();
},
onUsernameChange: function(e) {
this.model.set({ username: this.$username.val() });
},
onPasswordChange: function(e) {
this.model.set({ password: this.$password.val() });
}
});
var loginView = new LoginView({ el: "#login-form" });
That way, the context (this) is available in the events callbacks. Avoid hard-coding the el property and prefer passing it on the initialization of a new view instance.
Handling JSON data posted to a CodeIgniter controller
Since I don't use CodeIgniter, I will refer you to other resources.
Working with restful services in CodeIgniter
Retrieve JSON POST data in CodeIgniter
Post JSON to Codeigniter controller
Get JSON response In Codeigniter
Code Igniter - How to return Json response from controller
Here is my backbone model constructor
define([], function(){
return Backbone.Model.extend({
urlRoot:'/dummy-api/Instances',
defaults:{
name:null,
read:false,
write:false
},
initialize: function () {
this.fetch();
console.log("after init "+this.get("id")+" name="+this.get("name"));
}
})
});
and at /dummy-api/Instances/1 is have put this
{"id":1,"name":"bangladesh"}
And I have attached this model to a view with this
define(['models/instance.js'], function(Model){
View = Backbone.View.extend({
initialize: function() {
this.model = new Model({
id:1
});
});
return new View();
});
The URL is getting called, and it's content is as above, I can see that in firebug, but "name" isnt getting set.
I know I can provide a parse function, which as I am using sequelize-restful-extended I may need to do, but I'd first like backbone to read and set from a fixed file. The doco is straight forward enough, what I have should work, so am I doing something else bad ?
You're logging the values before the model.fetch has completed.
Set a callback instead to log the values after fetch has successfully completed, and it should work as expected.
define([], function(){
return Backbone.Model.extend({
urlRoot:'/dummy-api/Instances',
defaults:{
name:null,
read:false,
write:false
},
initialize: function () {
this.fetch({
success: function() {
console.log("after init "+this.get("id")+" name="+this.get("name"));
}.bind(this)
});
}
})
});
This is necessary because this.fetch() executes an XMLHttpRequest asynchronously, and continues on to the next line of code while that request is executed by your browser in a separate "thread" (for all intents and purposes).
In using Backbone.js with Laravel 4, I am inserting into the database using this.collection.create() with the wait: true option. It inserts into the database alright, but it doesn't update the model if wait: true is set. So if I try output the collection again, it's not updated even though trying a success callback function returned a value and the POST request comes back positive.
The app is a contact managing system.
This is my model for a contact:
App.Models.Contacts = Backbone.Model.extend({
});
This is my View for adding a contact
App.Views.AddContact = Backbone.View.extend({
el: '#addContact',
events: {
'submit': 'addContact',
},
initialize: function() { // cache values
this.first_name = $('#first_name');
this.last_name = $('#last_name');
this.email_address = $('#email_address');
this.description = $('#description');
},
addContact: function(e) {
e.preventDefault();
this.collection = this.collection.create({
first_name: this.first_name.val(),
last_name: this.last_name.val(),
email_address: this.email_address.val(),
description: this.description.val(),
}, { wait: true });
},
});
And this is the collection:
App.Collections.Contacts = Backbone.Collection.extend({
model: App.Models.Contacts,
url: '/projects/gamesapp/public/contacts',
});
And this is the script on the page that puts all this into action:
<script>
new App.Router;
Backbone.history.start();
App.contacts = new App.Collections.Contacts;
App.contacts.fetch().then(function() {
new App.Views.App({ collection: App.contacts });
});
</script>
What can I do to get this working? I want create in the view to update the model locally as well, which it doesnt seem to.
Thanks!
The problem was not in the js or backbone code, it was in the Laravel code I wrote.
It seemed that the POST returned a positive response and all, but the problem was in my controller's store method, you have to return the create() function from the store() function!!!, to make it clear:
public function store()
{
$input = Input::all();
return Contact::create(array( // YOU HAVE TO RETURN SOMETHING
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'email_address' => $input['email_address'],
'description' => $input['description'],
));
}
My problem was ocurring because I wasn't returning it, just calling the Contact::create() function... (Contact is the Laravel Model)
Good day! I'm new in backbone and i writing a simple little application based backbone + jquerymobile.
When I get data from the server, I need to properly transfer the data to the view, where they are passed to the template. Because the .fetch() asynchronous, i cant just pass in render my model. How can I do so that would after .fetch() data received from the server, written in the model and then passed to the template?
//template
<script type="text/html" class="template" id="profile-form">
<div data-role="header">
<h3>Step 4</h3>
Home
logout
</div>
<div data-role="content">
<h2 class="ui-li-heading"><%= username %></h2>
<p class="ui-li-desc"><strong><%= phone %></strong></p>
</div>
</script>
// model
var UserInfo = Backbone.Model.extend({
url: appConfig.baseURL + "users/",
});
// routes
profileInfo: function () {
var user = new UserInfo()
this.changePage(new ProfilePageView({ model: user }));
},
// view
var ProfilePageView = Backbone.View.extend({
initialize: function () {
this.template = $.tpl['profile-form'];
},
render: function (eventName) {
$(that.el).html(that.template());
return this;
}
});
I trying to add this part in my render in view. Its works, but my styles are not working.
I'm not quite sure that I did the right thing by putting fetch in render, can advise how to do correctly?
var that = this
this.model.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST',
success: function (response) {
$(that.el).html(that.template(JSON.parse(JSON.stringify(response))));
}
});
Use the built-in events to decouple everything. Fetching is one step, updating is distinct. In your view do:
initialize: function () {
this.template = $('#profile-form').html();
this.listenToOnce(this.model, 'sync', function(){
this.render();
//this.listenTo(this.model, 'change', this.render, this);
}, this);
},
Every time the model has the set method called (and some attribute changes) it will trigger a change event. The listenTo method will run a callback when that happens. Another event you might want is sync which is called after a successful fetch. Sometimes you might need listenToOnce if you only want to render on the first sync and then control it yourself after that.
Your template probably needs its parameters passed in too:
render: function(){
$(this.el).html(_.template(this.template, this.model.toJSON()));
return this;
}
In terms of when to fetch, that's up to you a bit. You could fetch regularly or only when someone routes to that page. In the code you give I would do something like:
profileInfo: function () {
var user = new UserInfo();
user.fetch();
this.changePage(new ProfilePageView({ model: user }));
}
I believe my problem relates to scope somehow, as I'm a js newbie. I have a tiny backbone.js example where all I am trying to do is print out a list of items fetched from the server.
$(function(){
// = Models =
// Video
window.Video = Backbone.Model.extend({
defaults: function() {
return {
title: 'No title',
description: 'No description'
};
},
urlRoot: 'api/v1/video/'
});
// VideoList Collection
// To be extended for Asset Manager and Search later...
window.VideoList = Backbone.Collection.extend({
model: Video,
url: 'api/v1/video/'
});
// = Views =
window.VideoListView = Backbone.View.extend({
tagName: 'ul',
render: function(eventName) {
$(this.el).html("");
_.each(this.model.models, function(video) {
$(this.el).append(new VideoListRowView({model:video}).render().el);
}, this);
return this;
}
});
// VideoRow
window.VideoListRowView = Backbone.View.extend({
tagName: "li",
template: _.template("id: <%= id %>; title: <%= title %>"),
className: "asset-video-row",
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"assetManager"
},
assetManager:function() {
this.assetList = new VideoList();
this.assetListView = new VideoListView({model:this.assetList});
this.assetList.fetch();
$('#content').html(this.assetListView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
// The following works fine:
window.mylist = new VideoList();
window.mylistview = new VideoListView({model:window.mylist});
});
If I access mylist.fetch(); mylist.toJSON() from the console, mylist populates fine. I can tell that this.assetList.fetch() is accurately fetching the data from the backend, but it doesn't appear to be adding the objects to this.assetList.
The fetch method on Backbone collections is asynchronous:
Fetch the default set of models for this collection from the server, resetting the collection when they arrive. [...] Delegates to Backbone.sync under the covers, for custom persistence strategies.
And Backbone.sync says:
Backbone.sync is the function that Backbone calls every time it attempts to read or save a model to the server. By default, it uses (jQuery/Zepto).ajax to make a RESTful JSON request.
So fetch involves an (asynchronous) AJAX call and that means that you're trying to use the collection before fetch has retrieved the data from the server. Note that fetch supports success and error callbacks so you can do this instead:
var self = this;
this.assetList.fetch({
success: function(collection, response) {
$('#content').html(self.assetListView.render().el);
}
});
Or you could bind a callback to the collection's reset event as fetch will reset the collection. Then render your assetListView when the collection's reset event is triggered.
Also, your assetList is a collection so you should be doing:
this.assetListView = new VideoListView({collection: this.assetList});
and:
window.VideoListView = Backbone.View.extend({
tagName: 'ul',
render: function(eventName) {
$(this.el).html("");
_.each(this.collection.models, function(video) {
// ...