So, when i get the info from the MongoDB database in a NestJS service, i want to return it to the controller and send it to the client (Angular)
I tried returning it with a Promise but i cant get it working.
Heres my code with the thing a tried:
Service:
import { Injectable } from "#nestjs/common";
import { InjectModel } from "#nestjs/mongoose";
import mongoose, { Model } from "mongoose";
import {
FriendRequest,
FriendRequestDocument,
} from "../schema/friend-requests.schema";
import { Users, UsersDocument } from "../schema/user.schema";
import { FriendsDto } from "./dto/friends.dto";
#Injectable()
export class AddFriendService {
constructor(
#InjectModel(Users.name) private readonly usersModel: Model<UsersDocument>,
#InjectModel("friendRequests")
private readonly friendrequestModel: Model<FriendRequestDocument>
) {}
async getRequests(userid: string) {
let incomingrqs = new Promise((resolve, reject) => {
let response = {
incoming: [],
};
this.friendrequestModel
.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "userdata",
// pipeline: [
// {
// $match: {
// friend_id: new mongoose.Types.ObjectId(userid)
// }
// }
// ],
},
},
])
.exec((err, data) => {
if (err) {
console.log(err);
}
if (!data) {
console.log("No data returned");
}
data.forEach((rqsData) => {
response.incoming.push({
userid: rqsData.userdata[0]._id,
username: rqsData.userdata[0].username,
created_at: rqsData.userdata[0].created_at,
pfp: "/assets/cdn/pfp/" + rqsData.userdata[0].pfp,
});
});
});
resolve(response);
})
incomingrqs.then(x => {
return x;
})
}
}
Controller:
import { Get, Controller, Body, Post, Param } from "#nestjs/common";
import { AddFriendService } from "./friends.service";
import { FriendsDto } from "./dto/friends.dto";
#Controller("friends")
export class AddFriendController {
constructor(private friendsService: AddFriendService) {}
#Post("rqs")
async getRqs(#Body() friendsDto: FriendsDto){
let rqs = await this.friendsService.getRequests(friendsDto.userid);
console.log(rqs)
return rqs;
}
}
FriendRequestDocument:
import { Prop, Schema, SchemaFactory } from "#nestjs/mongoose";
import mongoose, { Document, ObjectId } from "mongoose";
export type FriendRequestDocument = FriendRequest & Document;
#Schema({collection: "friendRequests"})
export class FriendRequest {
#Prop()
author: mongoose.Types.ObjectId;
#Prop()
friend_id: mongoose.Types.ObjectId;
#Prop()
request_at: Date;
}
export const FriendRequestSchema = SchemaFactory.createForClass(FriendRequest);
Can you help me? Thanks!
aggregate.exec can already return a promise if no callback is given, just FYI.
You need to return the incomingrqs to the controller. This is standard of how promises work.. Returning inside the then forwards the return on to the next promise, which is why the incomingrqs needs to be returned as well. Otherwise, the controller will never know what the service returns.
Related
I am current having a quick startup on an Ant Design Pro project, when I try to implement translation with useIntl function from umi, it always give me a Invalid hook call error. I tried several workarounds to fix it but failed.
Here are my codes:
src/pages/user/login/model.ts
import { Effect, history, Reducer, useIntl } from 'umi';
import { message } from 'antd';
import { parse } from 'qs';
import { fakeAccountLogin, getFakeCaptcha } from './service';
import { extend } from 'lodash';
export function getPageQuery() {
return parse(window.location.href.split('?')[1]);
}
export function setAuthority(authority: string | string[]) {
const proAuthority = typeof authority === 'string' ? [authority] : authority;
localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority));
// hard code
// reload Authorized component
try {
if ((window as any).reloadAuthorized) {
(window as any).reloadAuthorized();
}
} catch (error) {
// do not need do anything
}
return authority;
}
export interface StateType {
status?: 'ok' | 'error';
type?: string;
currentAuthority?: 'user' | 'guest' | 'admin';
}
export interface ModelType {
namespace: string;
state: StateType;
effects: {
login: Effect;
getCaptcha: Effect;
};
reducers: {
changeLoginStatus: Reducer<StateType>;
};
}
const Model: ModelType = {
namespace: 'userAndlogin',
state: {
status: undefined,
},
effects: {
*login({ payload }, { call, put }) {
const response = yield call(fakeAccountLogin, payload);
yield put({
type: 'changeLoginStatus',
payload: response,
});
// Login successfully
if (response.status === 'ok') {
const intl = useIntl();
// Error Here //
message.success(intl.formatMessage({ id: 'userandlogin.login.success' }));
const urlParams = new URL(window.location.href);
const params = getPageQuery();
let { redirect } = params as { redirect: string };
if (redirect) {
const redirectUrlParams = new URL(redirect);
if (redirectUrlParams.origin === urlParams.origin) {
redirect = redirect.substr(urlParams.origin.length);
if (redirect.match(/^\/.*#/)) {
redirect = redirect.substr(redirect.indexOf('#') + 1);
}
} else {
window.location.href = redirect;
return;
}
}
history.replace(redirect || '/');
}
},
*getCaptcha({ payload }, { call }) {
yield call(getFakeCaptcha, payload);
},
},
reducers: {
changeLoginStatus(state, { payload }) {
setAuthority(payload.currentAuthority);
return {
...state,
status: payload.status,
type: payload.type,
};
},
},
};
export default Model;
The error is from the line
message.success(intl.formatMessage({ id: 'userandlogin.login.success' }));
Initially I thought it might cause by I used the React function in the Typescript file, so I tried to call the message.success in another global service through event, but the same thing happened, so I guess, is that any mistake I made in declaring the const intl in a model response part (maybe not the actual phrase for it, if not understand I can explain further)?
Edited 1:
As references, here is the source of the original project.
Ant Design Pro
Found Solution
getIntl(getLocale()).formatMessage({id:''});
From: Github
Array not passing from service to component:
In the test() function on the service.ts page, google calendar data is successfully being read and pushed to an array called response. All the data logs.
When lesson-summary.component.ts calls on the test() function, the response array data does not show up in the lesson-summary.component.html
Thanks for any help!
google-calendar.service.ts
import { Injectable, Directive } from "#angular/core";
import * as moment from "moment-timezone";
declare var gapi: any;
#Injectable({
providedIn: "root"
})
export class GoogleCalendarService {
private response = [];
constructor() { }
test() {
gapi.load("client", () => {
gapi.client.init({
apiKey: "API string here",
discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"]
}).then(() => {
var month = moment().month();
const firstOfMonth = moment().startOf("month").format("YYYY-MM-DD hh:mm");
const lastOfMonth = moment().endOf("month").format("YYYY-MM-DD hh:mm");
var firstOfMonthUTC = moment.tz(firstOfMonth, "America/Toronto").format();
var lastOfMonthUTC = moment.tz(lastOfMonth, "America/Toronto").format();
return gapi.client.calendar.events.list({
calendarId: "calendar id here",
timeMax: lastOfMonthUTC,
timeMin: firstOfMonthUTC,
singleEvents: true
});
})//end of .then
.then((data) => {
this.response.push.apply(this.response, data.result.items);
console.log(data.result.items, "data.result.items");
return this.response;
});//end of .then
});//end of .load
}//end of test
}//end of export
lesson-summary.component.ts
import { Component, OnInit } from "#angular/core";
import { Observable } from "rxjs";
import { GoogleCalendarService } from "../google-calendar.service";
declare var gapi: any;
#Component({
selector: "app-lesson-summary",
templateUrl: "./lesson-summary.component.html",
styleUrls: ["./lesson-summary.component.css"]
})
export class LessonSummaryComponent implements OnInit {
private response;
constructor(
private calendarService: GoogleCalendarService) {
this.response = this.calendarService.test();
}
ngOnInit() {
}
}
lesson-summary.component.html
<ul>
<li *ngFor = "let item of response">
{{ item.summary }}
</li>
</ul>
That's because you're mixing promises and sync functions in an incorrect way, so the test() function will not return anything.
Try adding a promise to your test():
test() {
return new Promise(resolve => { // <-- now test func return promise
gapi.load("client", () => {
gapi.client.init({
apiKey: "API string here",
discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"]
}).then(() => {
// code...
}).then((data) => {
// code...
resolve(this.response); // <-- when we have the response, we are resolving the promise
});
});
});
}
And then use this promise in the component:
this.calendarService.test().then(data => this.response = data);
Learn more about promises on MDN
I'm currently getting an error that says "can't read property 'get' of null" which is because I'm passing in null as my first argument to the FirmService constructor in my beforeEach in my spec file...what is the best way to mock or pass in http to my service here?
#Injectable
export class FirmService {
public stateObservable: Observable<FirmState>;
constructor(private: $http: AuthHttp, private store: Store<FirmState>) {
this.stateObservable = this.store.select('firmReducer');
}
public getFirms(value?: string) {
return this.$http.get('/api/firm').map((response: Response) => {
this.store.dispatch({
type: firmActions.GET_FIRMS,
payload: response.json()
});
return;
}
}
}
Here is my unit test for the above service:
import {Store} from '#ngrx/store';
import {FirmService} from './firm.service'
import {firmActions} from './firm.reducer'
import {FirmState} from './firm.state'
import {HttpModule, Http, Response, ResponseOptions, XHRBackend} from 'angular/http';
import {MockBackend, MockConnection} from '#angular/http/testing';
class MockStore extends Store<FirmState> {
constructor() {
super(null, null, null)
}
public dispatch () {
return undefined;
}
}
describe('firm actions', () => {
it('getFirms should dispatch the GET_FIRMS action', () => {
let connection: MockConnection;
const expectedAction = {
type: firmActions.GET_FIRMS
payload: undefined
}
const mockBackendResponse = (connection: MockConnection, response: string) => {
connection.mockRespond(new Response(new ResponseOptions({ body: response })));
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
{provide: XHRBackend, useClass: MockBackend}
]
});
spyOn(mockStore, 'dispatch');
firmService.getFirms().subscribe(result => {
expect(mockStore.dispatch).toHaveBeenCalled();
expect(mockStore.dispatch).toHaveBeenCalledWith(expectedAction);
};
}
}
}
you can try something using MockBackend and MockConnection from angular's http/testing library:
import { ResponseOptions, Response, XHRBackend, HttpModule } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
const mockBackendResponse = (connection: MockConnection, response: string) => {
connection.mockRespond(new Response(new ResponseOptions({ body: response })));
};
// test module configuration for each test
const testModuleConfig = () => {
TestBed.configureTestingModule({
imports: [
//.. your required modules for this test,
HttpModule, RouterTestingModule
],
providers: [
// required services,
{ provide: XHRBackend, useClass: MockBackend }
]
});
};
then before each test:
beforeEach(() => {
injector = getTestBed();
backend = <any>injector.get(XHRBackend);
store = injector.get(Store);
// sets the connection when someone tries to access the backend with an xhr request
backend.connections.subscribe((c: MockConnection) => connection = c);
// construct after setting up connections above
firmService = injector.get(FirmService);
});
sample test using an Array of Items as result:
t.it('should search', () => {
let list: Array<Item> = []; // ... your sample mock entity with fields
observer.subscribe(result => {
expect(result).toEqual(new SearchedAction(list));
});
// mock response after the xhr request (which happens in constructor), otherwise it will be undefined
let expectedJSON:string = JSON.stringify(list);
mockBackendResponse(connection, expectedJSON);
}
I am new to Angular (and Javascript for that matter). I've written an Angular service which returns an array of users. The data is retrieved from an HTTP call which returns the data in JSON format. When logging the JSON data returned from the HTTP call, I can see that this call is successful and the correct data is returned. I have a component which calls the service to get the users and an HTML page which displays the users. I cannot get the data from the service to the component. I suspect I am using the Observable incorrectly. Maybe I'm using subscribe incorrectly as well. If I comment out the getUsers call in the ngInit function and uncomment the getUsersMock call, everything works fine and I see the data displayed in the listbox in the HTML page. I'd like to convert the JSON data to an array or list of Users in the service, rather then returning JSON from the service and having the component convert it.
Data returned from HTTP call to get users:
[
{
"firstName": "Jane",
"lastName": "Doe"
},
{
"firstName": "John",
"lastName": "Doe"
}
]
user.ts
export class User {
firstName: string;
lastName: string;
}
user-service.ts
...
#Injectable
export class UserService {
private USERS: User[] = [
{
firstName: 'Jane',
lastName: 'Doe'
},
{
firstName: 'John',
lastName: 'Doe'
}
];
constructor (private http: Http) {}
getUsersMock(): User[] {
return this.USERS;
}
getUsers(): Observable<User[]> {
return Observable.create(observer => {
this.http.get('http://users.org').map(response => response.json();
})
}
...
user.component.ts
...
export class UserComponent implements OnInit {
users: User[] = {};
constructor(private userService: UserService) {}
ngOnInit(): void {
this.getUsers();
//this.getUsersMock();
}
getUsers(): void {
var userObservable = this.userService.getUsers();
this.userObservable.subscribe(users => { this.users = users });
}
getUsersMock(): void {
this.users = this.userService.getUsersMock();
}
}
...
user.component.html
...
<select disabled="disabled" name="Users" size="20">
<option *ngFor="let user of users">
{{user.firstName}}, {{user.lastName}}
</option>
</select>
...
!!! UPDATE !!!
I had been reading the "heroes" tutorial, but wasn't working for me so I went off and tried other things. I've re-implemented my code the way the heroes tutorial describes. However, when I log the value of this.users, it reports undefined.
Here is my revised user-service.ts
...
#Injectable
export class UserService {
private USERS: User[] = [
{
firstName: 'Jane',
lastName: 'Doe'
},
{
firstName: 'John',
lastName: 'Doe'
}
];
constructor (private http: Http) {}
getUsersMock(): User[] {
return this.USERS;
}
getUsers(): Promise<User[]> {
return this.http.get('http://users.org')
.toPromise()
.then(response => response.json().data as User[])
.catch(this.handleError);
}
...
Here is my revised user.component.ts
...
export class UserComponent implements OnInit {
users: User[] = {};
constructor(private userService: UserService) {}
ngOnInit(): void {
this.getUsers();
//this.getUsersMock();
}
getUsers(): void {
this.userService.getUsers()
.then(users => this.users = users);
console.log('this.users=' + this.users); // logs undefined
}
getUsersMock(): void {
this.users = this.userService.getUsersMock();
}
}
...
!!!!!!!!!! FINAL WORKING SOLUTION !!!!!!!!!!
This is all the files for the final working solution:
user.ts
export class User {
public firstName: string;
}
user.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { User } from './user';
#Injectable()
export class UserService {
// Returns this JSON data:
// [{"firstName":"Jane"},{"firstName":"John"}]
private URL = 'http://users.org';
constructor (private http: Http) {}
getUsers(): Observable<User[]> {
return this.http.get(this.URL)
.map((response:Response) => response.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
user.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { User } from './user';
import { UserService } from './user.service';
#Component({
moduleId: module.id,
selector: 'users-list',
template: `
<select size="5">
<option *ngFor="let user of users">{{user.firstName}}</option>
</select>
`
})
export class UserComponent implements OnInit{
users: User[];
title = 'List Users';
constructor(private userService: UserService) {}
getUsers(): void {
this.userService.getUsers()
.subscribe(
users => {
this.users = users;
console.log('this.users=' + this.users);
console.log('this.users.length=' + this.users.length);
console.log('this.users[0].firstName=' + this.users[0].firstName);
}, //Bind to view
err => {
// Log errors if any
console.log(err);
})
}
ngOnInit(): void {
this.getUsers();
}
}
Take a look at your code :
getUsers(): Observable<User[]> {
return Observable.create(observer => {
this.http.get('http://users.org').map(response => response.json();
})
}
and code from https://angular.io/docs/ts/latest/tutorial/toh-pt6.html
(BTW. really good tutorial, you should check it out)
getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then(response => response.json().data as Hero[])
.catch(this.handleError);
}
The HttpService inside Angular2 already returns an observable, sou don't need to wrap another Observable around like you did here:
return Observable.create(observer => {
this.http.get('http://users.org').map(response => response.json()
Try to follow the guide in link that I provided. You should be just fine when you study it carefully.
---EDIT----
First of all WHERE you log the this.users variable? JavaScript isn't working that way. Your variable is undefined and it's fine, becuase of the code execution order!
Try to do it like this:
getUsers(): void {
this.userService.getUsers()
.then(users => {
this.users = users
console.log('this.users=' + this.users);
});
}
See where the console.log(...) is!
Try to resign from toPromise() it's seems to be just for ppl with no RxJs background.
Catch another link: https://scotch.io/tutorials/angular-2-http-requests-with-observables Build your service once again with RxJs observables.
I want to develop a file configuration of json and it is called with http get the constructor and return the value I want the config file to another component. But when return gives me value undefined.
My Config.json
[ {"urlServer": "http://localhost:56877"}]
My Config.Service
export class configService
{
url: string;
constructor(public _http: Http)
{
let injector = Injector.resolveAndCreate([loggerService]);
let logger = injector.get(loggerService);
try {
return this._http.get('/app/config.json',
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <configModel>res.json();
this.url = data.urlServer;
JSON.stringify(this.url);
});
}
catch (ex) {
logger.registarErros('configService', ex);
}
}
returnConfig()
{
return this.url;
}
Now my other Component
constructor(public _http: Http, public config: configService)
{
this.token = sessionStorage.getItem('token');
this.username = sessionStorage.getItem('username');
}
login(username: String, password: String)
{
let injector = Injector.resolveAndCreate([loggerService]);
let logger = injector.get(loggerService);
try
{
alert(this.config.url);
return this._http.post('http://localhost:56877/api/Login/EfectuaLogin', JSON.stringify({ username, password }),
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <authLoginModel>res.json();
this.token = data.token;
this.username = data.nome;
sessionStorage.setItem('token', this.token);
sessionStorage.setItem('username', this.username);
return Observable.of('authObservable');
});
}
catch (ex) {
logger.registarErros('authentication', ex);
}
}
I no longer know how to solve the problem, I need your help, I'm not very experienced with Angular 2.
Thanks very much.
The problem here is that the config is load asynchronously. You could use something like that leveraging the flatMap operator:
#Injectable()
export class ConfigService {
urlServer:string;
constructor(public _http: Http) {
}
getConfig() {
if (this.urlServer) {
return Observable.of(this.urlServer);
}
return this._http.get('/app/config.json', {
headers: contentHeaders
})
.map((res: any) => {
let data = <configModel>res.json();
return data.urlServer;
}).do(urlServer => {
this.urlServer = urlServer;
});
}
}
and in your component:
login(username: String, password: String) {
return this.configService.getConfig().flatMap(urlServer => {
this._http.post('http://localhost:56877/api/Login/EfectuaLogin',
JSON.stringify({ username, password }),
{
headers: contentHeaders
})
.map((res: any) =>
{
let data = <authLoginModel>res.json();
this.token = data.token;
this.username = data.nome;
sessionStorage.setItem('token', this.token);
sessionStorage.setItem('username', this.username);
return data; // or something else
});
}
});
}
Another approach would be boostrap asynchronously after having loaded the configuration:
var app = platform(BROWSER_PROVIDERS)
.application([BROWSER_APP_PROVIDERS, appProviders]);
service.getConfig().flatMap((url) => {
var configProvider = new Provider('urlServer', { useValue: urlServer});
return app.bootstrap(appComponentType, [ configProvider ]);
}).toPromise();
See this question for the second approach:
angular2 bootstrap with data from ajax call(s)
You can go further by mixing the last approach with a CustomRequestOptions:
import {BaseRequestOptions, RequestOptions, RequestOptionsArgs} from 'angular2/http';
export class CustomRequestOptions extends BaseRequestOptions {
merge(options?:RequestOptionsArgs):RequestOptions {
options.url = 'http://10.7.18.21:8080/api' + options.url;
return super.merge(options);
}
}
See this question:
Angular 2 - global variable for all components