- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
假设我有一个带有嵌套集合的 firestore 数据库。一所学校可以有不同的类(class),每门类(class)有不同的部分,每个部分有不同的页面。真的很简单。
从 Firestore 模式的角度来看,这也很简单。嵌套集合优雅地解决了这个问题。
- schools
- schoolDoc1
- courses
- courseDoc1
- sections
- sectionDoc1
- pages
- pageDoc1
Firestore.instance.collection('schools')
.document('<the school document id>')
.collection('courses').snapshot();
class TeachApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthenticationBloc>(
builder: (context) {
return AuthenticationBloc(
userRepository: FirebaseUserRepository(),
)..dispatch(AppStarted());
},
),
BlocProvider<SchoolBloc>(
builder: (context) {
return SchoolBloc(
schoolRepository: FirebaseSchoolRepository(),
);
},
),
BlocProvider<CourseBloc>(
builder: (context) {
return CourseBloc(
courseRepository: FirebaseCourseRepository(),
);
},
),
BlocProvider<SectionBloc>(
builder: (context) {
return SectionBloc(
sectionRepository: FirebaseSectionRepository(),
);
},
),
],
child: MaterialApp(
...
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:school_repository/school_repository.dart';
import 'package:teach_mob/core/blocs/authentication/authentication.dart';
import 'package:teach_mob/ui/shared/palette.dart';
import 'package:teach_mob/ui/views/course_add_view.dart';
import 'package:teach_mob/ui/views/course_view.dart';
import 'package:teach_mob/ui/views/home_view.dart';
import 'package:teach_mob/ui/views/login_failed_view.dart';
import 'package:teach_mob/ui/views/section_add_view.dart';
import 'package:teach_mob/ui/views/section_view.dart';
import 'package:user_repository/user_repository.dart';
import 'package:teach_mob/core/blocs/blocs.dart';
import 'core/constants/app_constants.dart';
import 'core/repositories/course_repository/lib/course_repository.dart';
import 'core/repositories/section_repository/lib/section_repository.dart';
void main() async {
BlocSupervisor.delegate = SimpleBlocDelegate();
runApp(TeachApp());
}
class TeachApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthenticationBloc>(
builder: (context) {
return AuthenticationBloc(
userRepository: FirebaseUserRepository(),
)..dispatch(AppStarted());
},
),
BlocProvider<SchoolBloc>(
builder: (context) {
return SchoolBloc(
schoolRepository: FirebaseSchoolRepository(),
);
},
),
BlocProvider<CourseBloc>(
builder: (context) {
return CourseBloc(
courseRepository: FirebaseCourseRepository(),
);
},
),
BlocProvider<SectionBloc>(
builder: (context) {
return SectionBloc(
sectionRepository: FirebaseSectionRepository(),
);
},
),
],
child: MaterialApp(
title: "TeachApp",
routes: {
RoutePaths.Home: (context) => checkAuthAndRouteTo(HomeView(), context),
RoutePaths.Course: (context) => checkAuthAndRouteTo(CourseView(), context),
RoutePaths.CourseAdd: (context) => checkAuthAndRouteTo(CourseAddView(
onSave: (id, name, description, teaserImage) {
BlocProvider.of<CourseBloc>(context)
.dispatch(AddCourseEvent(Course(
name: name,
description: description,
teaserImage: teaserImage
))
);
},
isEditing: false,
), context),
RoutePaths.CourseEdit: (context) => checkAuthAndRouteTo(CourseAddView(
onSave: (id, name, description, teaserImage) {
BlocProvider.of<CourseBloc>(context)
.dispatch(UpdateCourseEvent(Course(
id: id,
name: name,
description: description,
teaserImage: teaserImage
))
);
},
isEditing: true
), context),
RoutePaths.Section: (context) => checkAuthAndRouteTo(SectionView(), context),
RoutePaths.SectionAdd: (context) => checkAuthAndRouteTo(SectionAddView(
onSave: (id, name) {
BlocProvider.of<SectionBloc>(context)
.dispatch(AddSectionEvent(Section(
name: name
))
);
},
isEditing: false,
), context),
RoutePaths.SectionEdit: (context) => checkAuthAndRouteTo(SectionAddView(
onSave: (id, name) {
BlocProvider.of<SectionBloc>(context)
.dispatch(UpdateSectionEvent(Section(
id: id,
name: name
))
);
},
isEditing: true
), context)
},
theme: ThemeData(
scaffoldBackgroundColor: Palette.backgroundColor
)
)
);
}
BlocBuilder<AuthenticationBloc, AuthenticationState> checkAuthAndRouteTo(Widget view, BuildContext context) {
return BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is Authenticated) {
return view;
}
if (state is Unauthenticated) {
return LoginFailedView();
}
return Center(child: CircularProgressIndicator());
},
);
}
}
import 'dart:async';
import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
import 'package:school_repository/school_repository.dart';
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
import 'package:teach_mob/core/blocs/school/school_event.dart';
import 'package:teach_mob/core/blocs/school/school_state.dart';
class SchoolBloc extends Bloc<SchoolEvent, SchoolState> {
final SchoolRepository _schoolRepository;
StreamSubscription _schoolSubscription;
// Repository is injected through constructor, so that it can
// be easily tested.
SchoolBloc({@required SchoolRepository schoolRepository})
: assert(schoolRepository != null),
_schoolRepository = schoolRepository;
// Each Bloc needs an initial state. The state must be
// defined in the state file and can't be null
@override
SchoolState get initialState => SchoolInitState();
// Here we map events to states. Events can also trigger other
// events. States can only be yielded within the main function.
// yielding in listen methods will not work. Hence from a listen
// method, another event has to be triggered, so that the state
// can be yielded.
@override
Stream<SchoolState> mapEventToState(SchoolEvent event) async* {
if(event is LoadSchoolAndCacheEvent) {
yield* _mapLoadSchoolAndCacheEventToState(event);
} else if(event is LoadSchoolFromCacheEvent) {
yield* _mapLoadSchoolFromCacheEvent(event);
} else if(event is SchoolLoadedFromCacheEvent) {
yield* _mapSchoolLoadedFromCacheEvent(event);
} else if(event is SchoolCachedEvent) {
yield* _mapSchoolCachedEventToState(event);
}
}
// Get a single school and store it in shared preferences
Stream<SchoolState> _mapLoadSchoolAndCacheEventToState(LoadSchoolAndCacheEvent event) async* {
final prefs = await StreamingSharedPreferences.instance;
yield SchoolDataLoadingState();
_schoolSubscription?.cancel();
_schoolSubscription = _schoolRepository.school(event.id).listen(
(school) {
final schoolString = json.encode(school.toEntity().toJson());
prefs.setString("school", schoolString);
dispatch(SchoolCachedEvent(school));
}
);
}
// Load the school from shared preferences
Stream<SchoolState> _mapLoadSchoolFromCacheEvent(LoadSchoolFromCacheEvent event) async* {
final prefs = await StreamingSharedPreferences.instance;
yield SchoolDataLoadingState();
final schoolString = prefs.getString("school", defaultValue: "");
schoolString.listen((value){
final Map schoolMap = json.decode(value);
final school = School(id: schoolMap["id"],
name: schoolMap["name"]);
dispatch(SchoolLoadedFromCacheEvent(school));
});
}
// Yield school loaded state
Stream<SchoolState> _mapSchoolLoadedFromCacheEvent(SchoolLoadedFromCacheEvent event) async* {
yield SchoolDataLoadedState(event.school);
}
}
Stream<SchoolState> _mapSchoolCachedEventToState(SchoolCachedEvent event) async* {
yield SchoolDataLoadedState(event.school);
}
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
import 'package:teach_mob/core/blocs/course/course_event.dart';
import 'package:teach_mob/core/blocs/course/course_state.dart';
import 'package:teach_mob/core/repositories/course_repository/lib/course_repository.dart';
class CourseBloc extends Bloc<CourseEvent, CourseState> {
final CourseRepository _courseRepository;
StreamSubscription _courseSubscription;
// Repository is injected through constructor, so that it can
// be easily tested.
CourseBloc({@required CourseRepository courseRepository})
: assert(courseRepository != null),
_courseRepository = courseRepository;
@override
get initialState => CourseInitState();
@override
Stream<CourseState> mapEventToState(CourseEvent event) async* {
if(event is LoadCoursesEvent) {
yield* _mapLoadCoursesToState(event);
} else if(event is CoursesLoadedEvent) {
yield* _mapCoursesLoadedToState(event);
} else if(event is AddCourseEvent) {
yield* _mapAddCourseToState(event);
} else if(event is UpdateCourseEvent) {
yield* _mapUpdateCourseToState(event);
} else if(event is DeleteCourseEvent) {
yield* _mapDeleteCourseToState(event);
}
}
// Load all courses
Stream<CourseState> _mapLoadCoursesToState(LoadCoursesEvent event) async* {
yield CoursesLoadingState();
_courseSubscription?.cancel();
_courseRepository.setSchool = "3kRHuyk20UggHwm4wrUI";
_courseSubscription = _courseRepository.courses().listen(
(courses) {
dispatch(
CoursesLoadedEvent(courses),
);
},
);
}
Stream<CourseState> _mapCoursesLoadedToState(CoursesLoadedEvent event) async* {
yield CoursesLoadedState(event.courses);
}
Stream<CourseState> _mapAddCourseToState(AddCourseEvent event) async* {
_courseRepository.addCourse(event.course);
}
Stream<CourseState> _mapUpdateCourseToState(UpdateCourseEvent event) async* {
_courseRepository.updateCourse(event.updatedCourse);
}
Stream<CourseState> _mapDeleteCourseToState(DeleteCourseEvent event) async* {
_courseRepository.deleteCourse(event.course);
}
@override
void dispose() {
_courseSubscription?.cancel();
super.dispose();
}
}
最佳答案
BLoC 是应用程序的业务逻辑发生的地方,因此可以在 BLoC 内部调用来自存储库的多个 Firestore 请求。对于这个用例,可以调用 Firestore 查询来获取“类(class)”所需的“学校”。
关于flutter_bloc - 每个 Bloc 有多个存储库,还是只有一个?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58898985/
任何人都知道如何将内容从一个 Magnolia CMS 存储库(Jackrabbit 存储库)传输/迁移到我们的应用程序使用的另一个自定义 jacrabbit 存储库? 最佳答案 正如另一张海报所建议
Git 是否支持任何允许我直接从本地/工作树提交到远程存储库的命令?正常的工作流程至少需要一个“git add”来用文件内容的副本等填充对象数据库。 我知道这不是正常的、预期的 Git 工作流程。但我
我们正在将 Git 存储库移动到新服务器。迁移后我们显然可以只删除旧的存储库,所以当人们尝试推送或 pull 时他们会得到一个错误并在 wiki 上查找新的存储库 URL,但是是否有可能阻止 pull
我们有两个 Subversion 存储库,每个存储库都有一个项目。所以: svn://server/svn/project_a svn://server/svn/project_b 它们是独立的项目,
使用 maven/tycho 构建 Nodeclipse Eclipse 插件每个版本都有新的 p2 存储库。 发布是在 Bintray 上完成的不允许更新文件。所以每个版本都在它的文件夹中。 Bas
这听起来有点复杂,让我解释一下: Project_A 在它自己的 Mercurial 存储库中已经存在了一段时间。 Project_A 现在被 merge 到一个新的 super 项目 Super-P
这听起来有点复杂,所以让我解释一下: Project_A 已在其自己的 Mercurial 存储库中存在了一段时间。 Project_A 现在正在 merge 到一个新的 super 项目 Super
我想将我的所有文件从 Git Repo A 移动到 Git Repo B 并具有完整的历史记录。 Git B 已经包含另一个项目文件。我尝试了几种方法,例如 How to move files fro
我从 github 中托管的公共(public) git 存储库创建了一个裸存储库 (MY_LOCAL_REP): ~$ git clone --bare github 存储库已更新(创建了一个分支
工作 SVN 库 我正在启动一个 git 存储库来与 svn 存储库进行交互。 svn 存储库已设置并且工作正常,其中包含一个基本 README 文件的单次提交。 检查它工作正常: tchalvak:
我正在使用 spring boot 1.5.2 和 spring boot data redis 1.8。 我有两个@Id 注解,一个用于JPA,另一个用于redis hash。这里我想使用 JPA
我是 maven 的新手。我仍然无法理解它的概念。 例如,我正在寻找 com.extjs:gxt:jar:2.2.5 或 org.syslog4j:syslog4j:jar:0.9.46。我在任何 r
我已经阅读了很多关于存储库模式和服务层的作用的书,我(我认为)很清楚这两者之间的区别。但是现在有一个简单的问题让我挠头了一段时间。 我知道数据访问层如何负责...访问数据,因此典型的存储库可能具有插入
我是 Git/Smartgit 的新手。现在我不得不在 WIN 10 下配置一台新 PC。请问我如何将设置和存储库从旧 PC 导出/导入到新 PC? 非常感谢,问候, 本德 最佳答案 SmartGit
所以我最近开始在我的工作项目中使用存储库模式。我一直遇到同样的问题,我似乎无法找到答案: 可以将另一个存储库注入(inject)现有存储库吗?这样做有什么负面影响? 例如 : class Crawls
我有一个应用程序容器推送到 gitlab 容器注册表。我正在尝试将其作为容器部署到 azure web 应用程序服务中。我根据azure的文档尽我所能地进行了配置。但我不明白我错过了什么,因为 azu
我使用java框架来开发crud应用程序。这个框架被称为:“Cuba.Platform”。 我的问题是,我无法在古巴打开该项目。当我尝试在那里打开我的项目时,我遇到了这个问题: “存储库包含 http
我在我的数据层中有几个对数据库执行 CRUD 操作的存储库类。我不确定这种设计,因为大多数表都需要每个存储库一个专用类,一段时间后我最终会为数据库中存在的每个表得到很多存储库。我这样做是因为,当然,我
我正在重构一些代码,并将一些执行数据库 CRUD 操作的方法提取到它们自己的存储库类中。 我有几个问题,鉴于以下两种类型的对象存储在数据库中:用户和角色 我应该创建 IUserRepository 和
尝试在这里创建一个非常简单的存储库和服务层模式。 (.NET 4、C#、LINQ,尽管这个问题部分与语言无关)。注意:这只是研发。 我的目标是尽量减少服务层中方法定义的数量。 这是我的存储合约: in
我是一名优秀的程序员,十分优秀!