Mapping a Stream<List> to another type is returning a Stream<Null> - dart

I'm trying to transform a Stream of a list of one type into a Stream of a list of another type, and having an issue with this.
I have this list of Habits that I'm streaming from Firebase, and I want to accept that stream in a function, and return a new stream that is a list of ViewModels of another type from it. But my function is returning a stream of the wrong type.
Here is my code:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) => completion.date
.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
I am getting a compile error because the result variable is showing as type Stream<Null> when I hover over it, where I would expect it to be Stream<List<HabitCompletionViewModel>>. Any idea what I'm doing wrong?

Your outer .map call does not have a return statement which is why you are getting a Stream<Null>.
So add a return statement like so:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
// added return statement here
return habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
However the above code still has an error because it is now returning a Stream<List<Future<HabitCompletionViewModel>>> instead of the desired Stream<List<HabitCompletionViewModel>>. To solve this you can use .asyncMap instead of .map.
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.asyncMap((habitsList) {
return Stream.fromIterable(habitsList).asyncMap(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}

Related

Adding key value pair to object inside forEach loop

I'm trying to add a thenable result to an object using a forEach loop. I can see the entries when I console.log the results, but when I try to use them for other parts of my code, I get an empty object.
I was getting an error previously telling me that the object's name (results) was not defined. I moved the object outside of the function and now I just get an empty object returned when I try to return the values of the object.
I tried this first:
let results = {};
// Check for all videos in cache (returns [])
const findAllVidsInCache = (videoArray) => {
videoArray.forEach(video => {
check(video).then(res => {
// resultsArray.push(res);
results[video] = res;
return results;
});
});
return results;
}
Then I tried this:
let results = {};
// Check for all videos in cache (returns [])
const findAllVidsInCache = (videoArray) => {
videoArray.forEach(video => {
check(video).then(res => {
// resultsArray.push(res);
results[video] = res;
return results;
});
});
let values = Object.values(results);
return values;
}
But I still keep getting an empty object when the function is called (I'm using devTools to call the function so nothing else should be interfering with it).
What I'm looking for, and what I can see in the console when I log it to the console, is an object that appears like so:
'video1': false,
'video2': false,
'video3': false,
'video4': true,
'video5': false,
...
Up to 12 videos.
Any ideas what I'm doing wrong here?
Try reading about promise handling. return results; returns a promise.

Return value from subscribe in Ionic

So I want to return a value from a subscribe function like this:
async obtenerListadoClases(categoria) {
var clasesDB = await this.getClases(categoria)
.subscribe((data: any) => {
clasesDB = data.clasesDB // **Want to return this**
console.log(clasesDB,'clasesDB'); // **Getting Value**
})
console.log(clasesDB, 'outside'); // **Not Getting Value**
return clasesDB;
}
Also, I want to use this function in another place like this:
var listaClases = await this.claseServicio.obtenerListadoClases(categoria); // Not getting the correct info
// console.log(listaClases , 'listado clases');
What Im doing wrong? Or how can I fix it? Thanks in advance!
You can only subscribe to observables.
The Observable way
getClases(categoria): Observable<any> {
return new Observable(observer => {
// logic to return data
observer.next(data);
observer.complete()
// logic when error
observer.error(error);
});
}
Return the getClases() function
obtenerListadoClases(categoria): Observable<any>{
return this.getClases(categoria);
}
Use the function where you want:
this.obtenerListadoClases(categoria)
.subscribe(
result => {
// what you want to do with the result
},
error => {
// what you want to do with the error
});
The Promise way
getClases(categoria): Promise<any> {
return new Promise((resolve, reject) => {
// logic to return data
resolve(data);
// logic when error
reject(error);
});
}
Return the getClases() function
obtenerListadoClases(categoria): Promise<any>{
return this.getClases(categoria);
}
Use the function where you want:
this.obtenerListadoClases(categoria)
.then(result => {
// what you want to do with the result
})
.catch(error => {
// what you want to do with the error
});
You should be using promises with the .subscribe(). Only observables use .subcribe()
Also, stay away from promises in the angular world. Time to think reactive.
Is this returning an observable? this.getClases(categoria) post the code please.

How to return from then of a Future in dart

I have a function which does some asynchronous operations and I want to return the status of the operation back to the caller. How can I achieve this?
Future<bool> setData() async {
Firestore.instance.collection("test").document('$id').setData({
'id': 'test'
}).then((onValue) {
print('Data set success');
return true;
}).catchError((onError) {
print('Data set Error!!!');
return false;
});
}
//Calling
final status = await setData();
if(status){
//do success
}
But this function complains that it doesn't end with a return statement. What is the logical mistake I'm making here?
You miss a return in your setData function
return Firestore.instance....

breeze observableArray binding - are properties observable?

I have a viewmodel which consists of a list(foreach loop) of DoctorPrices and when clicking on an item in the list it open up a CRUD form on the side. However when i update the values on the CRUD the observableArray that is bound to the foreach is not refreshing? (although the values are updates in the DB correctly)
From my data access module i call the following query.
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
return manager.executeQueryLocally(query);
}
In my viewmodel i have the following code:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
services is bound using a foreach loop (not posting here as the code is simple and works)
When i click on a one of the DoctorPrices it gets the data as follows and places it in an observable:
this.selectedPrice = function (data, event) {
self.currentService(data);
self.showEdit(true);
};
I then bind selectPrice to a simple form that has the properties on it to be modified by the user. I then call manager.SaveChanges().
This results in the following problem: the value is being updated correctly but the GUI / Original List that is bound in the foreach is not being updated? Are the properties in breeze not observables? What is the best way to work with something like this.
I thought of a workaround and changing the code with something like this:
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
});
But i feel that clearing the array in that way is sloppy and not the right way of doing things specially with long lists.
Can someone please point me in the right direction on how to bind observableArray properties properly so they are updated?
Additional code my VM Component:
function services() {
var self = this;
this.showForm = ko.observable(false);
this.currentService = ko.observable();
this.services = ko.observableArray(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
this.title = ko.observable();
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
self.showDetails(false);
});
this.show = function (value) {
self.showForm(value);
};
this.showDetails = ko.observable(false);
this.addNewService = function() {
self.currentService(doctorServices.createService(doctorList.viewModel.instance.currentDoctorID()));
console.log(self.currentService().entityAspect.entityState);
self.showDetails(true);
};
this.showDelete = ko.computed(function() {
if (self.currentService() == null)
return false;
else if (self.currentService().entityAspect.entityState.isDetached()) {
self.title('Add new service');
return false;
} else {
self.title('Edit service');
return true;
}
});
this.deleteService = function() {
self.currentService().entityAspect.setDeleted();
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
};
this.closeDetails = function () {
doctorServices.manager.rejectChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.showDetails(false);
};
this.selectService = function (data, event) {
self.currentService(data);
self.showDetails(true);
};
this.saveChanges = function () {
console.log(self.currentService().entityAspect.entityState);
if (self.currentService().entityAspect.entityState.isDetached()) {
doctorServices.attachEntity(self.currentService());
}
console.log(self.currentService().entityAspect.entityState);
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.currentService.notifySubscribers();
self.showDetails(true);
};
}
return {
viewModel: {
instance: new services()
},
template: servicesTemplate,
};
Below is my Breeze Data Class:
define('data/doctorServices', ['jquery', 'data/dataManager', 'knockout','mod/medappBase', 'breeze', 'breeze.savequeuing'], function ($, manager, ko,base, breeze, savequeuing) {
var services = ko.observableArray([]);
return {
attachEntity:attachEntity,
getServices: getServices,
services: services,
manager:manager,
getDoctorServices: getDoctorServices,
getServiceById: getServiceById,
createService:createService,
hasChanges: hasChanges,
saveChanges: saveChanges
};
function getServices() {
var query = breeze.EntityQuery.from("DoctorPrices");
return manager.executeQuery(query).then(function (data) {
services(data.results);
}).fail(function (data) {
console.log('fetch failed...');
console.log(data);
});;
}
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
var set = manager.executeQueryLocally(query);
return set;
}
function getServiceById(serviceId) {
return manager.createEntity('DoctorPrice', serviceId);
//return manager.getEntityByKey('DoctorPrice', serviceId);
}
function handleSaveValidationError(error) {
var message = "Not saved due to validation error";
try { // fish out the first error
var firstErr = error.innerError.entityErrors[0];
message += ": " + firstErr.errorMessage;
base.addNotify('error', 'Could not save.', message);
} catch (e) { /* eat it for now */ }
return message;
}
function hasChanges() {
return manager.hasChanges();
}
function attachEntity(entity) {
manager.addEntity(entity);
}
function createService(doctorId) {
return manager.createEntity('DoctorPrice', { DoctorPricingID: breeze.core.getUuid(), DoctorID:doctorId }, breeze.EntityState.Detached);
};
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
base.addNotify('success', 'Saved.', 'Your updates have been saved.');
}
function saveFailed(error) {
var reason = error.message;
var detail = error.detail;
if (error.innerError.entityErrors) {
reason = handleSaveValidationError(error);
} else if (detail && detail.ExceptionType &&
detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
// Concurrency error
reason =
"Another user, perhaps the server, " +
"may have deleted one or all of the settings." +
" You may have to restart the app.";
} else {
reason = "Failed to save changes: " + reason +
" You may have to restart the app.";
}
console.log(error);
console.log(reason);
}
}
});
Please note this is my frist attempt at both a data class and VM. At the moment i am relying heavily on clearing the array ([]) and using notifySubscribers to make the array refresh :(
I bet you're missing an observable somewhere. I can't tell because you keep hopping from property to property whose definition is not shown.
For example, I don't know how you defined this.currentService.
I'm confused by this:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
Why is it a ko.computed? Why not just make it an observable array.
self.service = ko.observableArray();
// ... later replace the inner array in one step ...
self.service(doctorServices.getDoctorServices(
doctorList.viewModel.instance.currentDoctorID()));
I urge you to follow the observability trail, confident that your Breeze entity properties are indeed observable.
vm.selectedPrice = ko.dependentObservable(function () {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
}, vm);
vm is ur model on which u applied bindings , try this it will work.

How can I access the result of the response of HttpRequest in Dart?

After many attempts to get the content of the response in HttpRequest, I failed completely to know or understand why I can't have what I want, and I must mention that I can log and manipulate the response only inside an onReadyStateChange (onLoad and onLoadEnd are giving me the same results!), but I really want that value outside the callback.
Here is the part of code that I'm stuck with
Map responsData;
req=new HttpRequest()
..open(method,url)
..send(infojson);
req.onReadyStateChange.listen((ProgressEvent e){
if (req.readyState == HttpRequest.DONE ){
if(req.status == 200){
responsData = {'data': req.responseText};
print("data receaved: ${ req.responseText}");
//will log {"data":mydata}
}
if(req.status == 0){
responsData = {'data':'No server'};
print(responsData );
//will log {"data":No server}
}
}
});
//anything here to get responsData won't work
You have to assign an onLoad callback before you call send.
I'm not sure what you mean with only inside an onReadyStateChange.
Maybe you want to assign the responseText to a variable outside the the callback.
Create a method:
Future<String> send(String method, String url, String infojson) {
var completer = new Completer<String>();
// var result;
req=new HttpRequest()
..open(method,url)
..onLoad.listen((event) {
//print('Request complete ${event.target.reponseText}'))
// result = event.target.responseText;
completer.complete(event.target.responseText);
})
..send(infojson);
return completer.future;
}
and call this method like
var result;
send(method, url).then(
(e) {
// result = e;
print('Request complete ${e}'));
});

Resources