Chain HTTP calls - http

I have a REST API that returns an array of appointments. For each item in this array I want to make an single call against another API to get more Information for the item.
this.httpClient.get(this.serverUrl, this.httpOptions.RequestOptions)
.map(res => res.json())
.map((items: Array<any>) => {
let list: Array<MettAppointmentModel> = [];
if (items) {
items.forEach(item => {
let model = new MettAppointmentModel();
model.Created = item.created;
model.CreatedBy = item.createdBy;
model.Date = item.date;
model.Id = item._id;
model.participated = this.httpClient.get(this.serverUrl + model.Id, this.httpOptions).map(response => return response.json());
list.push(model);
});
}
return list;
} );
I don't know how to get another call chained in this call
model.participated = this.httpClient.get(this.serverUrl + model.Id, this.httpOptions).map(response => return response.json());

Related

WhenAnyValue is not getting called by RaisePropertyChanged

I am new to ReactiveUI along with DynamicData
Declaration
ReadOnlyObservableCollection<Employee> itemSource;
public ReadOnlyObservableCollection<Employee> ItemSource
{
get => itemSource;
}
SourceList<Employee> Employees = new SourceList<Employee>();
Implementation
Employees
.Connect()
.Sort(SortExpressionComparer<Employee>.Ascending(emp => emp.ID))
.Bind(out itemSource)
.ObserveOn(RxApp.MainThreadScheduler)
.Do(_ =>
{
this.RaisePropertyChanged(nameof(ItemSource));
})
.DisposeMany()
.Subscribe()
.DisposeWith(disposable);
this.WhenAnyValue(x => x.itemSource)
.Do(_ =>
{
Debug.Print("Called");
});
But whenever I am calling
Employees.Add(new Employee
{
Name = "Hello" + DateTime.Now.ToShortDateString(),
ID = random.Next(1,1000)
});
I expect it to print "Called" in debug window as this.WhenAnyValue should be called, but apparently this is not the case.
Could you please help me to understand if I am doing any mistake?
Thanks Glen. I was able to fix it by using below code
this.WhenAnyValue(x => x.itemSource.Count)
.Do(_ =>
{
Debug.Print("Called");
});

How do I combine multiple http requests and merge results

I need to handle a situation where I have 3 endpoints to call and would like to get the data in the most convenient/efficient way. The first call can be handled independently and returns a single result. The second endpoint returns a collection but will need to initiate 0-* subsequent calls, where a given key is present.
Ideally would like to receive the collection (from the 2nd endpoint call) as a mutated/new collection that includes the result from the 3rd endpoint call.
I am currently using forkJoin(observableA$, observableB$) to handle the first 2 calls in parallel but I cannot work out how to include the sequential calls and have the data included in observableB$
//Customer observable
const customer$ = this._customerManagementService.getCustomer(
accountNumber
);
return forkJoin({
customer: customer$,
saleCycles: saleCyclesWithVehicle$
}).pipe(finalize(() => this._loaderFactoryService.hide()));
getSalesWithVehicle(accountNumber: string, dealerKey: string) {
return this._salesCycleService
.getCyclesForCustomer({
customerNumber: accountNumber,
dealerKey: dealerKey
})
.pipe(
concatMap((results: ISaleCycle[]) => {
return results.map(cycle => {
return this._purchaseVehicleService.getPurchaseVehicle(
cycle.vehicleKey
);
});
})
);
}
I expect the collection to include further data as a new property on the original collection
UPDATE
After a bit more thought maybe I should be using reduce somewhere in the solution. This way I can be in control of what's getting push into the array and it could be dynamic?
getSalesWithVehicle(accountNumber: string, dealerKey: string) {
return this._salesCycleService
.getCyclesForCustomer({
customerNumber: accountNumber,
dealerKey: dealerKey
})
.pipe(
switchMap((results: ISaleCycle[]) => {
return results.map(cycle => {
if (cycle.vehicleKey) {
return this._purchaseVehicleService
.getPurchaseVehicle(cycle.vehicleKey)
.pipe(
reduce((acc, vehicle) => {
return { cycle: cycle, vehicle: vehicle };
}, []),
toArray()
);
}
else {
///No extra data to be had
}
});
}),
concatAll()
);
}
I would use concatMap() to merge the responses of HTTP requests 2 and 3.
import { of } from 'rxjs';
import { map, concatMap } from 'rxjs/operators';
const pretendGetCustomer = of({accountNumber: 123, name:"John Doe"});
const pretendGetVehiculeHttpRequest = (customerNumber) => {
return of([{custNum: 123, vehicleId:"2"}, {custNum: 123, vehicleId:"1"}]);
}
const pretendGetCyclesHttpRequest = (cycleIds) => {
return of([{id:"1", name:"yellow bike", retailPrice:"$10"}, {id:"2", name:"red bike", retailPrice:"$20"}]);
}
const yourFunction = () => {
pretendGetCustomer.subscribe(customer => {
// Assuming you do other things here with cust, reason why we are subscribing to this separately
// isHappy(customer)
// Your second & third calls
pretendGetVehiculeHttpRequest(customer.accountNumber).pipe(
// Need to use concatMap() to subscribe to new stream
// Note: use mergeMap() if you don't need the 1st stream to be completed
// before calling the rest
concatMap(purchases => {
const cyclesIds = purchases.map(p => p.vehicleId);
// concatMap() requires an Observable in return
return pretendGetCyclesHttpRequest(cyclesIds).pipe(
// Use map() here because we just need to use the data,
// don't need to subscribe to another stream
map(cycles=>{
// Retrun whatever object you need in your subscription
return {
customerNumber: customer.accountNumber,
customerName: customer.name,
purchases: purchases.map(p => cycles.find(c => p.vehicleId === c.id))
}
})
);
})
).subscribe(resultof2and3 => {
// Do something with the new/mutated Object which is a result of
// your HTTP calls #2 and #3
console.log(resultof2and3);
});
});
}
yourFunction();
I made a stackblitz if you want to see the above run (see console): https://stackblitz.com/edit/rxjs-nqi7f1
This is the solution I eventually came up with. I've taken the advice from BoDeX and used concatMap(). In my mind it was clear that I wanted to use forkJoin and be able to reference the results by object key, I.e customer or saleCycles.
In the scenario where a vehicleKey was present I needed to return the results in a defined data structure, using map(). Likewise, if no vehicle was found then I just needed the outer observable.
const customer$ = this._customerManagementService.getCustomer(accountNumber);
const saleCyclesWithVehicle$ = this.getSalesWithVehicle(accountNumber,dealerKey);
getSalesWithVehicle(accountNumber: string, dealerKey: string) {
return this._salesCycleService
.getCyclesForCustomer({
customerNumber: accountNumber,
dealerKey: dealerKey
})
.pipe(
concatMap(cycles => {
return from(cycles).pipe(
concatMap((cycle: ISaleCycle) => {
if (cycle.vehicleKey) {
return this._purchaseVehicleService
.getPurchaseVehicle(cycle.vehicleKey)
.pipe(
map(vehicle => {
return { cycle: cycle, vehicle: vehicle };
})
);
} else {
return of({ cycle: cycle });
}
}),
toArray()
);
})
);
}
return forkJoin({
customer: customer$,
saleCycles: saleCyclesWithVehicle$
}).pipe(finalize(() => this._loaderFactoryService.hide()));

Dispatch an action after another is executed in Effect

I Have the following function in my code
private loadDrones() {
this.store$.dispatch(new UsedDronesStoreActions.GetUsedDronesRequest({organizations_id : environment.organizationId}));
this.store$.pipe(select(UsedDronesStoreSelectors.selectAll)).subscribe((drones) => {
drones.forEach((drone) => {
if (this.drones.has(drone.id)) { return; }
this.drones.set(drone.id, drone);
this.store$.dispatch(new UsedDronesStoreActions.OpenUsedDroneUpdatePositionChannelRequest({ droneId: drone.id, projectId : environment.projectId }));
this.store$.dispatch(new UsedDronesStoreActions.OpenUsedDroneUpdateStatusChannelRequest({ droneId: drone.id, projectId : environment.projectId }));
});
});
}
I would like to move this function into an INIT Action in my effect.
Using
#Effect()
init$ = this.actions$.pipe(
ofType(ROOT_EFFECTS_INIT),
map(action => ...)
);
My question is, , the actual function load a list, and then thank to the result of that list, dispatch a serie of actions for each element of the list.
Is it possible to make this inside a single effect ? Or do I have to split it up in 2 different actions ?
https://medium.com/#amcdnl/dispatching-multiple-actions-from-ngrx-effects-c1447ceb6b22
#Effect() save = this.update$.pipe(
map(action => action.payload),
switchMap(payload => this.myService.save(payload)),
switchMap(res => [
new Notification('save success'),
new SaveSuccess(res)
])
);

Reference members in Moq lambda

I have a function in a class that returns a value based on the state of a class property. In this example, I want HasName() to return true if Name is not null. I could simply do Returns(false), however I want it evaluate as a lambda so that it works properly if Name is modified during the test.
public interface IThing
{
string Name { get; set; }
bool HasName();
}
var mocks = new Dictionary<string, IThing>();
Mock<IThing> mockThing;
mockThing = new Mock<IThing>();
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mockThing.Object.Name != null;
});
mocks["first"] = mockThing.Object;
mockThing = new Mock<IThing>();
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mockThing.Object.Name != null;
});
mocks["second"] = mockThing.Object;
Console.WriteLine(mocks["first"].HasName());
mocks["first"].Name = null;
Console.WriteLine(mocks["first"].HasName());
The 2nd Console.WriteLine prints true instead of false due to scoping (referencing the 2nd mock). Resharper actually complains of "Access to modified closure". What is the correct way to do this?
Although your design a little bit strange but you can access the generated mock object with mockThing.Object in your Setup function:
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mockThing.Object.Name != null;
});
var thing = mockThing.Object;
var hasName = thing.HasName(); // true because Name returns "test"
thing.Name = null;
hasName = thing.HasName(); // false
The problem is that you are referencing the mockThing with your lambdas and then you are reasigning it. So both setup will end up using the same instance.
Use the mocks from the dictionary and it will work:
var mocks = new Dictionary<string, IThing>();
Mock<IThing> mockThing;
mockThing = new Mock<IThing>();
mocks["first"] = mockThing.Object;
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mocks["first"].Name != null;
});
mockThing = new Mock<IThing>();
mocks["second"] = mockThing.Object;
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mocks["second"].Name != null;
});
Console.WriteLine(mocks["first"].HasName());
mocks["first"].Name = null;
Console.WriteLine(mocks["first"].HasName());

how to get selected row value in the KendoUI

I have a kendoUI grid.
#(Html.Kendo().Grid<EntityVM>()
.Name("EntitesGrid")
.HtmlAttributes(new { style = "height:750px;width:100%;scrollbar-face-color: #eff7fc;" })
.Columns(columns =>
{
columns.Bound(e => e.Id).Hidden().IncludeInMenu(false);
columns.Bound(e => e.EntityVersionId).Hidden().IncludeInMenu(false);
columns.Bound(e => e.Name).Width("70%").Title("Entity Name");
columns.Bound(e => e.EIN).Width("30%");
})
.ToolBar(toolBar => toolBar.Template("<a class='k-button k-button-icontext k-grid-add' id='addEntity'><span class='k-icon k-add'></span>Entity</a>" +
"<a class='k-button k-button-icontext' id='editEntity'><span class='k-icon k-edit'></span>Edit</a>"))
.DataSource(dataSource => dataSource
.Ajax().ServerOperation(false)
.Model(model => model.Id(e => e.Id))
.Read(read => read.Action("GetEntities", "Entity", new { projectId = Request.QueryString[DataKeyNameConstants.ProjectId] })))
.Sortable()
.Scrollable()
.Filterable()
.Resizable(resize => resize.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.ColumnMenu()
.Selectable(s => s.Mode(GridSelectionMode.Multiple))
.Events(events => events.Change("entSelChange"))
)
now, I need to get the value of EntityVersionId from the selected Row. but not sure how to do it.
here's my javascript function
$("#editEntity").click(function () {
var entityGrid = $("#EntitesGrid").data("kendoGrid");
// what should I do from here
});
UPDATE: add code to loop all rows.
function loadPreviousEntityVersion() {
alert("sdfsdfsdf");
var entityGrid = $("#EntitesGrid").data("kendoGrid");
var data = entityGrid.dataSource.data();
for(var i = 0; i<data.length; i++) {
var currentDataItem = data[i];
alert(dataItem.EntityVersionId);
}
}
One way is to use the Grid's select() and dataItem() methods.
In single selection case, select() will return a single row which can be passed to dataItem()
var entityGrid = $("#EntitesGrid").data("kendoGrid");
var selectedItem = entityGrid.dataItem(entityGrid.select());
// selectedItem has EntityVersionId and the rest of your model
For multiple row selection select() will return an array of rows. You can then iterate through the array and the individual rows can be passed into the grid's dataItem().
var entityGrid = $("#EntitesGrid").data("kendoGrid");
var rows = entityGrid.select();
rows.each(function(index, row) {
var selectedItem = entityGrid.dataItem(row);
// selectedItem has EntityVersionId and the rest of your model
});
There is better way. I'm using it in pages where I'm using kendo angularJS directives and grids has'nt IDs...
change: function (e) {
var selectedDataItem = e != null ? e.sender.dataItem(e.sender.select()) : null;
}
I think it needs to be checked if any row is selected or not?
The below code would check it:
var entityGrid = $("#EntitesGrid").data("kendoGrid");
var selectedItem = entityGrid.dataItem(entityGrid.select());
if (selectedItem != undefined)
alert("The Row Is SELECTED");
else
alert("NO Row Is SELECTED")
If you want to select particular element use below code
var gridRowData = $("<your grid name>").data("kendoGrid");
var selectedItem = gridRowData.dataItem(gridRowData.select());
var quote = selectedItem["<column name>"];

Resources