如何在链接管道操作后 return 从 angular 中的服务观察到
How to return an observable from service in angular after chaining pipe operations
我正在尝试链接/管道操作和 return 来自 angular 中使用 angular fire 的服务的 Observable。
我保证我有这个工作
服务
saveDiploma(diploma: { title: any; description: any; picture: any }) {
return new Observable(observer => {
const id = this.db.createId();
this.storage.ref(`diplomas/${id}/original.jpg`)
.putString(diploma.picture, 'data_url')
.then(task => {
task.ref.getDownloadURL()
.then(url => {
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
this.db.doc(`diplomas/${id}`)
.set(saved)
.then(() => {
observer.next(saved);
observer.complete();
})
.catch(e => observer.error(e));
})
.catch(e => observer.error(e));
})
.catch(e => observer.error(e));
});
}
组件
save() {
this.diplomasService.saveDiploma({
title: this.diplomaForm.value.title,
description: this.diplomaForm.value.description,
picture: this.currentImage
}).subscribe(diploma => {
console.log('saved diploma', diploma);
}, e => console.error('error while saving the diploma', e));
}
我正在尝试在服务中使用 Observables 而不是 Promises 并像这样通过管道
saveDiploma(diploma: { title: any; description: any; picture: any }) {
const id = this.db.createId();
const ref = this.storage.ref(`diplomas/${id}/original.jpg`);
return ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(
concatMap(task => {
console.log('getDownloadURL');
return from(task.ref.getDownloadURL());
}),
concatMap(url => {
console.log('url', url);
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
return from(this.db.doc(`diplomas/${id}`).set(saved));
})
);
}
但是 getDownloadURL 方法在上传完成之前被触发,因此 return 出现错误 storage/object-not-found
。我试过在 concatMap(getDownloadURL) 之前添加一个终结器或过滤器(在 task.state == 'success' 上),但我没能让它工作。
有谁知道如何通过管道传输此操作和 return 来自他们的 Observable?
我正在使用 Angular 8.1.2,Angular Fire 5.2.1 和 rxjs 6.5.1
这里的问题是 promise 默认是急切的。我认为用 defer
运算符 (https://rxjs.dev/api/index/function/defer) 包装 from
运算符应该可以解决您的问题。所以代码看起来像这样:
return ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(
concatMap(task => defer(() => {
console.log('getDownloadURL');
return from(task.ref.getDownloadURL());
})),
concatMap(url => defer(() => {
console.log('url', url);
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
return from(this.db.doc(`diplomas/${id}`).set(saved));
}))
传递给 defer 的方法在被订阅后立即被评估。一旦有来自源 observable 的传入通知,ConcatMap 将自动订阅内部 observable。
根据 AngularFire 文档ref.putString(..).snapshotChanges()
Emits the raw UploadTaskSnapshot as the file upload progresses.
所以你的问题是 .snapshotChanges()
在文件上传完成之前发出。 concatMap
在每次从源发出时触发,而不仅仅是在完成时触发。你应该使用 concat
.
saveDiploma(diploma: { title: any; description: any; picture: any }) {
const id = this.db.createId();
const ref = this.storage.ref(`diplomas/${id}/original.jpg`);
return concat(
ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(ignoreElements()),
defer(() => ref.getDownloadURL().pipe(
switchMap(url => {
console.log('url', url);
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
return this.db.doc(`diplomas/${id}`).set(saved); // you can return a Promise directly
})
))
);
}
可能的选择:
saveDiploma(diploma: { title: any; description: any; picture: any }) {
const id = this.db.createId();
const ref = this.storage.ref(`diplomas/${id}/original.jpg`);
return ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(
last(),
switchMap(() => ref.getDownloadURL()),
map(url => ({
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
})),
switchMap(saved => this.db.doc(`diplomas/${id}`).set(saved))
);
}
我正在尝试链接/管道操作和 return 来自 angular 中使用 angular fire 的服务的 Observable。
我保证我有这个工作
服务
saveDiploma(diploma: { title: any; description: any; picture: any }) {
return new Observable(observer => {
const id = this.db.createId();
this.storage.ref(`diplomas/${id}/original.jpg`)
.putString(diploma.picture, 'data_url')
.then(task => {
task.ref.getDownloadURL()
.then(url => {
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
this.db.doc(`diplomas/${id}`)
.set(saved)
.then(() => {
observer.next(saved);
observer.complete();
})
.catch(e => observer.error(e));
})
.catch(e => observer.error(e));
})
.catch(e => observer.error(e));
});
}
组件
save() {
this.diplomasService.saveDiploma({
title: this.diplomaForm.value.title,
description: this.diplomaForm.value.description,
picture: this.currentImage
}).subscribe(diploma => {
console.log('saved diploma', diploma);
}, e => console.error('error while saving the diploma', e));
}
我正在尝试在服务中使用 Observables 而不是 Promises 并像这样通过管道
saveDiploma(diploma: { title: any; description: any; picture: any }) {
const id = this.db.createId();
const ref = this.storage.ref(`diplomas/${id}/original.jpg`);
return ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(
concatMap(task => {
console.log('getDownloadURL');
return from(task.ref.getDownloadURL());
}),
concatMap(url => {
console.log('url', url);
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
return from(this.db.doc(`diplomas/${id}`).set(saved));
})
);
}
但是 getDownloadURL 方法在上传完成之前被触发,因此 return 出现错误 storage/object-not-found
。我试过在 concatMap(getDownloadURL) 之前添加一个终结器或过滤器(在 task.state == 'success' 上),但我没能让它工作。
有谁知道如何通过管道传输此操作和 return 来自他们的 Observable?
我正在使用 Angular 8.1.2,Angular Fire 5.2.1 和 rxjs 6.5.1
这里的问题是 promise 默认是急切的。我认为用 defer
运算符 (https://rxjs.dev/api/index/function/defer) 包装 from
运算符应该可以解决您的问题。所以代码看起来像这样:
return ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(
concatMap(task => defer(() => {
console.log('getDownloadURL');
return from(task.ref.getDownloadURL());
})),
concatMap(url => defer(() => {
console.log('url', url);
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
return from(this.db.doc(`diplomas/${id}`).set(saved));
}))
传递给 defer 的方法在被订阅后立即被评估。一旦有来自源 observable 的传入通知,ConcatMap 将自动订阅内部 observable。
根据 AngularFire 文档ref.putString(..).snapshotChanges()
Emits the raw UploadTaskSnapshot as the file upload progresses.
所以你的问题是 .snapshotChanges()
在文件上传完成之前发出。 concatMap
在每次从源发出时触发,而不仅仅是在完成时触发。你应该使用 concat
.
saveDiploma(diploma: { title: any; description: any; picture: any }) {
const id = this.db.createId();
const ref = this.storage.ref(`diplomas/${id}/original.jpg`);
return concat(
ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(ignoreElements()),
defer(() => ref.getDownloadURL().pipe(
switchMap(url => {
console.log('url', url);
const saved = {
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
};
return this.db.doc(`diplomas/${id}`).set(saved); // you can return a Promise directly
})
))
);
}
可能的选择:
saveDiploma(diploma: { title: any; description: any; picture: any }) {
const id = this.db.createId();
const ref = this.storage.ref(`diplomas/${id}/original.jpg`);
return ref.putString(diploma.picture, 'data_url').snapshotChanges().pipe(
last(),
switchMap(() => ref.getDownloadURL()),
map(url => ({
title: diploma.title,
description: diploma.description,
url,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
createdBy: this.auth.auth.currentUser ? this.auth.auth.currentUser.uid : 'anonymous'
})),
switchMap(saved => this.db.doc(`diplomas/${id}`).set(saved))
);
}