>以前,我们创建了自己的firestore getters来返回适当的观察力,从而将诺言转变为可冷观察的诺言。今天,让我们继续使用其他命令,以正确地映射我们的数据。
>
映射数据
>现在我们不依赖rxfire返回映射的文档id,我们将创建自己的转换器。
“ firestore有一个内置的converter withconverter to conconter tor map to and firestore。不,谢谢。我们将从这里拿走。
getdoc()返回具有id的文档napshot,以及上述数据()。 getdocs()返回带有docs数组的querysnapshot,除其他外,这是返回的documentsnapshot对象。我们的类别模型现在看起来像这样:
// category.model// mapping new instanceexport interface icategory { name: string | null; id: string | null; key?: string;}// static class or export functions, it's a designers choiceexport class category { public static newinstance(data: querydocumentsnapshot): icategory { if (data === null) { return null; } const d = data.data(); return { id: data.id, name: d['name'], key: d['key'] }; } // receives querysnapshot and maps out the `data() ` and id public static newinstances(data: querysnapshot): icategory[] { // read docs array return data.docs.map(n => category.newinstance(n)); }}
因此,现在的服务调用看起来像这样:
// category.service getcategories(params: iwhere[] = []): observable { // ... return from(getdocs(_query)).pipe( map((res: querysnapshot) => { // here map return category.newinstances(res); }) );}getcategory(id: string): observable { return from(getdoc(doc(this.db, 'categories', id))).pipe( map((res: documentsnapshot) => { // map return category.newinstance(res); }), );}
>让我们看看什么adddoc()返回。
>
创建文档
>我创建了一个带有名称和键的快速表单,添加类别,以及一个服务呼叫以创建类别:
// components/categories.listaddnew() { // read form: const newcat: partial = this.fg.value; this.categoryservice.createcategory(newcat).subscribe({ next: (res) => { // the category list should be updated here console.log(res); } });}
>该服务有望返回可观察到的,以下内容可能无法将其删除,返回的对象是一个文档重新率,仅是对具有id创建的文档的引用。
// category.servicecreatecategory(category: partial): observable { return from(adddoc(collection(this.db, 'categories'), category)).pipe( map(res => { // i have nothing but id! return {...category, id: res.id}; }) );}
>可能看起来不错,但是我不希望在不浏览映射器的情况下直接使用id,解决方案可以像单独的映射器一样简单,或者我们可以稍微适应newinstance
// category.model// adjust newinstance to accept partial categorypublic static newinstance(data: documentsnapshot | partial, id?: string): icategory { if (data === null) { return null; } const d = data instanceof documentsnapshot ? data.data() : data; return { id: data.id || id, // if data.id doesnt exist (as in adddoc) name: d['name'], key: d['key'] };}
然后,在服务呼叫中,我们通过返回的id
通过原始类别
// category.servicecreatecategory(category: partial): observable { return from(adddoc(collection(this.db, 'categories'), category)).pipe( map(res => { // update to pass category and res.id return category.newinstance(category, res.id); }), );}
firestore语法的奇怪案例
>以下方法是如此相同,如果您不区分它们,您可能会在50岁之前失去头发。因此,我决定列出它们,然后忘记它们。
doc(this.db,“类别”,“ someid”)返回新文档或现有文档的文档retreference
doc(collection(this.db,’cantories’))返回新生成的id >
setdoc(_document_reference,{… newcategory},选项)设置文档数据,并在文档参考
中保存提供的id
adddoc(collection(this.db,’cantories’),{… newcategory});添加带有自动生成id的文档(这是doc(collection …)然后setdoc())>的快捷方式updatedoc(doc(this.db,’categories’,’staust_id’),partial_category)这会更新现有文档(doc(db …)的快捷键(db …),然后使用现有id)进行setdoc()>从前两个开始激起了混乱,它们看起来是如此相似,但是它们是不同的>
// find document reference, or create it with new idconst categoryref = doc(this.db, 'categories', '_somedocumentreferenceid');// create a document reference with auto generated idconst categoryref = doc(collection(this.db, 'categories'));
现在,返回的参考文档可用于创建实际文档,或对其进行更新。因此,预计将通过setdoc 成功
// then use the returned reference to setdoc, to add a new documentsetdoc(categoryref, {name: 'bubbles', key: 'bubbles'});// pass partial document with options: merge to partially update an existing documentsetdoc(existingcategoryref, {name: 'bubble'}, {merge: true});
>如果文档不存在wit合并设置为true,它将创建一个带有部分数据的新文档。不好。小心。安全的选项是最后两个:
>
// add a new documnet with a new generated idadddoc(collection(this.db, 'categories'), {name: 'bubbles', key: 'bubbles'});// update and existing document, this will throw an error if non existentupdatedoc(existingcategoryref, {name: 'bubbles'})
出于插图目的,这是创建新类别
的另一种方法
// an alternative way to createcreatecategory(category: partial): observable { // new auto generated id const ref = doc(collection(this.db, 'categories')); return from(setdoc(ref, category)).pipe( map(_ => { // response is void, id is in original ref return category.newinstance(category, ref.id); }) );}
更新文档
>在上面的构建中,我为类别详细信息创建了一个快速表单以允许更新。我仍然会通过所有字段,但是我们总是可以通过部分字段。
>
// category.sevice// id must be passed in categoryupdatecategory(category: partial): observable { return from(updatedoc(doc(this.db, 'categories', category.id), category)).pipe( map(_ => { // response is void // why do we do this? because you never know in the future what other changes you might need to make before returning return category.newinstance(category); }) );}
这是概念的证明,有一些方法可以拧紧松动的末端,例如强迫id通过,检查不存在的文档,返回不同的数据形状等。
>
使用它,收集表格,并捕获id:>
// categories/list.componentupdate(category: icategory) { // gather field values const cat: partial = this.ufg.value; // make sure the id is passed this.categoryservice.updatecategory({...cat, id: category.id}).subscribe({ next: (res) => { console.log(res); // this has the same category after update } }); }
删除文档
这是直截了当的,我们可能会选择退还布尔值以获得成功。
>
// category.sevicedeletecategory(id: string): observable { return from(deletedoc(doc(this.db, 'categories', id))).pipe( // response is void, but we might want to differentiate failures later map(_ => true) );}
我们只是通过传递id
来称其为
// category/list.componentdelete(category: icategory) { this.categoryservice.deletecategory(category.id).subscribe({ next: (res) => { console.log('success!', res); } });}
>没有直截了当的批量删除方式。让我们继续前进。
负责任地查询
>我们创建了一个iwher模型,以允许将任何查询传递到我们的类别列表。但是,我们应该控制在模型中查询的字段,并保护我们的组件免受现场变化的影响。我们还应该控制可以询问的内容,以及如何始终领先于火箱上的任何隐藏价格。
在我们的iwhere模型中,我们可以添加ifieldoptions模型。这是标签而不是名称的一个示例,以说明我们如何保护我们的组件免受火灾的变化。
// where.model// the param fields object (yes its plural)export interface ifieldoptions { label?: string; // exmaple key?: string; // other example fields month?: date; recent?: boolean; maxprice?: number;}// map fields to where conditions of the proper firestore keysexport const maplistoptions = (options?: ifieldoptions): iwhere[] => { let c: any[] = []; if (!options) return []; if (options.label) { // mapping label to name c.push({ fieldpath: 'name', opstr: '==', value: options.label }); } if(options.key) { c.push({ fieldpath: 'key', opstr: '==', value: options.key }); } // maxprice is a less than or equal operator if (options.maxprice) { c.push({ fieldpath: 'price', opstr: '=', value: lastweek }); } return c;}
我们服务的更改就像这样
>
// category.sevice// accept options of specific fieldsgetcategories(params?: ifieldoptions): observable { // translate fields to where: const _where: any[] = (mapfieldoptions(fieldoptions)) .map(n => where(n.fieldpath, n.opstr, n.value)); // pass the where to query const _query = query(collection(this.db, this._url), ..._where); // ... getdocs}
这是如何以最高价格获取一组文档的示例:
// example usagesomething$ = someservice.getlist({maxprice: 344});
// example usagesomething$ = someservice.getlist({maxprice: 344});
这更有意义。我添加了一个日期示例,请注意,即使firestore日期为type tymestamp,在javascript日期上的操作正常。该示例的想法是说明组件不应该真正知道实现的详细信息,因此获得“最新文档”,例如应该像这样的
>
// examplesomething$ = someservice.getlist({recent: true});
另一个示例是获取日期,firestore直接处理javascript日期:>
// where.modelconst mapmonth = (month: date): iwhere[] => { const from = new date(month); const to = new date(month); from.setdate(1); from.sethours(0, 0, 0, 0); to.setmonth(to.getmonth() + 1); to.sethours(0, 0, 0, 0); // the edge of next month, is last day this month let c: iwhere[] = [ { fieldpath: 'date', opstr: '>=', value: from }, { fieldpath: 'date', opstr: '<=', value: to } ]; return c;};
这是按预期工作的。
时间戳
firestore参考时间戳。
在查询时,此类可能不会有所作为,但是如果您有一个日期字段,并且希望保存用户提供的日期,则最好使用firestore timestamp类进行转换。 firestore时间戳和javascript日期之间的差异是微妙的。如此微妙,我什至看不到它!我认为这是四舍五入的等级。
>
// somemodel// use timestamp class to map from and to datesconst jsdate = new date();// adddoc withconst newdata = { fbdate: timestamp.fromdate(jsdate)}// documentsnapshot returns data() with date field, the type is timestamp// use todate to convert to js datereturn data['fbdate'].todate();
firestore lite
firebase为firestore提供了简单crud的lite版本,到目前为止,我们仅使用了lite版本中的那些。我更改了所有
导入{…}来自’@angular/fire/firestore’;
>进入
导入{…}来自’@angular/fire/firestore/lite’;
>
构建块的确很小。我个人项目中减少的主要部分从502 kb下降到352 kb。
拦截
>使用httpclient,我们使用了截距函数来添加加载效果,那么我们如何使用解决方案来做到这一点?我们可以将所有呼叫调查到一个照顾它的http服务。我们的新服务应该与此有关:
// http.service@injectable({ providedin: 'root' })export class httpservice { public run(fn: observable): observable { // show loader here showloader(); // return function as is return fn .pipe( // hide loader finalize(() => hideloader()), // debug and catcherrors as well here ); }}
然后在服务中
// category.serviceprivate httpservice = inject(httpservice);getcategories(params?: iwhere[]): observable { // ... // wrap in run return this.httpservice.run(from(getdocs(q)).pipe( map((res: querysnapshot) => { return category.newinstances(res); }) ));}
>上面的一个设计问题是类型检查的丢失,因为内部函数返回可观察到的。这可以用通用
修复
// http.service// ...public run(fn: observable): observable { // ...}
然后这样称呼:
>
// category.servicereturn this.httpService.run(from(getDocs(q)).pipe( map((res: QuerySnapshot) => { return Category.NewInstances(res); })));
>查看我们先前使用状态管理创建的加载效果。我们需要的只是注入州服务,然后呼叫显示和隐藏。这使用了良好的ol’rxjs,我调查了信号,看不到它如何替换rxj,而不会造成大混乱。可能是一个罚款星期二。
就是这样。在50岁之前不要丢发头发。
你上火了吗?继续说话。还没有结束。
资源
stackblitz project
firesstore docs
相关文章
>在angular
中使用rxj创建负载效果
将angular fire firestore库使用-ii- sekrab车库 角燃箱炸弹群。以前,我们创建了自己的firestore获取器,以返回适当的观察力,从而将诺言转变为可感冒的诺言。今天,让我们继续使用其他命令,以正确地映射我们的数据。
garage.sekrab.com
以上就是将角消防员图书馆放置 – II的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1501208.html
微信扫一扫
支付宝扫一扫