I build ToDoApp and connect firebase to my project but i have error.
ERROR in src/app/todo-component/todo-component.component.ts(25,7): error TS2740: Type 'DocumentChangeAction<{}>[]' is missing the following properties from type 'Observable<ToDoInterface[]>': _isScalar, source, operator, lift, and 5 more.
Here my ToDOInterface:
export interface ToDoInterface {
id?: string;
title?: string;
completed?: boolean;
}
My ToDoService:
import { Injectable } from '#angular/core';
import {ToDoInterface} from './ToDoInterface'
import {AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument} from "#angular/fire/firestore";
import {Observable} from "rxjs";
#Injectable({
providedIn: 'root'
})
export class ToDoService {
public toDoArray:ToDoInterface[] = [
{
id: "sdfsdf",
title: 'Todo one',
completed: false
},
{
id: "13dsfsdf",
title: 'Todo two',
completed: false
},
{
id: "safsdf",
title: 'Todo third',
completed: false
}
];
ToDoCollection: AngularFirestoreCollection<ToDoInterface>;
ToDo: Observable<ToDoInterface[]>;
ToDoCollectionName: string = "ToDo";
constructor(private firestore: AngularFirestore) {
}
getToDos(){
return this.toDoArray;
}
getToDosFirebase(){
return this.firestore.collection(this.ToDoCollectionName).snapshotChanges();
}
changeToDos(index,property,value){
this.toDoArray[index][property] = value;
}
deleteToDos(index){
this.toDoArray.splice(index,1);
}
deleteToDosFirebase(index){
// this.firestore.collection("ToDO").doc(index).delete();
}
addToDos(obj: ToDoInterface){
this.toDoArray.push(obj);
}
addToDosFirebase(obj: ToDoInterface){
return new Promise<any>(((resolve, reject) => {
this.firestore
.collection("ToDo")
.add(obj)
.then(res => console.log('data send!'), err => reject(err))
}))
}
}
And my function what i call in ngOnInit
getToDo(){
this._toDoService.getToDosFirebase().subscribe(items => {
this.toDosFirebase = items;
console.log(items);
});
Maybe i dont know some about rxjs but here data from that func if toDosFirebase: Observable<any[]>; and how i can see it normal for my Interface
In service i hardcode data and all works fine, and my hardcoded data by types equal data from firebase.
Do like in official documentation:
private ToDoCollection: AngularFirestoreCollection<ToDoInterface>;
ToDo: Observable<ToDoIdInterface[]>;
ToDoCollectionName: string = "ToDo";
constructor(private readonly firestore: AngularFirestore) {
this.ToDoCollection = firestore.collection<ToDoInterface>(this.ToDoCollectionName);
this.ToDo = this.ToDoCollection.snapshotChanges().pipe(
map(a => {
const data = a.payload.doc.data() as ToDoInterface; //ERROR
const id = a.payload.doc.id;
return {id, ...data}
}
));
console.log(this.ToDo);
}
In your "getToDosFirebase" method you are calling a ".snapshotChanges();" method.
Actions returned by snapshotChanges are of type DocumentChangeAction
and contain a type and a payload. The type is either added, modified
or removed and the payload contains the document's id, metadata and
data.
In other words you need to map the received data something like this:
.snapshotChanges()
.map(actions => {
return actions.map(a => {
//Get document data
const data = a.payload.doc.data() as Task;
//Get document id
const id = a.payload.doc.id;
//Use spread operator to add the id to the document data
return { id, …data };
});
});
For more information see these links: link1, link2
update
In your "to-do-service.service.ts"
change this line:
ToDo: Observable<ToDoIdInterface[]>;
to this:
ToDo: Observable<any>;
also change this:
map(a => {
to this:
map((a: any) => {
After this the project should build correctly.
Related
I want to display the ngx-wheel using api but I'm having trouble displaying the data.
Here my Service :
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class RestServices {
restEndpoint:string = 'https://gorest.co.in/public/v2/users'
constructor(
private httpClient: HttpClient
) { }
async getServiceId() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
}
return this.httpClient.get<any[]>(this.restEndpoint, httpOptions)
}
Here my Component :
private subscription: Subscription | undefined;
items: any = []
ngOnInit(): void {
this.subscription = this._restService.getServices()
.subscribe((res:any)=>{
let item = res
this.items = item.map((v:any) => ({
text: v.name,
id: v.id,
textFillStyle: "white",
textFontSize: "16"
}));
})
}
ngOnDestroy(): void {
this.subscription?.unsubscribe()
}
Here for html
<ngx-wheel #wheel [width]='350' [height]='350' [spinDuration]='8' [disableSpinOnClick]='true' [items]='items'
[innerRadius]='10' [spinAmount]='10' [textOrientation]='textOrientation' [textAlignment]='textAlignment'
pointerStrokeColor='black' pointerFillColor='white' [idToLandOn]='idToLandOn' (onSpinStart)='before()'
(onSpinComplete)='after()'>
I hope to find the answer here. Thank you
First, you don't need await, async and ,toPromise()... remove them and simply return
return this.httpClient.get<any[]>(this.restEndpoint, httpOptions);
inside your component you should use your constructor only for simple data initialization: if you have to consume a rest api it is a better approach to move that piece of code inside the ngOnInit method:
items: any[] = []
constructor(private restService: RestService){}//dependency injection
ngOnInit(): void {
this.restService.getServiceId().subscribe(response => {
console.log('response success: ', response);
this.items = response; //this may change a little based on your api
console.log('items: ', this.items);
}, errorLog => {
console.log('response error: ', errorLog)
});
}
The above solution is valid, you can enrich it by adding a *ngIf="isLoaded" on your html element and set to true the isLoaded INSIDE subscribe method. but if you prefer you can do the following in the component.ts
items$: Observable<any> = EMPTY;
constructor(private restService: RestService){}//dependency injection
ngOnInit(): void {
this.items$ = this.restService.getServiceId();
}
then, in your html it would change to the following:
<ngx-wheel #wheel *ngIf="items$ | async as items" [width]='350' [height]='350' [spinDuration]='8' [disableSpinOnClick]='true' [items]='items'
[innerRadius]='10' [spinAmount]='10' [textOrientation]='textOrientation' [textAlignment]='textAlignment'
pointerStrokeColor='black' pointerFillColor='white' [idToLandOn]='idToLandOn' (onSpinStart)='before()'
(onSpinComplete)='after()'>
I am working on the backend of a project using Nestjs and everything has been okay until now that I found out an endpoint is giving an issue when I tested it on postman. Whenever I try to create using this endpoint, it gives this error: "TypeError: relatedEntities.forEach is not a function". I have tried my best to fix this but was not successful.
assessment entity
#Entity()
export class Result {
//...
#Column({ nullable: true })
summary: string;
#OneToMany(() => Assessment, (assessment) => assessment.result)
assessments: Assessment[];
//...
}
result entity
#Entity()
export class Assessment {
//...
#Column({ nullable: true })
format: string;
#ManyToOne(() => Result, (result) => result.assessments)
result: Result;
//...
}
result repository
#EntityRepository(Result)
export class ResultRepository extends Repository<Result> {
async createResult(createResultDto: CreateResultDto): Promise<Result> {
const { name, summary, assessments, comment } = createResultDto;
const result = new Result();
result.name = name;
result.assessments = assessments;
result.summary = summary;
result.comment = comment;
return this.save(result);
//more code...
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm getting a weird error where when I console log my array value it returns as [object Set]. I'm unsure if this is happening in the component or the service, but I'm not getting the object as values.
This is happening when the deleteRow function is being called from the component and passed to the service.
view.component.ts
#Component({
templateUrl: "viewpage.component.html"
})
export class ViewpageComponent implements AfterViewInit, OnInit, OnDestroy {
viewData: any;
viewName: string;
viewTag: number;
fetchedData: any;
dataSource: ViewDataSource;
pageSizeOptions: number[] = [10, 20, 50];
defaultSortCol = "1";
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
selection = new SelectionModel<TableRow>(true, []);
displayedColumns: string[] = [];
navSub: any;
primaryTableValue: any;
constructor(
private actionService: ActionService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
// Init the component the first time it is navigated to.
this.initData();
// Subscribe to the router, so that any new navigation to this component loads new data.
this.navSub = this.router.events.subscribe((e: any) => {
if (e instanceof NavigationEnd) {
this.initData();
}
});
}
initData() {
this.viewTag = +this.route.snapshot.paramMap.get("tag");
this.dataSource = new ViewDataSource(this.actionService);
// if (this.viewData) {
// console.log(this.viewData);
// }
// Load the View from the DataSource with default params
this.dataSource.loadView(this.viewTag, 0, 10, this.defaultSortCol, "asc");
// Subscribe to the View in the DataSource
this.dataSource.view.subscribe(x => {
if (x.ActionName) {
x.ColumnIds.unshift("9");
this.viewData = x;
this.fetchedData = this.viewData.TableData;
this.primaryTableValue = (this.viewData.ViewData.DbrAction.PrimaryTable);
}
});
}
ngAfterViewInit() {
// After sorting, jump back to first page of newly sorted data.
this.sort.sortChange.subscribe(() => {
this.paginator.pageIndex = 0;
});
// Sort changes and pagination events should reload the page.
merge(this.sort.sortChange, this.paginator.page)
.pipe(tap(() => this.loadPage()))
.subscribe();
}
loadPage() {
this.dataSource.loadView(
this.viewTag,
// '',
this.paginator.pageIndex,
this.paginator.pageSize,
this.sort.active,
this.sort.direction
);
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.view['source']['value'].TableData;
return numSelected === numRows.length;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected()
? this.selection.clear()
: this.dataSource.view['source']['value'].TableData.forEach((row: TableRow) =>
this.selection.select(row)
);
}
// Delete row functionality
deleteRow() {
this.selection.selected.forEach(item => {
const index: number = this.dataSource.view['source']['value'].TableData.filter (
(d: TableRow) => d === item
);
this.dataSource.view['source']['value'].TableData.splice(index, 1);
this.dataSource = new ViewDataSource(this.dataSource.view['source']['value'].TableData);
});
this.selection = new SelectionModel<TableRow>(true, []);
this.actionService.deleteRow(this.selection, this.primaryTableValue).subscribe(response => {
console.log("Success!");
});
}
view.service.ts
#Injectable({ providedIn: 'root' })
export class ActionService {
private actionSource = new BehaviorSubject<any>([]);
currentAction = this.actionSource.asObservable();
private refNumSubject = new BehaviorSubject<any>([]);
currentRef = this.refNumSubject.asObservable();
// private dataSource = new BehaviorSubject<any>([]);
// currentPrimaryNumber = this.dataSource.asObservable();
currentRefNumber: number;
// This is for saving states of views:
public stateMap: Map<string, ActionState>;
public refNumber: number;
viewData: any;
constructor(private http: HttpClient, private router: Router) {
// Init the stateMap
this.stateMap = new Map<string, ActionState>();
this.refNumber = 0;
}
// Http Options
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
// Loads a page of an Action after retrieving data from the API.
// ##BOOKMARK
loadPage(actionTag: number, pageIndex: number, pageSize: number, sortCol: string, sortDirection: string): Observable<any> {
let user = JSON.parse(localStorage.getItem('currentUser'));
return this.http.post<any>('/actions/actionPage/',
{
SessionId: user.userEnv.sessionId,
ActionTag: { value: actionTag },
SortDirection: sortDirection,
SortCol: +sortCol,
PageIndex: pageIndex,
PageSize: pageSize,
Filter: ""
}).pipe(map(actionData => {
const actionObj = JSON.parse(actionData);
// Cacheing stuff:
// this.refNumSubject.next(this.refNumber);
// let actionState = new ActionState(this.refNumber++, actionTag, pageIndex, pageSize, +sortCol, sortDirection);
// this.cacheAction(actionState);
//
return actionObj;
}));
}
// Delete Row ##TEST
deleteRow(selection: any, primaryTable: any): Observable<any> {
const user = JSON.parse(localStorage.getItem('currentUser'));
const indices = [selection._selection].map((row: { value: any; }) => `${row}`);
console.log(`Session Id Value in Service: ` + user.userEnv.sessionId);
console.log(`Primary Table Value in Service: ` + primaryTable);
console.log(`Row Selection Value in Service: ` + indices);
return this.http.post<any>('/actions/deleteRow/',
{
sessionId: user.userEnv.sessionId,
table: primaryTable,
Tag: indices
}).pipe(map(deleteRowObject => {
const deleteRowReturn = JSON.parse(deleteRowObject);
console.log(`test delete ` + deleteRowReturn);
return deleteRowReturn;
}));
}
'[object object]' because of the + operator, it calls to the toString method of the object
using like this:
console.log('scope is ' + scope);
Produced the string scope is [object object]
Instead use the console.log() method with commas to be able to print the object
console.log('scope is', scope)
I would like to set a resolver, on an individual field that returns a string.
For this example. I want to take the title attribute, and make it .toUpperCase
Schema
type Product {
title(uppercase:Boolean!): String!
}
type Query {
products: [Product]
}
Resolver
Query: {
products: () => [{title:'foo'}],
products.title: (stringToRtn, { action }) => {
return action ? stringToRtn.toUpperCase : stringToRtn
}
}
Here is the solution:
const resolvers = {
Product: {
title: product => {
return product.title.toUpperCase();
}
},
Query: {
products: () => [{title:'foo'}]
}
};
If you're using TypeScript, use these typeDefs:
type Product {
title: String!
}
type Query {
products: [Product]
}
Another way is to use custom directive like "#upperCase", but it's too complex for this.
TypeScript update directive way
Remove : GraphQLField<any, any> if you're not using TypeScript.
#uppercase directive implementation:
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { GraphQLField, defaultFieldResolver } from 'graphql';
class UppercaseDirective extends SchemaDirectiveVisitor {
public visitFieldDefinition(field: GraphQLField<any, any>) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function resolver(...args) {
const result = resolve.apply(this, args);
if (typeof result === 'string') {
return result.toUpperCase();
}
return result;
};
}
}
export { UppercaseDirective };
If you're using TypeScript use these typeDefs:
const typeDefs: string = `
enum Status {
SOLD_OUT
NO_STOCK
OUT_OF_DATE #deprecated(reason: "This value is deprecated")
}
type Book {
id: ID!
title: String #uppercase
author: String
status: Status
name: String #deprecated(reason: "Use title instead")
}
type Query {
books: [Book]!
bookByStatus(status: Status!): [Book]!
}
`;
schema:
(Remove : GraphQLSchema if you're not using TypeScript.)
const schema: GraphQLSchema = makeExecutableSchema({
typeDefs,
resolvers,
schemaDirectives: {
deprecated: DeprecatedDirective,
uppercase: UppercaseDirective
}
});
Here is a link to source code using TypeScript
I find allready some posts on google where people solve this problem. but i cant reproduce the solutions on my project.
My Interface:
declare module PlatformInterface {
export interface Design {
primaryColor: string;
backgroundImage: string;
}
export interface Saga {
id: string;
name: string;
short_desc: string;
desc: string;
manga: Manga[];
anime: Anime[];
}
export interface Root {
id: string;
name: string;
design: Design[];
saga: Saga[];
}
}
My Model:
export class PlatformModel implements PlatformInterface.Root {
id: string;
name: string;
design = [];
saga = [];
constructor(obj?: any) {
this.id = obj.name.toLowerCase().replace(' ', '-');
this.name = obj.name;
this.design = obj.design;
this.saga = obj.saga;
}
}
My Service:
#Injectable()
export class PlatformService {
public list$: Observable<PlatformModel[]>;
private _platform: AngularFirestoreCollection<PlatformModel>;
constructor(db: AngularFirestore) {
this._platform = db.collection<PlatformModel>('platforms');
this.list$ = this._platform.valueChanges();
}
/** Get Platform by id */
get(id: string): Observable<PlatformModel> {
return this._platform.doc<PlatformModel>(id).valueChanges();
}
/** Add / Update Platform */
set(id: string, platforms: PlatformModel) {
return fromPromise(this._platform.doc(id).set(platforms));
}
/** Remove Platform */
remove(id: string) {
return fromPromise(this._platform.doc(id).delete());
}
}
My function in Component.ts
constructor(public _platformService: PlatformService) {
}
addPlatform(name: string) {
if (name !== '') {
const platform = new PlatformModel({
name: name,
design: [],
saga: []
});
this._platformService.set(platform.id, platform).subscribe();
}
}
The Angular Compiler dont Throw any error, But when i try to fire the addPlatform Function i get in Browser this error:
ERROR Error: Function DocumentReference.set() called with invalid data. Data must be an object, but it was: a custom PlatformModel object
The Errors Says that the Data must be an object, but it is allready an object or not? i mean i define in the service it with:
public list$: Observable<PlatformModel[]>;
[] Makes it to an object or not?
I've found some clarification here Firestore: Add Custom Object to db
while firebase could send the data inside your object to the database, when the data comss back it cannot instantiate it back into an instance of your class. Therefore classes are disallowed
my workaround for custom class was
this.db.collection(`${this.basePath}/`).doc(custom_class.$key)
.set(Object.assign({}, JSON.parse(JSON.stringify(custom_class))))
.then( ret => {
log.debug('file added', ret);
}).catch( err => {
log.error(err);
});
so I guess in your case it would be
/** Add / Update Platform */
set(id: string, platforms: PlatformModel) {
return fromPromise(this._platform.doc(id).set(Object.assign({},JSON.parse(JSON.stringify(platforms))));
}
For adding a Map into Firestore document you'll have to use:
Object.assign({}, JSON.parse(JSON.stringify(YOUR_MAP)))