NavigationListItem & Sort - sapui5

I tried to sort a NavigationListItem on a field which is different from the keyfield within the onInit method of the view controller w/o any success.
Note, there is no orderby implementation on the server side.
My xml view which doesn't implement any sorter since orderby isn't implemented: https://openui5.hana.ondemand.com/#/api/sap.tnt.NavigationListItem
<tnt:NavigationList id="navigationList">
<tnt:NavigationListItem icon="sap-icon://"
expanded="{expanded}"
key="{MY_KEY}"
items="{/MY_PATH}">
<tnt:NavigationListItem text="{MY_CONTENT}" key="{MY_KEY}"></tnt:NavigationListItem>
</tnt:NavigationListItem>
</tnt:NavigationList>
Here is an attempt to add a sorter that presently doesn't work. First, because return items correspond to the first level of NavigationListItem and secondly probably because of a wrong approach.
var oNavigationList = this.getView().byId('navigationList');
var aSorter = [];
aSorter[0] = new sap.ui.model.Sorter('MY_FIELD', true);
var oItems = oNavigationList.getItems();
oItems.sort("items", {path: "/MY_PATH", sorter: aSorter});
Could you please advice on the appropriate approach?

Since your List is bound to ODATA Model, all the sorting will happen at server side. Every time you say, sort, a new request is sent to server. Now, as you said, the server is not supporting orderby, I suggest the following approach :
Do oDataModel.read and store the data in a JSON Model. oData Read API
Apply the sorting in XML using below code:
<tnt:NavigationList id="navigationList">
<tnt:NavigationListItem icon="sap-icon://"
expanded="{expanded}" key="{MY_KEY}"
items="{
path : '/MY_PATH',
sorter: {
path: 'MY_CONTENT',
descending: false,
group: false
},
}">
<tnt:NavigationListItem text="{MY_CONTENT}"
key="{MY_KEY}"></tnt:NavigationListItem>
</tnt:NavigationListItem>
</tnt:NavigationList>
Let me know if you need more information.

Related

API reference: Where to find all the properties available for aggregation binding info object?

Where can I find aggregation binding properties like path, factory, etc.. in the API reference?
Just like in following code
<List items="{
path: '/Products',
factory: '.productListFactory',
...
}">
Where to find more such properties and their descriptions?
You can see more such properties in ManagedObject#bindAggregation.
Additionally, parameters and events await key-value pairs of which the possible keys are documented in the corresponding model type.
For example, the keys for parameters, when binding with a v2.ODataModel, can be found in ODataModel#bindList or in ODataListBinding. The latter module also documents which events can be registered.
{ // Standard options. See ManagedObject bindAggregation
path: '...',
filters: [/*...*/],
...
parameters: { // Model dependent. See e.g. ODataListBinding constructor
expand: 'ToA, ToB/ToB1/ToB2, ToC',
select: 'ProductID, ProductName',
custom: 'myCustomQuery',
batchGroupId: 'myGroup',
},
events: { // Model dependent. See e.g. ODataListBinding events
dataRequested: '.onDataRequested',
dataRequested: '.onDataReceived',
change: '.onDataChange'
}
}
That's a binding (they're enclosed in {}).
You can use the getBindingInfo method.
So, for example, if your List control has the id "mylist":
this.getView().byId("mylist").getBindingInfo("items");
It will return you an object containing all the binding properties, such as path and factory.

Filter expanded OData request

I need to pass a parameter to the backend. I want to use $filter for this.
My code below is not working, there are no filters returned by io_tech_request_context->get_filter( )->get_filter_select_options( ) in the Figures entityset.
var aFilters = [ new sap.ui.model.Filter("TeamMembers/Figures/FILTERKEY", sap.ui.model.FilterOperator.EQ, sFilterValue) ];
this.getView().bindElement({
path: "/TeamDataSet('0')",
parameters: {
expand: 'TeamMembers/Figures'
},
filters: aFilters
});
Element binding / ContextBinding doesn't support the property filters since you're binding just a single entity and not a collection. If you need to filter Figures, you'll have to bind them to an aggregation (e.g. items on List), get the corresponding ListBinding object, and then call .filter from there.
Here is a similar example: https://embed.plnkr.co/AoIZI4/. Take a look at the handler onSearch.
In case someone actually wants to filter a list by expanded results, the navigation property can be applied in the Filter path which then gets resolved by the path of the bound binding object. For example:
<List items="{
path: 'myODataModel>/Products',
parameters: {
expand: 'ToSupplier'
}
}">
Then in Controller, filter Products by their supplier country:
new Filter({
path: "ToSupplier/Country",
operator: "EQ",
value1: "UK",
})
(From: https://embed.plnkr.co/wAlrHB/).
Be aware that this works only if expanding does not result in a collection but a single object (e.g. associated supplier). Filtering by looking up at an associated collection is supported as of OData V4 which can be achieved via FilterOperator.All / .Any.

What is use of 'parameters' option in binding object?

In the code below, what is the significance of parameters option and select option within it?
I have looked into API but to avail nothing.
<List id="list"
items="{
path: '/PurchaseOrders',
sorter: [{
path: 'ChangedAt',
descending: true
}, {
path: 'POId',
descending: false
}],
parameters: {
select: 'POId,OrderedByName,SupplierName,GrossAmount,CurrencyCode,ChangedAt,ItemCount'
}
}">
...
</List>
With the select property you can tell the server to return just a subset of entity properties. This is useful if you have large entities with a lot of properties, but want to display only some of them, e.g. in a table. This works only if you are using an ODataModel and thus an ODataListBinding.
In general the parameters argument is used to pass implementation specific parameters to the binding while having a common API for all model implementations, i.e. all implementations of a ListBinding have the following signature:
oModel (the model which holds the data)
sPath (the binding path)
oContext (the binding context)
aSorters (sorters to be applied)
aFilters (filters to be applied)
mParameters (additional parameters which depend on the model implementation and are not necessarily supported by all of them)

Multiple entry select2 and angular model fetched by $resource

I am having some difficulty figuring out how to make it all work together. Here is what I would like to do:
The model is fetched using $resource from the rest API:
var itemResource = $resource('http://blabla.com/items/:id');
$scope.item = itemResource.get({id: '12345'});
The returned item has some fields among which is one array field that lists the ids of the categories:
{
"item_name: "some value",
"categories": ["cat_id1", "cat_id7", "cat_id8"]
}
In the UI I want these categories to be shown as editable multi select. The user should not operate using ids, but rather he should see and be able to chose string representations which come from the mapping within the application. So in html:
<input type"text" ui-select2="categoryOptions" ng-model="item.categories" />
and also in controller:
var categoryMapping = [
{id: "cat_id1", text: "CategoryAlpha"},
...
{id: "cat_id8", text: "CategoryOmega"},
...
];
$scope.categoryOptions = {
'multiple': true,
'placeholder': 'Chose categories',
'width': 'element',
'data': categoryMapping,
};
Obviously the pieces of code above are not working and I don't know how to make them work to do what I want. ui-select2 wants the model (item.categories) to be an array of objects {id, text} and I want it to store only the ids in the items in the database and have the mapping separate. I can't be the first one to do it, there must be a solution, please help.
Thanks

How to design a REST search with backbone

I'm designing an API and also consuming it with Backbone.js. Part of the API will include search operations. For example when searching for cars, I might have something like:
http://api.mysite.com/search/cars?q=volvo
With backbone, I can see two options for consuming the results.
Option 1: A search is a Collection
var CarSearch = Backbone.Collection.extend({
model: Car,
initialize : function(models, options){
this.query = options.query;
},
url: function(){
return "http://api.mysite.com/search/cars?q="+this.query;
}
});
var volvos = new CarSearch([], {query:'volvo'});
volvos.fetch();
Option 2: A search is a Model, and the results are a Collection
var CarSearchResults = Backbone.Collection.extend({
model: Car
});
var CarSearch = Backbone.Model.extend({
defaults: {
"query":"",
"carSearchResults":null
},
url: function(){
return "http://api.mysite.com/search/cars?q="+this.get('query');
},
parse: function(resp,xhr){
resp.carSearchResults = new CarSearchResults(resp.carSearchResults);
return resp;
}
});
var volvoSearch = new CarSearch();
volvoSearch.set({query:'volvo'});
volvoSearch.save();
What are the advantages / disadvantages of these options? Is there a backbone-y way of designing this?
I'm leaning towards option 2 because it seems easier to add things to the response like pagination details, or a next url. But option 2 seems messier in a couple of ways. For example, would I generate an ID on the server for the search model when it is saved? Don't think I need to get that model by ID, deleting or updating it doesn't really make sense either cause I'm not persisting it.
i dont know if its a good practice,
but i use for my search the "data" option in the "fetch" method.
https://stackoverflow.com/a/6659501/1067061
Maybe it helps.
Good Luck!
EDIT
This is the right way to pass query parameters in your collections url,
The reference to the Docs shows how to pass the data attribute in fetch options, the data attribute is actually an object with key value pairs referring to query params and their values
I would go with option one. At least imo a model should correspond to a single search result and the collection to the entire set of search results. so if you search for volvo and there are 6 items returned, each item should be a model contained within your collection.
Now this will largely depend on how you are representing a result on your server. If say for instance you have car instances then you just do the search server side using the query and return the resulting objects as json. Then you can have the returned list be the collection of car models that match the criteria. but if you are planning on returning the query results some other way then you will have to think about how the model should represent the data
I would recommend using a collection, like in option 1, but without the need to define a new collection just for the search.
Take a look at my blog post about this here: http://willdemaine.ghost.io/restful-search-with-backbone/
var SearchableCollection = Backbone.Collection.extend({},{
search: function(query, options){
var search = $.Deferred();
options = options || {};
var collection = new this([], options);
collection.url = _.result(collection, 'url') + 'search?q=' + query;
var fetch = collection.fetch();
fetch.done(_.bind(function(){
Backbone.Events.trigger('search:done');
search.resolveWith(this, [collection]);
}, this));
fetch.fail(function(){
Backbone.Events.trigger('search:fail');
search.reject();
});
return search.promise();
}
});
Then you can do:
var Cars = SearchableCollection.extend({});
var findCars = Cars.search('volvo');
findCars.done(function(cars){
var carsView = new CarsView({
collection: cars
});
carsView.render();
});