I've been trying for days to get this working and I just cannot figure out why when I have my view to destroy a model which belongs to a collection (which properly has a url attribute for the beginning fetch of models' data), only fires the destroy 'event' which is bubbled up to the collection for easy binding by my list view. But it does not ever send an actual DELETE request or any request to the server at all. Everywhere I look, I see everyone using either the collection's url attr, or urlRoot if the model is not connected to a collection. I've even tested before the actual this.model.destroy() to check the model < console.log(this.model.url());
I have not overwritten the destroy nor sync methods for backbone. Also each model does have an id attribute which is populated via the collection's fetch (from database records).
The destroy takes place in the list item view, and the collection's "destroy" event is bound in the list view. All that works well (the event handling), but the problem, again, is there's no request to the server.
I was hoping that backbone.js would do it automatically. That was what the documentation implies, as well as the numerous examples everywhere.
Much thanks to anyone who can give some useful input.
FYI: I'm developing on wampserver PHP 5.3.4.
ListItemView = BaseView.extend({
tagName: "li",
className: "shipment",
initialize: function (options) {
_.bindAll(this);
this.template = listItemTemplate;
this.templateEmpty = listItemTemplateEmpty;
},
events: {
'click .itemTag' : 'toggleData',
'click select option' : 'chkShipper',
'click .update' : 'update',
'click button.delete' : 'removeItem'
},
// ....
removeItem: function() {
debug.log('remove model');
var id = this.model.id;
debug.log(this.model.url());
var options = {
success: function(model, response) {
debug.log('remove success');
//debug.log(model);
debug.log(response);
// this.unbind();
// this.remove();
},
error: function(model, response) {
debug.log('remove error');
debug.log(response);
}
};
this.model.destroy(options);
//model.trigger('destroy', this.model, this.model.collection, options);
}
});
Collection = Backbone.Collection.extend({
model: Model,
url: '?dispatch=get&src=shipments',
url_put : '?dispatch=set&src=shipments',
name: 'Shipments',
initialize: function () {
_.bindAll(this);
this.deferred = new $.Deferred();
/*
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
*/
},
fetchSuccess: function (collection, response) {
collection.deferred.resolve();
debug.log(response);
},
fetchError: function (collection, response) {
collection.deferred.reject();
debug.log(response);
throw new Error(this.name + " fetch failed");
},
save: function() {
var that = this;
var proxy = _.extend( new Backbone.Model(),
{
url: this.url_put,
toJSON: function() {
return that.toJSON();
}
});
var newJSON = proxy.toJSON()
proxy.save(
newJSON,
{
success: that.saveSuccess,
error: that.saveError
}
);
},
saveSuccess: function(model, response) {
debug.log('Save successful');
},
saveError: function(model, response) {
var responseText = response.responseText;
throw new Error(this.name + " save failed");
},
updateModels: function(newData) {
//this.reset(newData);
}
});
ListView = BaseView.extend({
tagName: "ul",
className: "shipments adminList",
_viewPointers: {},
initialize: function() {
_.bindAll(this);
var that = this;
this.collection;
this.collection = new collections.ShipmentModel();
this.collection.bind("add", this.addOne);
this.collection.fetch({
success: this.collection.fetchSuccess,
error: this.collection.fetchError
});
this.collection.bind("change", this.save);
this.collection.bind("add", this.addOne);
//this.collection.bind("remove", this.removeModel);
this.collection.bind("destroy", this.removeModel);
this.collection.bind("reset", this.render);
this.collection.deferred.done(function() {
//that.render();
that.options.container.removeClass('hide');
});
debug.log('view pointers');
// debug.log(this._viewPointers['c31']);
// debug.log(this._viewPointers[0]);
},
events: {
},
save: function() {
debug.log('shipments changed');
//this.collection.save();
var that = this;
var proxy = _.extend( new Backbone.Model(),
{
url: that.collection.url_put,
toJSON: function() {
return that.collection.toJSON();
}
});
var newJSON = proxy.toJSON()
proxy.save(
newJSON,
{
success: that.saveSuccess,
error: that.saveError
}
);
},
saveSuccess: function(model, response) {
debug.log('Save successful');
},
saveError: function(model, response) {
var responseText = response.responseText;
throw new Error(this.name + " save failed");
},
addOne: function(model) {
debug.log('added one');
this.renderItem(model);
/*
var view = new SB.Views.TicketSummary({
model: model
});
this._viewPointers[model.cid] = view;
*/
},
removeModel: function(model, response) {
// debug.log(model);
// debug.log('shipment removed from collection');
// remove from server
debug.info('Removing view for ' + model.cid);
debug.info(this._viewPointers[model.cid]);
// this._viewPointers[model.cid].unbind();
// this._viewPointers[model.cid].remove();
debug.info('item removed');
//this.render();
},
add: function() {
var nullModel = new this.collection.model({
"poNum" : null,
"shipper" : null,
"proNum" : null,
"link" : null
});
// var tmpl = emptyItemTmpl;
// debug.log(tmpl);
// this.$el.prepend(tmpl);
this.collection.unshift(nullModel);
this.renderInputItem(nullModel);
},
render: function () {
this.$el.html('');
debug.log('list view render');
var i, len = this.collection.length;
for (i=0; i < len; i++) {
this.renderItem(this.collection.models[i]);
};
$(this.container).find(this.className).remove();
this.$el.prependTo(this.options.container);
return this;
},
renderItem: function (model) {
var item = new listItemView({
"model": model
});
// item.bind('removeItem', this.removeModel);
// this._viewPointers[model.cid] = item;
this._viewPointers[model.cid] = item;
debug.log(this._viewPointers[model.cid]);
item.render().$el.appendTo(this.$el);
},
renderInputItem: function(model) {
var item = new listItemView({
"model": model
});
item.renderEmpty().$el.prependTo(this.$el);
}
});
P.S... Again, there is code that is referenced from elsewhere. But please note: the collection does have a url attribute set. And it does work for the initial fetch as well as when there's a change event fired for saving changes made to the models. But the destroy event in the list-item view, while it does trigger the "destroy" event successfully, it doesn't send the 'DELETE' HTTP request.
Do your models have an ID? If not, the HTTP request won't be sent. –
nikoshr May 14 at 18:03
Thanks so much! Nikoshr's little comment was exactly what I needed. I spent the last 5 hours messing with this. I just had to add an id to the defaults in my model.
Related
I try to fetch data from a mySQL database using Axios to set the initial values of a form input generated with vue.js-formulate.
Here is my script where I want to set the initial value of "question1":
new Vue({
el: '#app',
created() {
this.fetchData();
},
data: {
row: "",
values: {
question1: this.row["answerq1"],
}
},
methods: {
fetchData() {
axios.get('retrieve.php')
.then(function (response) {
this.row = response.data;
// Checking output in Console:
console.log(this.row["answerq1"]);
});
},
}
})
The fetchData() function is working as expected, this.row["answerq1"] prints the expected string. However, access this value in the data part produces the error "this.row is undefined". I'm guessing it has something to do with the lifecycle of the created() hook but I can't figure it out.
this.row is an empty string before the API request is done, therefore you cannot access this.row["answerq1"]. You need to wait for the API request to finish.
Make this change and it should work:
data() {
return {
row: "",
values: {
question1: "" // set to empty string
}
};
}
I have found the answer to my question for anyone encountering a similar problem:
new Vue({
el: '#app',
created() {
this.fetchData();
},
data: {
row: [],
values: {
question1: null
}
},
methods: {
fetchData() {
axios.get('retrieve.php')
.then((response) => {
this.row = response.data;
this.values.question1 = this.row["answerq1"];
});
},
}
})
I am developing an app to store contact information and utilizing Vuejs and Laravel to do it. I am also using the axios library for CRUD functionality.
I have this error on axios.delete() I cannot figure out. This is my Contacts.Vue file:
<script>
export default {
data: function(){
return {
edit:false,
list:[],
contact:{
id:'',
name:'',
email:'',
phone:''
}
}
},
mounted: function(){
console.log('Contacts Component Loaded...');
this.fetchContactList();
},
methods: {
fetchContactList: function(){
console.log('Fetching contacts...');
axios.get('api/contacts').then((response) => {
console.log(response.data);
this.list = response.data;
}).catch((error) => {
console.log(error);
});
},
createContact: function(){
console.log('Creating contact...');
let self = this;
// merging params to the current object
let params = Object.assign({}, self.contact);
// pass above to axios request
axios.post('api/contact/store', params)
.then(function(){
self.contact.name = '';
self.contact.email = '';
self.contact.phone = '';
self.edit = false;
self.fetchContactList();
})
.catch(function(error){
console.log(error);
});
},
showContact: function(id){
let self = this;
axios.get('api/contact/' + id)
.then(function(response){
self.contact.id = response.data.id;
self.contact.name = response.data.name;
self.contact.email = response.data.email;
self.contact.phone = response.data.phone;
})
self.edit = true;
},
updateContact: function(id){
console.log('Updating contact '+id+'...');
let self = this;
// merging params to the current object
let params = Object.assign({}, self.contact);
// pass above to axios request
axios.patch('api/contact/'+id, params)
.then(function(){
self.contact.name = '';
self.contact.email = '';
self.contact.phone = '';
self.edit = false;
self.fetchContactList();
})
.catch(function(error){
console.log(error);
});
},
deleteContact: function(id){
axios.delete('api/contact/'+id)
.then(function(response){
self.fetchContactList();
})
.catch(function(error){
console.log(error);
});
}
}
}
</script>
I am getting a TypeError message saying that self.fetchContactList is not a function.
I know that its saying that the value is not actually a function. There is no typo in the function name. Did I call the function on the wrong object? Should I be using a different property name?
I used self.fetchContactList(); on adding and updating contacts, why will it not work with deleting the contact?
Do I need to add request headers? I didn't have to for the other requests.
If I simply remove self.fetchContactList() it will not function at all.
Despite the error, when I refresh the page, it deletes the contact, but I want the contact deleted upon clicking the delete button.
You don't have let self = this; line in deleteContact function, obviously you would get an error.
alternatively, you can use ES6 arrow functions to avoid assigning this to separate variable like this:
deleteContact: function(id) {
axios.delete('api/contact/'+id)
.then((response) => {
this.fetchContactList();
})
.catch((error) => {
console.log(error);
});
}
My goal to achieve is:
first to insert new database record with http post, resolve with stateProvider and grab the new id and change view and stateParams.
i have this code for my http post service
myApp.service('postService', ['$http', function($http) {
this.insertNew = function() {
$http.post('create_new.php')
.success(function(data) {
return data;
});
};
create_new.php returns the ID like this (it works, proved with console)
return json_encode($data);
and the stateProvider looks like this (section)
$stateProvider
.state('newRecord', {
resolve: {
newArtID: ['postService',
function(postService) {
return postService.insertNew();
}]
},
params: {
artID: <-- new ID from DB
},
i did tests with stateParams in serval variations (in resolve and by params). how can i bring the new ID to stateParams, so i can access from the views?
Thanks for any help!
I'm not so sure your oder of operations is correct. params is for when you already have that data. You should return the data from your resolve, then you can access it in your scope, for ex:
Service:
.service('postService', function ($http) {
this.insertNew = function () {
return $http.post('create_new.php').then(function (data) {
return data;
});
}
})
Route:
$stateProvider
.state('newRecord', {
views: {
"main": {
controller: 'SomeCtrl',
templateUrl: '...'
}
},
resolvedId: {
newArtID: function (postService) {
return postService.insertNew().then(function (response) {
return response;
});
}
}
})
Controller:
.controller('SomeCtrl', function (resolvedId) {
var newID = resolvedId.id; //depending on what is returned
});
I am using backbone.js for the first time and I am using in conjunction with php and mysql so that I can send and receive data from a database using backbone and I am having a problem with the delete request method. I cannot access the data that is sent along with the request method like I do with post and put. POST and PUT work perfectly in this script it is just delete I am having problems with.
Here is my code.
helloWorld.js
(function($) {
var Item = Backbone.Model.extend({
url: 'http://mysite.com/syncItem.php',
defaults: {
part1: 'hello',
part2: 'world',
}
});
var List = Backbone.Collection.extend({
model: Item
});
var ItemView = Backbone.View.extend({
tagName: 'li',
events: {
'click span.swap': 'swap',
'click span.delete': 'remove'
},
initialize: function() {
_.bindAll(this, 'render', 'unrender', 'swap', 'remove');
this.model.bind('change', this.render);
this.model.bind('remove', this.unrender);
},
render: function() {
$(this.el).html('<span style="color:black;">' + this.model.get('part1') +' '+this.model.get('part2')+'</span> <span class="swap" style="font-family:sans-serif; color:blue; cursor:pointer;">[swap parts]</span> <span class="delete" style="cursor:pointer; color:red; font-family:sans-serif;">[delete]</span>');
return this;
},
unrender: function() {
$(this.el).remove();
},
swap: function() {
var swapped = {
part1: this.model.get('part2'),
part2: this.model.get('part1')
};
this.model.set(swapped);
this.model.sync("update", this.model);
},
remove: function() {
this.model.sync("delete", this.model); //deletes Item from server DB
this.model.destroy(); //deletes Item from collection
}
});
var ListView = Backbone.View.extend({
el: $('div#backboneContainer'),
events: {
'click button#add': 'addItem'
},
initialize: function() {
_.bindAll(this, 'render', 'addItem', 'appendItem');
this.collection = new List();
this.collection.bind('add', this.appendItem);
this.counter = 0;
this.render();
},
render: function() {
var self = this;
$(this.el).append("<button id='add'>Add List Item</button>");
$(this.el).append("<ul></ul>");
},
addItem : function() {
this.counter++;
item = new Item({id: this.counter});
item.set({
part2: item.get('part2') + this.counter
});
this.collection.add(item);
item.sync("create", item);
},
appendItem: function() {
var itemView = new ItemView({
model: item
});
$('ul', this.el).append(itemView.render().el);
}
});
var listView = new ListView();
})(jQuery);
syncItem.php
<?php
include("includes/openDbConn.php");
$request_method = strtolower($_SERVER['REQUEST_METHOD']);
switch($request_method) {
case 'post': {
$data = json_decode(file_get_contents('php://input'));
if($stmt = $mysqli->prepare("INSERT INTO backbonetest (modelId, part1, part2) VALUES (?, ?, ?)"))
{
$stmt->bind_param("iss", $data->{'id'}, $data->{'part1'}, $data->{'part2'});
$stmt->execute();
}
break;
}
case 'get': {
//NOT FINISHED
break;
}
case 'put': {
$data = json_decode(file_get_contents('php://input'));
if($stmt = $mysqli->prepare("UPDATE backbonetest SET part1 = ?, part2 = ? WHERE modelId = ?"))
{
$stmt->bind_param("ssi", $data->{'part1'}, $data->{'part2'}, $data->{'id'});
$stmt->execute();
}
break;
}
case 'delete': {
$data = json_decode(file_get_contents('php://input')); //gets data from the json that was sent with the request
if($stmt = $mysqli->prepare("DELETE FROM backbonetest WHERE modelId = ?"))
{
$stmt->bind_param("i", $data->{'id'});
$stmt->execute();
}
break;
}
}
?>
I have narrowed down where the error is to two spots: where I call this.model.sync("delete", this.model); or $data = json_decode(file_get_contents('php://input')); in the delete case. So I was wondering if there was a special way to access the data when dealing with the delete request method or am I just calling the delete function in backbone wrong?
You have a problem of unfulfilled expectations :) DELETE requests in Backbone don't send by default the attributes as POST, PUT and PATCH do1 : compare the requests sent by model.save and model.destroy in this Fiddle http://jsfiddle.net/fz68a/
You can override Item.sync to give it the behavior you expect:
var Item = Backbone.Model.extend({
defaults: {
part1: 'hello',
part2: 'world'
},
sync: function (method, model, options) {
if (method === 'delete') {
options = options || {};
options.contentType = 'application/json';
options.data = JSON.stringify(this.toJSON());
}
return Backbone.sync.call(this, method, model, options);
}
});
And a demo http://jsfiddle.net/fz68a/1/
1 See Backbone source code http://documentcloud.github.io/backbone/docs/backbone.html#section-139
from what i see on your php code. should already do this.as $_SERVER['REQUEST_METHOD'] which you already are using already answers your question. you can also do a dump $_SERVER to see if yuo backbone app did indeed make the request.
on another note...
...
remove: function() {
this.model.sync("delete", this.model); //deletes Item from server DB
this.model.destroy(); //deletes Item from collection
}
...
you don't need to call model.sync, as calling destroy already implicitly does this.
http://backbonejs.org/#Model-destroy
...
addItem : function() {
this.counter++;
item = new Item({id: this.counter});
item.set({
part2: item.get('part2') + this.counter
});
this.collection.add(item);
item.sync("create", item);
},
...
also instead of doing an collection.add. you can call collection.create
http://backbonejs.org/#Collection-create
I am unable to understand what is "app.wineList.create(this.model)" in the "saveWine" method. How it will work ? I am new to backbone.js, plz help me to understand this. I am aware of this.model.save().
Actually I have removed some code here. Just I have posted the code where my problem was.
Thanks.
// Models
window.Wine = Backbone.Model.extend({
urlRoot:"../api/wines",
defaults:{
"id":null,
"name":"",
"grapes":"",
"country":"USA",
"region":"California",
"year":"",
"description":"",
"picture":""
}
});
window.WineCollection = Backbone.Collection.extend({
model:Wine,
url:"../api/wines"
});
// Views
window.WineView = Backbone.View.extend({
template:_.template($('#tpl-wine-details').html()),
initialize:function () {
this.model.bind("change", this.render, this); // (event, function, context)
},
render:function (eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
events:{
"click .save":"saveWine"
},
saveWine:function () {
this.model.set({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
if (this.model.isNew()) {
app.wineList.create(this.model);
} else {
this.model.save();
}
return false;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"list",
"wines/:id":"wineDetails"
},
initialize:function () {
$('#header').html(new HeaderView().render().el);
},
list:function () {
this.wineList = new WineCollection();
this.wineListView = new WineListView({model:this.wineList});
this.wineList.fetch();
$('#sidebar').html(this.wineListView.render().el);
},
wineDetails:function (id) {
this.wine = this.wineList.get(id);
if (app.wineView) app.wineView.close();
this.wineView = new WineView({model:this.wine});
$('#content').html(this.wineView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
As described in the Backbone documentation:
Convenience to create a new instance of a model within a collection.
Equivalent to instantiating a model with a hash of attributes, saving
the model to the server, and adding the model to the set after being
successfully created.
So it adds a model to your winelist collection, and saves it to server.