I'm using backbone.js as my javascript framework for my project. I got this error when retrieve data through API.
XMLHttpRequest cannot load https://izify.com/api/izify-api/user/get_all_categories.php?merchantId=74718912a2c0d82feb2c14604efecb6d. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://staging.revivalx.com' is therefore not allowed access. staging.revivalx.com/:1
error SidebarView.js:23
XMLHttpRequest cannot load https://izify.com/api/izify-api/user/get_all_products.php?merchantId=74718912a2c0d82feb2c14604efecb6d. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://staging.revivalx.com' is therefore not allowed access. staging.revivalx.com/:1
error HomeView.js:23
SidebarView.js
define(['jquery', 'underscore', 'backbone','models/global/GlobalModel','collections/category/CategoryCollection', 'text!templates/sidebar/sidebarTemplate.html'], function($, _, Backbone,GlobalModel,CategoryCollection, sidebarTemplate) {
var SidebarView = Backbone.View.extend({
el: $("#sidebar"),
initialize: function() {
this.$el.off();
},
render: function() {
var that = this;
var global = new GlobalModel();
this.collection = new CategoryCollection();
var formValues = {
merchantId: global.merchantId
};
this.collection.fetch({
data: formValues,
success: function(collection, response) {
var template = _.template(sidebarTemplate,{
categories: that.collection.models
});
$("#sidebar").append(template);
},
error: function(collection, response) {
console.log("error");
}
});
}
});
return SidebarView;
});
HomeView.js
define(['jquery', 'underscore', 'backbone','models/global/GlobalModel','collections/product/ProductCollection','views/sidebar/SidebarView','text!templates/home/homeTemplate.html'], function($, _, Backbone,GlobalModel,ProductCollection,SidebarView, homeTemplate) {
var HomeView = Backbone.View.extend({
el: $("#page"),
initialize: function() {
this.$el.off();
},
render: function() {
var that = this;
var global = new GlobalModel();
this.collection = new ProductCollection();
var formValues = {
merchantId: global.merchantId
};
this.collection.fetch({
data: formValues,
success: function(collection, response) {
var template = _.template(homeTemplate, {
products: that.collection.models
});
that.$el.html(template);
},
error: function(collection, response) {
console.log("error");
}
});
var sidebarView = new SidebarView();
sidebarView.render();
},
});
return HomeView;
});
My API header
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET');
I already upload my source code in github : https://github.com/datomnurdin/izify-template
Demo: http://staging.revivalx.com/izify-template/
Thanks a lot in advance.
Seems like CORS problem, you could not sove this with client side only.
To test API CORS support you can use test CORS
So you can solve this by:
Implement CORS support on API server side.
Proxy requests from your app server side to API.
Use another techics to cross-origin policy like(JSONP, etc..)
Related
I am trying to delete a model via:
var FeaturedUsersView = Backbone.View.extend({
events: {
"click .remove-featured-user" : "removeFeaturedUser"
},
template : _.template(FeaturedUsersTemplate),
render: function(){
this.$el.html(this.template({ featuredUsers: App.featuredUsers.toJSON() }));
return this;
},
removeFeaturedUser: function(event) {
userToRemove = this.collection.get(event.target.id);
userToRemove.set("_token", $('input[name="_token"]').val());
userToRemove.destroy({
wait: true,
/*
*headers: {
* _token : $('input[name="_token"]').val()
*},
*async: false,
*/
url: '/api/admin/versions/' + App.config.get("class_id") + '/featured_users',
});
}
});
I keep getting a ERROR: exception 'Illuminate\Session\TokenMismatchException'.
This same view have a model.save() that works with the same token. I have a feeling the destroy method is ignoring the token in the DELETE request. Any tips?
Here is backbone.js's destroy method:
destroy: function(options) {
options = options ? _.clone(options) : {};
var model = this;
var success = options.success;
var wait = options.wait;
var destroy = function() {
model.stopListening();
model.trigger('destroy', model, model.collection, options);
};
options.success = function(resp) {
if (wait) destroy();
if (success) success.call(options.context, model, resp, options);
if (!model.isNew()) model.trigger('sync', model, resp, options);
};
var xhr = false;
if (this.isNew()) {
_.defer(options.success);
} else {
wrapError(this, options);
xhr = this.sync('delete', this, options);
}
if (!wait) destroy();
return xhr;
},
Adding this globally fixed my problem:
<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
I am trying to do api call from my localhost to a different domain (https://xyz)
my solution:
<script> // ajax prefilter
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.crossDomain ={
crossDomain: true
};
options.xhrFields = {
withCredentials: true
};
options.url = 'https://xyz' + options.url;
});
</script>
<script> // collection
var KCollection = Backbone.Collection.extend({
url: '/api/s/y/',
});
</script>
<script> // view
var DataAgesView = Backbone.View.extend({
render: function(){
var that = this;
mycollecion = new KCollection();
mycollecion.fetch({
dataType: 'jsonp',
success: function(){
console.log('success!');
},
error: function() { console.log('Uh Oh!'); },
})
}
});
</script>
This will send a GET request to "https://xyz/api/s/y"
and I can see the data came back to my browser by looking at the dev tool/network.
But there is this error: Uncaught SyntaxError: Unexpected token :
I am very new to web and backbone.js. I used jsonp to just be able to call cross domains, and I am open to use any other solutions for that (as long as I understand it).
Note that my server returns "json" and I can not change anything from server side.
define(function (require) {
"use strict";
var $ = require('jquery'),
Backbone = require('backbone'),
Shell = require('app/views/Frame/Shell'),
Auth = require('app/models/json/session');
// Tell jQuery to watch for any 401 or 403 errors and handle them appropriately
$.ajaxSetup({
statusCode: {
401: function(){
// Redirect the to the login page.
Backbone.history.navigate("login", true);
},
403: function() {
// 403 -- Access denied
Backbone.history.navigate("login", true);
}
}
});
return Backbone.Router.extend({
routes: {
"" : "home",
"login" : "login",
"logout" : "logout"
},
initialize: function (opt) {
ev = opt.ev;
//Session.fetch();
},
home: function (id) {
new Dashboard({ev: ev});
}
});
});
The structure above I learned from http://clintberry.com/2012/backbone-js-apps-authentication-tutorial/ and I feel setting ajax error globally error like this not the proper way
What is the right way using backbone and requirejs?
define(["Backbone"], function(Backbone){
Backbone.ajax = function() {
// Invoke $.ajaxSetup in the context of Backbone.$
Backbone.$.ajaxSetup.call(Backbone.$, {
statusCode: {
401: function(){
// Redirect the to the login page.
Backbone.history.navigate("login", true);
},
403: function() {
// 403 -- Access denied
Backbone.history.navigate("login", true);
}
}
});
return Backbone.$.ajax.apply(Backbone.$, arguments);
};
});
I'm have the following backbone model
define(["jquery", "underscore", "backbone"],
function ($, _, Backbone) {
var file_upload = Backbone.Model.extend({
url: 'http://localhost:8080/rest/customForms'
});
return file_upload;
}
I have a view loaded at
localhost:38559/app/forms.html
which tries to do a post with the following code
var fd = document.getElementById('fileToUpload').files[0];
var file = new file_upload();
file.fetch({data: $.param({fileToUpload: fd}),
type: 'POST',
success: function(d){
console.log('success');
}
});
but this seems to just do a get request to forms.html passing fd as a param. I've also tried overriding the sync method in file_upload
sync: function (method, model, options) {
var self = this;
options = _(options).clone();
var error = options.error;
options.error = function (jqXHR, textStatus, errorThrown) {
alert('error');
if (error)
error(jqXHR, textStatus, errorThrown);
};
var success = options.success;
options.success = function (data, textStatus, jqXHR) {
if (success && data) {
alert("Success uploading form.");
success(data, textStatus, jqXHR);
}
else
alert("Error uploading form. Please try entering again.");
};
var params = {
type: 'POST'
};
$.ajax(_.extend(params, options));
}
}
I'm doing posts in other parts of the app with similar code so can't figure out why with this code the fetch does a get request to the page it's called on rather than a post to the url specified in the model. Does anyone have any ideas?
Thanks,
Derm
Ugh - finally found the issue coming back to this. The file upload was been done on a button click event. I needed to call preventdefault to force the use of the models url rather than the pages url. Annoying issue - dunno how I missed it! Code now is
uploadForm: function (e) {
e.preventDefault();
var self = this;
var fd = document.getElementById('fileToUpload').files[0];
var file = new file_upload();
file.fetch({data: $.param({fileToUpload: fd}),
type: 'POST',
success: function(d){
console.log('success');
}
});
},
Backbone.js's default, RESTful approach to fetching a model by the ID is easy and straight-forward. However, I can't seem to find any examples of fetching a model by a different attribute. How can I fetch a Backbone.js model by a different attribute?
var Widget = Backbone.Model.extend({
urlRoot: '/widgets',
fetchByName: function(){ ... }
});
var foowidget = new Widget({name: 'Foo'});
foowidget.fetchByName();
You can try doing something like this on your base model definition or on demand when calling fetch.
model.fetch({ data: $.param({ someParam: 12345}) });
In your case, along the lines of.
var Widget = Backbone.Model.extend({
initialize: function(options) {
this.name = options.name;
},
urlRoot: '/widgets',
fetchByName: function(){
this.fetch({ data: $.param({ name: this.name }) })
}
});
var foowidget = new Widget({name: 'Foo'});
foowidget.fetchByName();
One approach is to override Backbone.sync() method, either for all classes or for just your class. However, presumably your goal is to override fetch for just a single model. One way to do that is to directly call jQuery.ajax(...), and on success, take the response and set that, e.g.
fetchByName: function() {
var self = this;
$.ajax({
url: self.urlRoot+ "?name="+this.get('name'),
type: 'GET',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
self.set(data);
}
});
}
If the model is part of a collection you can use where() to pull out the models matching some criteria.
See http://backbonejs.org/#Collection-where
I really like the approach suggested by 'user645715'. I have adjusted the code to be more versatile. If you add this to a Backbone Model it will allow you to search the server by one or more attributes, and should work as a direct drop-in replacement for fetch.
fetchByAttributes: function(attributes, callbacks) {
var queryString = [];
for(var a in attributes){
queryString.push( encodeURIComponent(a)+'='+encodeURIComponent(attributes[a]) );
}
queryString = '?'+queryString.join('&');
var self = this;
$.ajax({
url: this.urlRoot+queryString,
type: 'GET',
dataType: "json",
success: function(data) {
self.set(data);
callbacks.success();
},
error: function(data){
callbacks.error();
}
});
}
It can be used like this:
var page = new Page();
page.fetchByAttributes({slug:slug}, {
success: function(){
console.log('fetched something');
},
error: function(){
console.log('nothing found');
}
});
this is simple model.fetch is same as $.ajax in some way
model = Backbone.Model.extend({
urlRoot: "/root/"
});
var Model = new model();
Model.fetch({
beforeSend: function () {
console.log("before");
},
data: {
param1: "param1",
param2: "param2"
},
success: function () {
console.log("success");
},
error: function () {
console.log("failure");
}
});