Dynamically load my 'IDs' into my Backbone Collection? - javascript

Ok, I have got this working with answers posted on another question here, but I am trying to do something a little more. I have a Backbone set up as follows,
var MyModel= Backbone.Model.extend();
var MyCol = Backbone.Collection.extend({
model : MyModel,
url: '/GetData/2',
parse: function(response) {
return response;
}
});
var stuff = new MyCol;
stuff.fetch({
success: function (collection, response) {
console.log(response);
}
})
Now this code fully works. Now to explain, the URL is set with PHP Slim, which returns JSON encoded data, where will be four key/pair groupings, for example:
<code> { id; XX, data: XX, another:XX, last:YY } </code>
This data is being received form a database, now how should I do this dynamically? By that I mean that on the URL' line in my collection, it passes 2 as the current ID. How can I get backbone to call each ID I need, there are currently 6 listing in the database. So I need it display/console log (currently for testing) all 6 inputs, so how would I change that ID input to 1-6? Should I just use a for loo? I really want something that will not need to change if say lists are removed form the database or more are added?
The end aim is for this JSON data is for it to load into my Backbone view, which is a template for a form.
All help most welcome,
Thanks
Glenn
P.S Sorry if my spelling is off I am dyslexic, also may not have explained things right, so let me know and I will improve my wording, thanks.
EDITS
This is what I am working with now ::
var AdminModel = Backbone.Model.extend({
defaults: {},
urlRoot: '/GetData'
});
var AdminColModel = Backbone.Collection.extend({
model : AdminModel,
url: '/',
parse: function(response) {
//console.log(response);
return response;
}
});
var stuff = new AdminColModel;
stuff.fetch({
success: function (collection, response) {
console.log(collection);
}
})
This does not console log anything at all? But the PHP is set up and working fine, so when you go to /getData you get a whole list if all the rows in the database. When you view /getData/X <- X is the id number, that will return just that rows ID data.
Glenn

You should not make an individual request for each model in your collection at the collection level. The url you have set for your collection should request all of the models by default. So instead of it being:
'/GetData/2'
It should instead be:
'/GetData'
Then on the server side you should ensure that the GetData action returns all MyModels.
You can then write overrides for GetData that take parameters to either return an individual model, or filter the results. You will be particularly interested in the individual model retrieval, which should be the URL used at the model level. in fact, you already have this action, as you are using it (incorrectly) to retrieve the collection. This is the action associated with the /GetData/2 URL. This action should be the one used when you call fetch inside the model, not the collection.
Hopefully this helps.
Post Edit
Adding some code to assist.
var AdminModel = Backbone.Model.extend({
urlRoot: '/GetData'
});
var AdminCollection = Backbone.Collection.extend({
model : AdminModel,
url: '/GetData'
});
var wholeCollection= new AdminCollection();
// the fetch method on the collection uses the AdminCollection.url property
wholeCollection.fetch({
success: function (collection) {
console.log(collection); // will output collection object to console
}
});
// this model does not exist in any collection
var modelOutsideCollection = new AdminModel({}, { id: "2" });
// because the model is not in a collection, fetch uses the AdminModel.urlRoot proeprty
modelOutsideCollection.fetch({
success: function (model) {
console.log(model); // outputs a single model with id of 2 to collection
}
});

So... The way you seem to be doing things is going to make it really hard (if even possible) to work with Backbone.
What you should have is an API endpoint (e.g.) "getData" that will return all models:
[
{id: 1, name: "asdflj"},
{id: 2, name: "lsdfkjg"},
// etc.
]
Then, you define "getData" as the collection's url. For each model in the collection, Backbone will automatically compute the model instance's url as (e.g.) "getData/2" (if the model's id is 2).
In other words, you'd need to change your API to have:
getData -> return all models
getData/2 -> return model with id 2

Related

SAPUI5 OData - How to create new entry with association to existing entity?

I am currently using SAPUI5/OpenUI5 to consume and modify OData Services.
I want to create a new product entry over an HTTP POST Request and have problems to properly config the associations to a category. For developing reasons I am using a reference OData Service with this metadata. The Product already has the NavigationProperty to the right Category EntrySet.
<NavigationProperty Name="Category" Relationship="ODataDemo.Product_Category_Category_Products" FromRole="Product_Category" ToRole="Category_Products"/>
I am using the following JavaScript code in my controller:
var oCategory = oModel.getData("/Categories(0)");
var oEntry = {};
oEntry.ID = "10";
oEntry.Name = "Beer";
oEntry.Category = oCategory;
oModel.create("/Products", oEntry, {
method: "POST",
success: function(data) {...},
error: function(response) {...}
});
The product is successfully created /Products(10) but the relation to the existing category /Products(10)/Category is not working properly. Instead a new category with the same ID and information is created (is this meant with 'deep insert'?) but I want to use the elected category (of course).
Do I have to reference the category differently or can I create the associations manually somehow? Shouldn't the OData Service check if the category ID already exists and then use the existing entry?
Is there any best practices for such cases?
It's important to note that you are using an OData V2 service. Yes, by building the request the way you are doing it, you are actually doing a deep insert.
If you think about it, it makes sense, because you would not need to send the whole category information to just link the new product to the exiting category. What if you would change something in the category data? Should a deep insert result in an update?
In any case, OData v2 has something called "links" (see the OData terminology - www.odata.org). Basically each "association" between entities is represented through such a link. You can manage these links separately from the entity (e.g. you can remove and create links between existing entities; without having to change the entity itself - see the OData v2 operations, chapters 2.9 to 2.12).
Depending on the data format that you are using (by default, JSON if you are using sap.ui.model.odata.v2.ODataModel), you can create entity links in the same time when creating new entities. Check out this answer: https://stackoverflow.com/a/4695387/7612556.
In a nutshell, you would have to write something along the lines of:
oModel.create("/Products", {
ID: "10",
Name: "Beer",
Category: {__metadata: {uri: "/Categories(0)"}}
}, {
method: "POST",
success: function(data) {...},
error: function(response) {...}
});

Backbone - change default GET URL format

REST:
My REST API is on the following URL:
http://localhost/Project/index.php/rest/resource/car
This runs a standard get request that returns all the data in the table as a JSON.
However, my REST API also has the following:
http://localhost/Project/index.php/rest/resource/car/carId/2.
Which when used will return only the data for the Car, with carId = 2.
Backbone:
this.mycar.fetch({
data: { 'carId': '1' },
success: function () { ...
Running the above code in backbone will create and run a request that looks like this: http://localhost:7070/Project/index.php/rest/resource/car?carId=1, which is not compatible with my REST api, so how can I change it?
Edit:
Model and Collection:
var Car = Backbone.Model.extend({
urlRoot: ROOT + 'car',
idAttribute: 'carId'
});
var Cars = Backbone.Collection.extend({
model: Car,
url: ROOT + 'car',
})
The data property is the jQuery $.ajax's data property. For GET requests it's used for setting query parameters.
You should update the .url method/property of the model instead.
Setting it directly as a parameter:
this.mycar.url = 'api_entry_point/car/' + car_id;
edit: I just have noticed that you have carId as a resource in your url. This is a rather unconventional url. It seems this is why you have idAttribute: 'carId' in your model constructor. The carId doesn't have to be in the url. It could be:
http://localhost/Project/index.php/rest/resource/car/2
In the above url, the 2 is the model id. There is absolutely no need to have carId in the url. Also as resource contains more than one item, using cars instead of the car segment makes more sense.
http://localhost/Project/index.php/rest/resource/cars/2
Of course it's just a personal preference.
In your backbone model file you can pass like this
url : return "http://localhost/Project/index.php/rest/resource/car/carId/=" + this.id;
in your backbone view you can pass like
this.model.id = this.id;
this.mycar.fetch({
success: function () {

Backbone Collection got wrong Models

how can I rewrite the Backbone Collection fetch() function to get a specific part in my JSON?
The json I get looks like this:
{
"success":true,
"data":[
{
"id":1,
"title":"asdf",
"link":"http://www.xx.cc/image/asdf/",
"date":1439993443000
},
{
"id":2,
"title":"qwer",
"link":"http://www.xx.cc/image/qwer/",
"date":1439993091000
}
]
}
And my simple Backbone Script:
var myCollection = Backbone.Collection.extend({
url: 'url.to/my/rest/'
});
myCollection.fetch();
console.log(myCollection);
the Problem here is, that the collection I get got a length of 1 with 1 model in in it. When I log that collection and open "n -> models -> 0 -> attributes -> data" I find all my objects within the JSON data array.
But I need the objects of tha "data"-array as models in my collection. So Far I found no solution to this problem so may someone got the right idea for me?
Supply your collection description object with a parse function, as described in the docs:
var myCollection = Backbone.Collection.extend({
url: 'url.to/my/rest/',
parse: function(data) {
return data.data;
}
});
This method extracts the relevant part from the server's response and fill a collection based on this part.

WebApi 2.1 + Backbone.js 1.1.2: sync everything at once

Disclaimer: I'm a WebApi/BackBone beginner, so the question might be a bit odd since there is a lot about these components I don't really know and/or understand.
It would be nice to have the possibility to issue just ONE sync() call to the server to synchronize everything. I mean, when I saw sync() method, at first I thought it's used like that, but as soon as I saw the "create", "update", "delete" params I realized it's not. But there is an underlying problem related to Backbones default implementation for DELETE.
I've learned that classic implementation of Backbone.js allows one deleted (destroyed) model at a time to be sync'ed to the server. Created/modified (POST/PUT operations) content is sent in the request body itself, so the JSON is filled with the data and deserialized by WebApi model binding on the server. It doesn't work like that for DELETE, since body is always empty and reference to the model is made by URL parameters in query string. So, I guess to achieve that functionality, the request for DELETE should be sent in body as well as for POST/PUT.
Is there a possibility to change all of this behavior AND make it work with WebApi? I googled for that stuff already, but can't find anything to point me to the right direction.
What I have until now is a Backbone model, collection and a view set up.
Backbone.sync("create", this.collection); is called by the view on button click.
On the server side there is a WebApi controller set up with scaffolded methods:
// GET
public IEnumerable<Ponuda> Get()
{
return _storageService.GetPonude().ToList();
}
// GET
public Ponuda Get(int id)
{
return (Ponuda)_storageService.GetPonuda(id);
}
// POST
public void Post([FromBody]IEnumerable<Ponuda> value)
{
_storageService.CreatePonude(value);
}
// PUT
public void Put([FromBody]IEnumerable<Ponuda> value)
{
_storageService.ModifyPonude(value);
}
// DELETE
public void Delete(IEnumerable<int> value)
{
_storageService.RemovePonude(value);
}
EDIT: I'm reading about Marionette.js and it seems to offer standard model/view related functionalities out of the box. However, I still can't see the possibility to save/sync e.g. the entire modified collection at once.
To sync all contents at once :
For POST and PUT Http methods, you can use Backbone.Sync API.
For DELETE, you can directly use the ajax API for deleting the content in the server and use Backbone Collection remove API to delete the content in the client side.
I have written skeleton code which demonstrates on how to achieve the functionality:
var PersonModel = Backbone.Model.extend({
url: '/demo',
defaults: {
"id": 0,
"name": "",
"age": 0
}
});
var PersonCollection = Backbone.Collection.extend({
url: '/demo',
model: PersonModel
});
var model1 = new PersonModel({"name": "John", "age": 30});
var model2 = new PersonModel({"name": "Joseph", "age": 30});
var collection = new PersonCollection();
// model will be added locally on client side. It will not sync to the server.
collection.add(model1);
collection.add(model2);
// POST. This will create both the models together using a single REST API request.
Backbone.sync('create', collection);
// PUT. This will update both the models together using a single REST API request.
Backbone.sync('update', collection);
// Extract the model ids to be deleted
var modelIds = [model1.get('id'), model2.get('id')];
$.ajax({
method: 'DELETE',
url: '/demo',
data: JSON.stringify(modelIds), // This will add ids to the request body
contentType: 'application/json',
success: function() {
// On successful deletion on server end, delete the models locally.
collection.remove([model1, model2]);
}
});
Regarding the WebApi, since I have not worked on it, will not be able to guide you. Having worked on Spring Rest API, I can tell you above functionality should work with the WebApi.

Problems with model.destroy()

Whenever I do a model.destroy() with Backbone, it tries to do a DELETE request on the following URL:
http://localhost:8000/api/v1/item/?format=json
Since I am doing a destroy on a model, I would expect that the ID of the model should be passed, so the URL to do a DELETE request on would be this:
http://localhost:8000/api/v1/item/8/?format=json
where the ID is specified. Because of the lack of ID, and my use of tastypie, all items get deleted. How do I make it so that the URL contains the ID of the item that I wish to delete?
I'm guessing that your model looks something like this:
var M = Backbone.Model.extend({
url: '/api/v1/item/?format=json',
// ...
});
The url for a model is supposed to be a function but, since it ends up going through the internal getValue function, a string will also "work"; if you check the source I linked to, you'll see why a string for url would give you the results you're seeing.
The solution is to use a function for url as you're supposed to:
url model.url()
Returns the relative URL where the model's resource would be located on the server. If your models are located somewhere else, override this method with the correct logic. Generates URLs of the form: "/[collection.url]/[id]", falling back to "/[urlRoot]/id" if the model is not part of a collection.
You'd probably want something like this:
url: function() {
if(this.isNew())
return '/api/v1/item/?format=json';
return '/api/v1/item/' + encodeURIComponent(this.id) + '/?format=json';
}
or this:
url: function() {
if(this.isNew())
return '/api/v1/item/?format=json';
return '/api/v1/item/' + encodeURIComponent(this.get('id')) + '/?format=json';
}

Categories