Devextreme same lookup in two columns, how to avoid making the request twice? - devextreme-angular

I have two columns that have the same lookup, they send 2 requests to the same place in the API, how to call it only once and use it for both columns?

You could make a http request with your service to API and cache the response in new ArrayStore (wrapped in dataSource object)
this.lookupsDataSource = new DataSource({
store: new ArrayStore({
data: //yourService.retrievedData
key: "Id",
})
});
Then you could redeclare grids columns (if you use that configuration) or you could re-set datasource of both lookups to this new arrayStore)

Related

How to add one item from observable to an array of items in a different observable?

I am struggling with using observables and pipes where I want to add one item from one observable to another observable containing a list of items of the same type.
I have type X. And of the type X I have an observable array:
readonly arrayOfx$: Observable<X[]>;
I also have an observable of only the the type X:
private readonly _x$: Observable<UpdateOfX>;
interface UpdateOfX {
x: X,
updateState: "Add" | "Modified" | "Removed"
}
All this code is in a Service Class where the service should only expose the array of X. The data in the array I want to show in my html with async piping and this part of the functionality works. The host and the client are connected with the signalR technique and onConnected, an array of items of type X is retrieved. But as the application runs, in the backend new items of type X can be created, existing items can be changed or can be removed and when this occures, only this item will be send via the signalR connection and the modification state.
In the front end, this item must be added to the already retrieved array of items of type X. In the service, the pipe technique is used and my question is, how do I add the single item I get in a later moment to the list of items I retrieved earlier?
constructor() {
this.arrayOfx$ = this._someSignalRHelperService.retrieveMultipleItems$.pipe(
tap((xArray: X[]) => console.log(xArray)),
//can I somehow get the a later created x from the server here...
);
this._x$ = this._someSignalRHelperService.retrieveOneItem$.pipe(
tap((updateOfX: UpdateOfX) => console.log(updateOfX)),
map((updateOfX: UpdateOfX) => {
//process the updateState
//... or must I do something here to get x into x[]?
})
);
}
Since SignalR is used, the backend is in control when the client receives a new item of type X when there is one created.
You can use combineLatest(), and do whatever manipulation you want as soon as your receive the two emissions:
constructor() {
this.combinedOfX$ = combineLatest(
this._someSignalRHelperService.retrieveMultipleItems$,
this._someSignalRHelperService.retrieveOneItem$
).pipe(
map(([singleOfX, multipleOfX]) => {
// do your adding or mapping and whatever here.
})
)
}
Not sure if that is by design but the problem with having the frontend (client) to deal with the data is not so ideal. Your single source of truth is now based on your client, which different machines have different processing speed and may cause nuances and inconsistent displays. Also, the code will be messy in a sense that you will now need to check through the entire arrayOfX every time a singleOfX gets updated - you need to check if it exists in the current list, if yes, edit/delete; else, append to the list. What if the user refreshes his browser accidentally? You lost all of your processing.
Since you are already using SignalR, it would be more advisable that you let the server handle all the data, and let the server be single source of truth. Then you will just need to subscribe to one hub and listen to the changes of the arrayOfX; and pretty much don't care of the single updates.

How to search multiple api services

I am developing a search engine with angular 2.
Therefore I use APIs from multiple platforms.
It works if I call the search function from every api service manually.
But is it possible to do the same foreach api service?
Every api service has the same function:
search (query: string): Observable<Array<SearchResult>> { ... }
In the UI I want to separate the results by tabs.
Therefore every api service has a title:
public title: string = "the title";
For storing the search results locally I have a class that is extended by every api service. This class has helper functions etc.
Depending on the behaviour you need you can use merge, concat or forkJoin to merge multiple streams into one.
The code would look pretty much the same.
For example using merge in order to merge 2 streams into one.
If you have a list of apis you need to call for the search. Your code would look like this.
let apis: string[] = [];
let observables = apis.map(api => search(api)); // get an array of observables
let merged = observables.reduce((previous, current) => previous.merge(current), new EmptyObservable()); // merge all obserbable in the list into one.
merged.subscribe(res => doSomething(res));
This article might be helpful.

dojo 1.10 JsonRest idAttribute - server passed a float in PUT

Just getting started with dojo/JsonRest, but having some problems with sending updates back to my server. I've got 2 questions that I'm stuck with;
The code below produces a grid with one of the columns set to editable.
The primary key in my json data is the "jobName" attribute (hence idAttribute in the JsonRest store).
First question is about the URI in the PUT;
- When I call dataStore.save() the server get's a PUT, but the URI is /myrestservice/Jobs/0.9877865987 (it changes each time, but is always a float)
- I don't see where dojo is getting the float number from? It's not my idAttribute value from that row. How can I get the PUT to respect the idAttribute in the JsonRest store?
- I did try setting idProperty in the MemoryStore to "jobName", but that changed the PUT in to a POST and removed the float, but I still don't get a jobName in the URI which is what my REST server needs.
Second question about the content of the PUT;
- The PUT contains the whole row. I'd really just like the idAttribute and the data that changed - is that possible?
I've been through the examples and docs, but there aren't many examples of handling the PUT/POST part of JsonRest.
Thanks
var userMemoryStore = new dojo.store.Memory( );
var userJsonRestStore = new dojo.store.JsonRest({target:"/myrestservice/Jobs/", idAttribute:"jobName"});
var jsonStore = new dojo.store.Cache(userJsonRestStore, userMemoryStore);
var dataStore = new dojo.data.ObjectStore( {objectStore: jsonStore } );
/*create a new grid*/
var grid = new dojox.grid.DataGrid({
id: 'grid'
,store: dataStore
,structure: layout
,rowSelector: '20px'}
,"gridDiv");
grid.startup();
dojo.query("#save").onclick(function() {
dataStore.save();
});
I think you want idProperty, not idAttribute. It also might help to set idProperty in the Memory store being used to cache as well; that may be what's generating the random float.
As for the second question, that'd probably require customization; I don't believe OOTB stores (or grids) generally expect to send partial items.

Modify store request in ExtJS4?

I had some issues with the structure of requests, which the tree-store sent. But I could modify it with an beforeload listener.
store.on( 'beforeload', function( s, o ) {
if( o.params.parent === 'root' ) delete o.params.parent;
});
In the normal store, the operation argument doesn't contain a params attribute somehow...
Problem is, the server always want a sortby and sortorder parameter, but
the store has
just a sort parameter (which contains the sortby and sortorder)
just sends this parameter when I sort a table and not at begining (if I put it in extra params of the proxy, this always overrides the store sort parameter)
So I want to split sort up into sortby and sortorder and send those params every time the store requests something.
The first problem I got by with 3 config parameters for the proxy:
simpleSortMode: true
sortParam : 'sortby'
directionParam: 'sortorder'
which split the
.../?sort=[[{property:'visits', direction: 'DESC'}]]&...
into
.../?sortby=visits&sortorder=DESC&...
The second problem, that the store didn't send the sort parameters from the beginning I solved with the sort() method of the store.
...
constructor: function( config ) {
this.callParent([config]);
this.sort( config.sortby, config.sortorder );
}
...
I simply used it to init the store with default sorting, before it even loaded data from the server. This caused it to send the sort-params with the first request.

Retrieving data from WCF service

Let's say I have database with two tables - Groups and Items.
Table Groups has only two columns: Id and Name.
Table Items has three columns: Id, GroupId and Name.
As you can see, there is one-to-many relation between Groups and Items.
I'm trying to build a web service using WCF and LINQ. I've added new LINQ to SQL classes file, and I've imported these two tables. Visual Studio has automatically generated proper classes for me.
After that, I've create simple client for the service, just to check if everything is working. After I call GetAllGroups() method, I get all groups from Groups table. But their property Items is always null.
So my question is - is there a way to force WCF to return whole class (whole Group class and all Items that belong to it)? Or is this the way it should behave?
EDIT: This is function inside WCF Service that returns all Groups:
public List<Group> GetAllGroups()
{
List<Group> groups = (from r in db.Groups select r).ToList();
return groups;
}
I've checked while debugging and every Group object inside GetAllGroups() function has it's items, but after client receives them - every Items property is set to null.
Most likely, you're experiencing the default "lazy-loading" behavior of Linq-to-SQL. When you debug and look at the .Items collection - that causes the items to be loaded. This doesn't happen however when your service code runs normally.
You can however enforce "eager-loading" of those items - try something like this:
(see Using DataLoadOptions to Control Deferred Loading or LINQ to SQL, Lazy Loading and Prefetching for more details)
public List<Group> GetAllGroups()
{
// this line should really be where you *instantiate* your "db" context!
db.DeferredLoadingEnabled = false;
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Group>(g => g.Items);
db.LoadOptions = dlo;
List<Group> groups = (from r in db.Groups select r).ToList();
return groups;
}
Do you get the Items collection populated now, when you call your service?