How to prevent OData service call on model change - odata

I have a sap.m.Table whose "items" are bound to oData v2 model. I need to delete item on click on delete icon. Here is what I do:
On click of delete icon, I get all the rows in the model, delete the one in question and set the property of model again.
However since the model is changed, it triggers a backend round trip and brings the latest data and table shows the original rows again.
I tried setting binding mode to OneTime but that does not work. Also tried setting RefreshAfterChange to false but even then service was called again.
Here is my code -
Controller
onInit: function() {
var oModel = new sap.ui.model.odata.v2.ODataModel("url", {
json: true,
useBatch : false,
refreshAfterChange: false,
defaultBindingMode: "OneTime"
});
this.getView.().setModel(oModel, "model1");
},
onDeleteIconPress : function(oEvent) {
// get the selected row
// get all the rows in oOriginalRows
// loop over oOriginalRows and delete the selected row from it
// set the model to reformed oOriginalRows
this.getView().getModel("omodel1").setProperty("/", oOriginalRows);
// Till this point every thing looks fine. I can see changes in the model
// refresh is called automatically and data service triggers backend call
// This fetches original data again and table shows all data again
}
How can I not trigger the round trip again? I need to update the locally

Your approach won't work with a ODataModel as it is strictly server side. Please use the corresponding remove method to delete an entity from the server.

Since Odata is server side model, it always triggered a round trip. So I did not bind my sap.m.Table to Data model. Instead I triggered a read manually. On success I copied the data received to local JSON model. I bound my table items to this JSON model. Now the delete button works just fine.
// Define a JSON Model
oJsonModel = new sap.ui.model.json.JSONModel();
//oModel is Odata model defined in manifest file
oModel.read("/entity1", {
success: function(oData, oResponse){
oJsonModel.setProperty("/entity1", oData.results);
// bind oJsonModel to table here
}
}

Related

Generic dashboard state on Thingsboard

I have a entity admin table that navigates to a dashboard state for a specific row on row click.
I want to have a default dashboard state and on row click populate the dashboard with the data of the device. This needs to be repeated for multiple devices.
How would I go about doing this?
You need to set the state entity. In your row click action, if you are using a simple logic one, be sure to tick the "Set entity from widget" checkbox.
Then in the target dashboard state, you need to include an entity alias as below.
Text desc of image:
Alias Name: state entity
Filter Type: Entity from dashboard state
Resolve as multiple: False
Others: None (leave empty)
This will mean that all widgets using this alias will dynamically change based on the current "state entity". You can change the state entity multiple ways, but I recommend to minimise the places that you do this. It can get complicated fast.
If you are not using a simple widget action, and instead using a custom widget action, you will need to use the following code to set the state entity.
function updateDashboardState(stateId, label) {
var params = {
entityId: entityId,
entityName: entityName,
entityLabel: label, // Optional
};
// Line below opens new state
widgetContext.stateController.openState(stateId,
params, false);
// Line below updates state
// widgetContext.stateController.updateState(stateId,
params, false);
}

Grid filled by a data provider: how to trigger an action after refreshment?

In Vaadin 8.2, I have a Grid bound to a bean using a data provider (AbstractBackEndDataProvider). Data is fetched from a DB, filters are applied:
Grid grid = new Grid<>();
grid.setDataProvider(dataProvider.withConfigurableFilter()); // dataProvider derives from AbstractBackEndDataProvider<T,F>
The essential flow is the following: user inputs an item id in a form and submits, a submit event listener gets the user input, creates a new filter and updates the data provider:
filterSubmitButton.addClickListener(event -> {
try {
ItemListFilter filter = new ItemListFilter(
itemFilter.getValue(), // itemFilter = new TextField();
);
filterBinder.writeBean(filter);
dataProvider.setFilter(filter);
} catch (ValidationException e) {
//...
}
});
When the data provider gets the filter updated it calls a service to fetch new items from DB with the filter applied (to the DB query). Vaadin takes care of refreshing the Grid with new data afterwards.
What I want is to have a callback at this last moment. Say an use case would be to check if a filtered fetched result set contains only one item, to select this item in the Grid (which in its turn will trigger an event showing item details in another pane). Or to select the first grid row after initial list is loaded
But the problem is that there is neither grid.addRefreshListener() nor dataProvider.addRefreshmentListener(). Any other listeners do not seem to apply in this case.
Thanks for any help in advance.
The only solution I've found is a trade-off.
The item list presenter (which handles the view with the grid) passes its com.vaadin.event.EventRouter to dataProvider (I've modified the dataProvider to hold an EventRounter as a member). And now instead of streaming DB results directly from the dataProvider I fire an event that the data is fetched (using EventRouter). The presenter can subscribe to this event and then delegate it to the presenter of the details panel. There you can read the fetched results (the event contains them), check if there's only one entry and open it by id.
public class ListItemDataProvider extends AbstractBackEndDataProvider<Item, ItemFilter> {
//...
#Override
protected Stream<Item> fetchFromBackEnd(Query<Item, ItemFilter> query) {
// ...
List<Item> fetchedResults = service.fetch(query.getOffset(), query.getLimit(), orderBy, getFilter(query));
eventRouter.fireEvent(new FilteredDataFetchedEvent(this, fetchedResults));
return fetchedResults.stream();
}
}
#Controller
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ItemListPresenter {
// ...
public void addFilteredDataFetchedListener(ItemListView.FilteredDataFetchedListener listener) {
eventRouter.addListener(FilteredDataFetchedEvent.class, listener, FilteredDataFetchedListener.FILTERED_DATA_FETCHED);
}
}
Few notes:
This is not exactly what I needed. Yes, I can do the most of my use case, if the filter is applied the list is reloaded and the details view gets the event to reload too. But I can't re-use the "selectionChanged" event listeners for this and actually can't select a row in the grid at all (just because the event from dataProdiver is thrown before the grid is updated).
This is sort of a dirty trick, because now the dataProvider throws events and deals with eventRouters of views/presenters. On the other hand Vaadins data providers anyway do allow to subscribe on events. Using that out-of-box grid-dataProvider reactive binding we just don't have an event fired after data is fetched, so we do it in a custom way.
What could also work is use the given Vaadin's subscriber dataProvider.addDataProviderListener and delegate from there an event containing the filled filter and just act independently catching that event in the details panel. But then you would need to execute sql queries twice (which can be costly) or cache them etc. This brings no benefits in comparison to the given and is still a trade-off.
When you invoke dataprovider.refreshAll(), the associated grid is automatially refreshed. Therefore, after following lines in your code:
filterBinder.writeBean(filter);
dataProvider.setFilter(filter);
add logic to get size of returned records (eg. dataprovider.size()) and if that equals one (01), invoke some other logic to select the one record and display its details in other panel.

Save operation using ODataModel

I am new to SAP ui5. I have successfully shown data using ODataModel to a table. Now I want to update the data in the table back into the database.
For this I know that we have something called 'update' function of the model in which we can specify the path and the data. My question here is what if I want to give the data in the form of json? How do I retrieve data in the table in the form of json and have it passed to the 'update' method?
Any help would be appreciated. If possible please share example such.
Thanks
You can use the OData model object to get it. Use the path to retrieve the JSON object, update what you need, and then call the update function. Here's an example:
getDataContext : function(oItem) {
var sPath = oItem.getBindingContextPath();
var oModel = this.getView().getModel();
return {
path : sPath,
data : oModel.getObject(sPath)
};
}
From there you can update the data in mDataContext.data as desired, then call the update:
this.getView().getModel().update(
mDataContext.path,
mDataContext.data,
{ // your context, success, error handlers etc }
)
Here's a fully working example that allows you to look at products in Northwind, select one, and increase or decrease that selected product's rating.

Adding related master/child entities (one to many) in breeze

I have a master file for a widget and there can be many versions of that widget that share the same widgetmaster. So there are widgetMasters and widgetVersions tables on the db.
widgetMaster ID is an identity integer field and has an icollection of widgetversions set.
Widgetversion has a foreign key pointing to the widgetMaster ID it belongs to.
I have a "create new widget" form on my site. This is loaded after a button is pressed and it uses "createEntity" to create blank entities for widgetMaster and widgetVersion. The idea is that the parent "widgetMaster" is created at the same time the version "001" is created. The master just contains the description and a few category fields. The version contains the specific fields relating to this version of the widget and there may be dozens of versions eventually.
The user fills in all the fields and presses "save".
At this point I validate the form fields and, if all is ok, move on to saving the entity via "datacontext.saveChanges()" This is done in the viewmodel for my "create new" form view.
This works fine when creating the widgetMaster, but I need to have more control of this process I think... I need to set the foreign key on the widgetVersion entity AFTER The id is created by "savechanges" but BEFORE it attempts to save the widgetVersion entity.
As "datacontext.saveChanges()" appears to be a one-stop shop I'm entirely baffled as to how I can save the widgetVersion entity with the newly-created ID from the widgetmaster I just saved.
Alrighty then. I can't say whether it's the best way of doing it, but here's how I accomplish it. Refer to this stackoverflow question for a bit more info: Breeze bug? It's trying to add related entity even though only the primary entity is specified in savechanges()
My viewmodel save method (on the form entry view that allows the user to populate the fields in the new entities) is now this:
var save = function () {
isSaving(true);
//1st save widgetMaster
return datacontext.saveChanges(new Array(newWidgetMaster())).then(function (saveResult) {
//set version entity to have master id
newWidgetVersion().widgetMasterID(newWidgetMaster().id());
return datacontext.saveChanges(new Array(newWidgetVersion())).fin(complete);
}).fail(function (e) {
//do something with the alert
});
function complete() {
isSaving(false);
}
};

Is there an event in Breezejs that is fired when an entity is added or deleted?

I have looked around for an answer to this, but I have come up dry so far. What I would like to do is have an event handler in a specific view model that listens to Breeze for entities being added or deleted so the view model can take appropriate action on the array it is managing. Does such an event exist?
I have a Jobs view model for my Jobs view that contains, among other properties, a ko.observableArray of Job entities, and a NewJob view model for my NewJob view. Both view models share the same data service. I would like to simply use the DataService from the NewJob view model to insert a new Job entity in to Breeze, and then have the Jobs view model simply subscribed to an event so it would know to add the new Job into it's Jobs array.
TIA
The Breeze EntityManager has an entityChanged event that may be used like so:
var em = new EntityManager( {serviceName: "api/NorthwindIBModel" });
em.entityChanged.subscribe(function(changeArgs) {
// This code will be executed any time any entity within the entityManager is added,
// modified, deleted or detached for any reason.
var action = changeArgs.entityAction;
var entity = changeArgs.entity;
// .. do something to this entity when it is changed.
});

Resources