I have a simple data class that represents a task:
class Task {
String name;
String userId;
}
And a stream of tasks:
class TaskDataSource {
Stream<List<Task>> getAll() {
}
For every task I want to also fetch a user that is assigned to it:
class UsersDataSource {
Stream<User> getById(String userId);
}
And finally combine a task with a user to create an object that contains user's data as well as task's data. I was playing with zip function as well as flatMap but couldn't find a working solution. Any help would be appreciated.
Use Rx.forkJoinList:
class UserAndTasks {
final User user;
final Task task;
UserAndTasks(this.user, this.task);
}
void main() {
UsersDataSource usersDataSource;
TaskDataSource taskDataSource;
taskDataSource
.getAll()
.flatMap(
(tasks) => Rx.forkJoinList(tasks
.map((task) => usersDataSource
.getById(task.userId)
.map((user) => UserAndTasks(user, task)))
.toList()),
)
.listen((List<UserAndTasks> event) => print(event));
}
Related
I have a little problem. But I dont know why it doesnt work. And I dont know how to post all ids by postman.
I am using unit of work with generic repository. I want to send int[] ids to my controller. I dont want to send entity. I searched a lot it today. And I changed my code. But what is problem now?
This is my repostiroy:
public async Task DeleteRangeAsync(Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _dbSet.Where(predicate);
await Task.Run(() => { _dbSet.RemoveRange(query.AsNoTracking()); });
}
This is my KulturManager:
public async Task<IResult> HardDeleteRangeAsync(int[] ids)
{
await UnitOfWork.Kulturs.DeleteRangeAsync(c => ids.Contains(c.Id));
await UnitOfWork.SaveAsync();
return new Result(ResultStatus.Success, Messages.Info("Kultur", "HardDelete"));
}
And this is my KulturController:
[HttpDelete("{ids}")]
public async Task<IActionResult> HardDeleteRangeAsync(int[] ids)
{
var result = await _kulturManager.HardDeleteRangeAsync(ids);
return Ok(result.Message);
}
Thank you for help
You shouldn't fetch all the entities you want to delete. Instead create stub entities for RemoveRange. If you don't have a common base class, this requires reflection, but with a common entity base class you can do it like this:
public void DeleteRange<T>(int[] ids) where T: BaseEntity, new()
{
_dbSet.RemoveRange(ids.Select(i => new T() { Id = i }).ToList());
}
or if the method is in a generic class, the method would look like
public void DeleteRange(int[] ids)
{
_dbSet.RemoveRange(ids.Select(i => new T() { Id = i }).ToList());
}
And there's no reason to mark this as Async now since it doesn't do any database access.
I want to initialize a parameter within my constructor depending on the value of another parameter.
So depending on if the oldEmployee is not null I should add him to the availableEmployees list otherwise take the availableEmployees list as is.
The reason I want to do this within this class is, that I have read that when using Bloc I should do the computing within the Bloc's State instead of doing this in the UI.
Here is how my class looks like:
class ShiftCreatedOrEdited extends ShiftsState {
final List<Employee> availableEmployees;
final Employee oldEmployee;
const ShiftCreatedOrEdited({
this.availableEmployees,
this.oldEmployee,
});
List<Employee> addOldEmployeeToTheAvailableEmployees(List<Employee> availableEmployees, Employee oldEmployee) {
if (oldEmployee != null) {
List<Employee> hList = availableEmployees;
hList.add(oldEmployee);
return hList;
} else {
return availableEmployees;
}
}
}
How about this:
void main() {}
class Employee {}
class ShiftsState {}
class ShiftCreatedOrEdited {
final Employee oldEmployee;
final List<Employee> availableEmployees;
ShiftCreatedOrEdited(
Employee _oldEmployee, List<Employee> _availableEmployees)
: this.availableEmployees =
_availableEmployees + (_oldEmployee != null ? [_oldEmployee] : []),
this.oldEmployee = _oldEmployee;
}
I am new to flutter development and exploring it. I have created one sample contact app using Flutter modular (Bloc with rxDart).
Screens are
1) ContactPage -- to show contact list
2) AddContactPage -- to add contact
3) ViewContactPage -- to view contact
4) EditContactPage -- to edit contact
I have common ContactBloc class which has following function
fetchContactList
fetchContactListBySearch
insertContact
updateContact
deleteContact
One thing I am unable to understand is why the fetchContactList function is called each time when I navigate to other than the ContactPage screen. Since I am calling the fetchContactList function inside the build method of ContactPage and after the edit delete operation.
I am unable to figure out these things.
Below are the ContactBloc file
class ContactBloc extends Disposable {
final ContactRepository contactRepository=
ContactModule.to.get<ContactRepository>();
List<Contact> contacts;
Contact contact;
bool searchButton= false;
bool showSearch= false;
BehaviorSubject<List<Contact>> _listContactController;
BehaviorSubject<Contact> _contactController;
BehaviorSubject<bool> _searchButtonController;
BehaviorSubject<bool> _searchController;
ContactBloc() {
Story.storyline(story: "ContactBloc()");
_listContactController= BehaviorSubject.seeded(contacts);
_contactController= BehaviorSubject.seeded(contact);
_searchButtonController= BehaviorSubject.seeded(searchButton);
_searchController= BehaviorSubject.seeded(showSearch);
}
Stream<List<Contact>> get listContactOut => _listContactController.stream;
Stream<Contact> get contactOut => _contactController.stream;
Stream<bool> get searchOut => _searchController.stream;
Stream<bool> get buttonSearchOut => _searchButtonController.stream;
Future<Answer> fetchContactList({
bool distinct, List<String> columns,
String where, List whereArgs,
String groupBy, String having,
String orderBy,
int limit, int offset
}) async {
//Story.storyline(story: "ContactBloc.fetchContactList[+]");
Answer A= await contactRepository.fetch(
distinct: distinct, columns: columns, where: where,
whereArgs: whereArgs, groupBy: groupBy, having: having,
orderBy: orderBy, limit: limit, offset: offset
);
_listContactController.sink.add(A.data);
return A;
}
Future<Answer> fetchContactListBySearch(String keywords) async {
keywords= "%$keywords%";
Answer A= await fetchContactList(
where: "name LIKE ? OR phone LIKE ? OR email LIKE ?",
whereArgs: [keywords, keywords, keywords]
);
return A;
}
Future<Answer> insertContact(Contact contact) async {
Answer A= await contactRepository.insert(contact);
fetchContactList();
return A;
}
Future<Answer> updateContact(Contact contact) async {
Answer A= await contactRepository.update(contact);
fetchContactList();
return A;
}
Future<Answer> deleteContact(Contact contact) async {
Answer A= await contactRepository.delete(contact);
fetchContactList();
return A;
}
...
}
you can find sample project for your reference here
https://github.com/dineshrajbhar777/flutter-bloc-with-rxdart.git
A data class in Dart:
import 'package:validate/validate.dart';
class AuthUser {
final String email, token, username, bio, image;
AuthUser(this.email, this.token, this.username, this.bio, this.image) {
Validate.isEmail(this.email);
}
#override
String toString() {
return 'AuthUser{email: $email, token: $token, username: $username, bio: $bio, image: $image}';
}
}
where Validate.isEmail will throws an Error when failed to match:
static void matchesPattern(String input, RegExp pattern,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
if (pattern.hasMatch(input) == false) {
throw new ArgumentError(message);
}
}
static void isEmail(String input,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
matchesPattern(input,new RegExp(PATTERN_EMAIL),message);
}
Now I want to use an elegant way to new this class.
When using Scala, I can use Try(new AuthUser(...)) and patten-matching it.
And in Dart, first I tried RxDart,
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
Observable.just(AuthUser("email", "token", "username", "bio", "img"))
.doOnError((e, s) => print("oh no"))
.listen((e) => print(e.toString()));
});
}
Not work, the test failed for the error(which means RxDart doesn't catch errors at all!!!)
And I want to try Future, failed also.
And I want to use dartz, but I am worried because there is just one maintainer...
Any advice?
If you are OK with using Future what's wrong with this advice: Using Future.sync() to wrap your code? The code will look like this:
void main() {
var f = Future.sync(() {AuthUser("email", "token", "username", "bio", "img"); });
f.then((v) => print("Value: " + v.toString())).catchError((e) => print("Failure: " +e.toString()));
}
The main trick is that Future.sync effectively enables lazy evaluation of the parameter but you have to pass your parameter wrapped in a function. This is actually the same trick Scala compiler does for Try (i.e. for call-by-name parameters) but takes adding a few brackets around.
If you only want the basic functionality of returning either type based on whether an exception occurred or not then you can easily create a utility class such as below.
Otherwise I recommend #SergGr's answer about using Future.sync since it gives you more monadic like pipeline.
void main() {
Try<Error, void> result = Try.it(() => Validate.isEmail("test-example.com"));
if (result is Success) {
print("Good");
} else if (result is Failure) {
print("Error: " + result.exception().toString());
}
}
typedef TryExec<V> = V Function();
abstract class Try<E extends Error, V> {
static Try<E, V> it<E extends Error, V>(TryExec<V> fn) {
try {
return Try.success(fn());
} catch (e) {
return Try.failure(e);
}
}
static Try<E, V> failure<E extends Error, V>(Error e) {
return new Failure(e);
}
static Try<E, V> success<E extends Error, V>(V v) {
return new Success(v);
}
}
class Failure<E extends Error, V> extends Try<E, V> {
final E _e;
Failure(this._e);
E exception() => _e;
}
class Success<E extends Error, V> extends Try<E, V> {
final V _v;
Success(this._v);
V value() => _v;
}
In rx, how do you handle the need to reuse an object instance in one step in the next step? For example, I need to get a context in the ORM to then act upon. Async/Await is in the syntax below:
public async Task<IList<string>> Delete(IList<string> ids)
{
var context = await _contextFactory.CreateContext();
context.Set<T>().RemoveRange(
context.Set<T>().Where(item => ids.Contains(item.Id)));
return ids;
}
An Observable version is
public IObservable<string> DeleteObservable(IList<string> ids)
{
return ids.ToObservable()
.Select(i =>
{
var context = await _contextFactory.CreateContext();
context.Set<T>().RemoveRange(
context.Set<T>().Where(item => item.Id == id));
return id;
});
}
However, I don't want to create a new context every time I delete an item. I want to create a context and then reuse it in the select. How should I do that?
Yes, in this example it would be best to also buffer and submit the ids together, but this was just an example for my question. I hope that part is not distracting.
The more idiomatic way of doing it is like this:
public IObservable<string> DeleteObservable(IList<string> ids)
{
return Observable.Using(
async () => await _contextFactory.CreateContext(),
context =>
ids.ToObservable().Select(i =>
{
context.Set<T>().RemoveRange(context.Set<T>().Where(item => item.Id == i));
return i;
}));
}
The Observable.Using method creates a disposable resource that gets disposed when the subscription to the observable closes.
The only problem with this is that the statement context.Set<T>().RemoveRange(context.Set<T>().Where(item => item.Id == i)); just shouldn't be inside an observable like that. Rx is about queries. Any changes should be made in a .Subscribe method.
What are you trying to achieve?
I think I got it and the answer keeps ended up being 'SelectMany'. I guess I'm still getting used to these operators.
public IObservable<string> DeleteObservable(IList<string> ids)
{
return Observable
.Return(_contextFactory)
.SelectMany(factory => factory.CreateContext())
.Zip(ids.ToObservable(), (dbContext, entityId) =>
{
dbContext.Set<T>().RemoveRange(
dbContext.Set<T>().Where(item => item.Id == entityId));
return entityId;
});
}