Auth.service.ts
import { Injectable } from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { SearchUserOutput } from 'src/users/dto/search-user.output';
import { UsersService } from '../users/users.service';
#Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(username: string, password: string): Promise<SearchUserOutput> {
const user = await this.usersService.findOne(username);
if (user && user.password == password) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: SearchUserOutput,role:string) {
return {
accessToken: this.jwtService.sign({
username: user.username,
role: role
}),
user,
};
}
}
Auth.resolver.ts
import { UseGuards } from '#nestjs/common';
import { Args, Context, Mutation, Resolver } from '#nestjs/graphql';
import { AuthService } from './auth.service';
import { LoginUserInput } from './dto/login-user.input';
import { LoginResponse } from './dto/login-response.ouput';
import { GqlAuthGuard } from './guards/gql-auth.guard';
#Resolver()
export class AuthResolver {
constructor(private authService: AuthService) {}
#Mutation(() => LoginResponse)
#UseGuards(GqlAuthGuard)
loginUser(
#Args('loginUserInput') loginUserInput: LoginUserInput,
#Context() context
): Promise<LoginResponse> {
return this.authService.login(context.user,'user');
}
}
Auth.module.ts
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { MongooseModule } from '#nestjs/mongoose';
import { PassportModule } from '#nestjs/passport';
import { UserSchema } from 'src/users/entities/user.entity';
import { UsersModule } from 'src/users/users.module';
import { AuthResolver } from './auth.resolver';
import { AuthService } from './auth.service';
import { JwtStrategy } from './strategies/jwt.strategy';
import { LocalStrategy } from './strategies/local.strategy';
#Module({
imports: [PassportModule,UsersModule,JwtModule.register({
secret: 'secret',
signOptions: { expiresIn: '30d' },
}),MongooseModule.forFeature([{ name: 'User', schema: UserSchema }])],
providers: [AuthResolver, AuthService,LocalStrategy,JwtStrategy]
})
export class AuthModule {}
local.strategy.ts
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { AuthService } from '../auth.service';
import { Strategy } from 'passport-local';
import { SearchUserOutput } from 'src/users/dto/search-user.output';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise<SearchUserOutput> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
gql-authh.guard.ts
import { ExecutionContext, Injectable } from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class GqlAuthGuard extends AuthGuard('local') {
constructor() {
super();
}
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const request = ctx.getContext();
request.body = ctx.getArgs().loginUserInput;
return request;
}
}
login-response.output.ts
import { Field, ObjectType } from '#nestjs/graphql';
import { SearchUserOutput } from 'src/users/dto/search-user.output';
#ObjectType()
export class LoginResponse {
#Field(() => String)
accessToken: string;
#Field(() => SearchUserOutput)
user: SearchUserOutput;
}
login-user.input.ts
import { Field, InputType } from '#nestjs/graphql';
import { IsNotEmpty, IsString } from 'class-validator';
#InputType()
export class LoginUserInput {
#Field(() => String)
#IsNotEmpty()
#IsString()
username: string;
#Field(() => String)
#IsNotEmpty()
#IsString()
password: string;
}
it consistently giving me error
if you want source code then i will send you
"dependencies": {
"#nestjs/apollo": "^10.0.19",
"#nestjs/common": "^9.0.0",
"#nestjs/core": "^9.0.0",
"#nestjs/graphql": "^10.0.21",
"#nestjs/jwt": "^9.0.0",
"#nestjs/mapped-types": "*",
"#nestjs/mongoose": "^9.2.0",
"#nestjs/passport": "^9.0.0",
"#nestjs/platform-express": "^9.0.0",
"#nestjs/testing": "^9.0.11",
"#types/jest": "^28.1.7",
"#types/node": "^18.7.6",
"#types/passport-jwt": "^3.0.6",
"apollo-server-express": "^3.10.1",
"class-validator": "^0.13.2",
"express": "^4.18.1",
"graphql": "^15",
"mongoose": "^6.5.2",
"passport": "^0.6.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"type-graphql": "^1.1.1",
"webpack": "^5.74.0"
},
"devDependencies": {
"#types/express": "^4.17.13",
"#types/passport-local": "^1.0.34",
"#types/supertest": "^2.0.11",
"#typescript-eslint/eslint-plugin": "^5.0.0",
"#typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.2",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.5",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5"
},
Related
im getting this error Cannot return null for non-nullable field CityBoundaries.city_name. from mongoDB, but if im using static json data, i get my response fine. Here's my code. Any help will be appreciated :)
city-boundries.model.ts
import { ObjectType, Field, ID, Int, Float } from '#nestjs/graphql';
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
import 'dotenv/config';
import { CityBoundiresFeatures } from 'src/types/city-boundries-features';
import mongoose from 'mongoose';
export type CityBoundriesDocument = CityBoundaries & Document;
#Schema()
#ObjectType()
export class CityBoundaries extends Document {
#Field(() => ID, { nullable: true })
_id: string | number;
#Prop()
#Field()
readonly city_admin_level: number;
#Prop({ required: true })
#Field()
readonly city_name: string;
#Prop({ required: true })
#Field(() => Float)
readonly city_id: number;
#Prop({ required: true })
#Field(() => Float)
readonly city_osm_id: number;
#Prop({
required: true,
type: mongoose.Schema.Types.ObjectId,
ref: 'CityBoundiresFeatures',
})
#Field(() => [CityBoundiresFeatures], { nullable: true })
features: CityBoundiresFeatures[];
#Prop({ required: true })
#Field({ nullable: true })
type: string;
}
export const CityBoundriesSchema = SchemaFactory.createForClass(CityBoundaries);
city-boundries.module.ts
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import {
CityBoundaries,
CityBoundriesSchema,
} from 'src/models/city-boundries.model';
import { CityBoundriesService } from 'src/services/city-boundries.service';
import { CityBoundriesResolvers } from 'src/resolvers/city-boundries.resolver';
import { Bop500_City, CitiesSchema } from 'src/models/cities.model';
import { CitiesService } from 'src/services/cities.service';
#Module({
imports: [
MongooseModule.forFeature([
{
name: CityBoundaries.name,
schema: CityBoundriesSchema,
},
{ name: Bop500_City.name, schema: CitiesSchema },
]),
],
providers: [CityBoundriesService, CityBoundriesResolvers, CitiesService],
})
export class CityBoundriesModule {}
cities.resolver.ts
import { Resolver, Query, Args, ResolveField, Parent } from '#nestjs/graphql';
import { Bop500_City } from 'src/models/cities.model';
import { CityBoundaries } from 'src/models/city-boundries.model';
import { CitiesService } from 'src/services/cities.service';
import { CityBoundriesService } from 'src/services/city-boundries.service';
#Resolver(() => Bop500_City)
export class CitiesResolvers {
constructor(
private readonly citiesService: CitiesService,
private readonly cityBoundariesService: CityBoundriesService,
) {}
#Query(() => [Bop500_City])
/**
* #returns list of all cities
*/
async getAllCities() {
return this.citiesService.findMany();
}
#Query(() => [Bop500_City], { nullable: true })
/**
* #param {string} value take city name as argument from user
* #returns detail of city
*/
async getCity(#Args('city') value: string) {
const res = await this.citiesService.findOneByName(value);
console.log('Response: ', res);
return res;
}
#ResolveField(() => CityBoundaries)
async getBoundries(#Parent() parent: Bop500_City) {
const res = await this.cityBoundariesService.findOneByCityName(
parent.city_name,
);
console.log('Response: ', res);
return res;
}
}
city-boundries.service.ts
import { Injectable } from '#nestjs/common';
import { Model } from 'mongoose';
import { InjectModel } from '#nestjs/mongoose';
import {
CityBoundaries,
CityBoundriesDocument,
} from 'src/models/city-boundries.model';
import * as cityBoundriesData from '../static/City_boundaries.json';
#Injectable()
export class CityBoundriesService {
constructor(
#InjectModel(CityBoundaries.name)
private cityBoundriesModel: Model<CityBoundriesDocument>,
) {}
/**
* #param {string} cityName take city name as argument from user
* #returns boundries of city
*/
async findOneByCityName(cityName: string) {
const res = await this.cityBoundriesModel.find({ city_name: cityName });
return res;
}
}
app.module.ts
import { Module } from '#nestjs/common';
import { GraphQLModule } from '#nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '#nestjs/apollo';
import { MongooseModule } from '#nestjs/mongoose';
import { CitiesModule } from './module/cities.module';
import 'dotenv/config';
import { CityBoundriesModule } from './module/city-boundries.module';
#Module({
imports: [
MongooseModule.forRoot(process.env.MONGODB_DB_URI, {
dbName: process.env.DB_NAME,
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'src/schema/schema.gql',
introspection: true,
playground: true,
}),
CitiesModule,
CityBoundriesModule,
],
})
export class AppModule {}
Errors
Error during migration generation:
MissingDriverError: Wrong driver: "undefined" given. Supported drivers are: "aurora-data-api", "aurora-data-api-pg", "better-sqlite3", "capacitor", "cockroachdb", "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "nativescript", "oracle", "postgres", "react-native", "sap", "sqlite", "sqljs".
at MissingDriverError.TypeORMError [as constructor] (D:\Projects\Mark1\api2\src\error\TypeORMError.ts:7:9)
at new MissingDriverError (D:\Projects\Mark1\api2\src\error\MissingDriverError.ts:8:9)
at DriverFactory.create (D:\Projects\Mark1\api2\src\driver\DriverFactory.ts:70:23)
at new Connection (D:\Projects\Mark1\api2\src\connection\Connection.ts:122:43)
at ConnectionManager.create (D:\Projects\Mark1\api2\src\connection\ConnectionManager.ts:61:28)
at D:\Projects\Mark1\api2\src\globals.ts:77:35
at step (D:\Projects\Mark1\api2\node_modules\tslib\tslib.js:143:27)
at Object.next (D:\Projects\Mark1\api2\node_modules\tslib\tslib.js:124:57)
at D:\Projects\Mark1\api2\node_modules\tslib\tslib.js:117:75
at new Promise ()
ormConfig.ts
import { TypeOrmModuleOptions } from '#nestjs/typeorm';
import { join } from 'path';
require('dotenv').config();
class ConfigService {
constructor(private env: { [k: string]: string | undefined }) { }
private getValue(key: string, throwOnMissing = true): string {
const value = this.env[key];
if (!value && throwOnMissing) {
throw new Error(`config error - missing env.${key}`);
}
return value;
}
public ensureValues(keys: string[]) {
keys.forEach(k => this.getValue(k, true));
return this;
}
public getPort() {
return this.getValue('PORT', true);
}
public isProduction() {
const mode = this.getValue('MODE', false);
return mode != 'DEV';
}
public getTypeOrmConfig(): TypeOrmModuleOptions {
return {
type: 'postgres',
host: this.getValue('POSTGRES_HOST'),
port: parseInt(this.getValue('POSTGRES_PORT')),
username: this.getValue('POSTGRES_USER'),
password: this.getValue('POSTGRES_PASSWORD'),
database: this.getValue('POSTGRES_DATABASE'),
entities: [join(__dirname, '**', '*.entity.{ts,js}')],
migrationsTableName: 'migration',
migrations: ['src/migration/*.ts'],
cli: {
migrationsDir: 'src/migration',
},
};
}
}
const configService = new ConfigService(process.env)
.ensureValues([
'POSTGRES_HOST',
'POSTGRES_PORT',
'POSTGRES_USER',
'POSTGRES_PASSWORD',
'POSTGRES_DATABASE'
]);
export = configService ;
appmodule.ts
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import configService from 'ormConfig';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [TypeOrmModule.forRoot(configService.getTypeOrmConfig())],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
}
.env file
POSTGRES_HOST=localhost
POSTGRES_PORT=8080
POSTGRES_USER=postgres
POSTGRES_PASSWORD=saad2113
POSTGRES_DATABASE=mark1
PORT=3000
MODE=DEV
RUN_MIGRATIONS=true
I have this ionic 4 project (Using REST API) and have also install ionic native storage. I might have multiple questions concerning this project, but the first is:
I want to store login data so I can be able to pass a token to the header to be used for other endpoints. But if I run the app and try to login, I get the following error:
Error storing item
Object { code: 5, source: "Native", exception: null, stack: "" }
My auth ts is where my login function is:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { tap } from 'rxjs/operators';
import { ApiService } from './api.service';
import { User } from '../models/user';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
#Injectable({
providedIn: 'root'
})
export class AuthService {
isLoggedIn = false;
token: any;
constructor(
private http: HttpClient,
private api: ApiService,
private storage: NativeStorage
) { }
login(account_number: Number, password: String) {
return this.http.post(this.api.API_URL + '/login',
{account_number: account_number, password: password}
).pipe(
tap(token => {
this.storage.setItem('token', token)
.then(
() => {
console.log('Token Stored', token);
},
error => console.error('Error storing item', error)
);
this.token = token;
this.isLoggedIn = true;
return token;
}),
);
}
register(name: String, email: String, phone: Number, reference: String, account_number: String, password: String) {
return this.http.post(this.api.API_URL + '/register',
{ name: name, email: email, phone: phone, reference: reference, account_number: account_number, password: password }
)
}
logout() {
const headers = new HttpHeaders({
'Authorization': "auth-token" + this.token
});
return this.http.get(this.api.API_URL + '/logout', { headers: headers })
.pipe(
tap(data => {
this.storage.remove("token");
this.isLoggedIn = false;
delete this.token;
return data;
})
)
}
getToken() {
return this.storage.getItem('token').then(
data => {
this.token = data;
if (this.token != null) {
this.isLoggedIn = true;
} else {
this.isLoggedIn = false;
}
},
error => {
this.token = null;
this.isLoggedIn = false;
}
);
}
}
And here is is my login.ts.
import { Component, OnInit } from '#angular/core';
import { ModalController, NavController } from '#ionic/angular';
import { AuthService } from 'src/app/services/auth.service';
import { AlertService } from 'src/app/services/alert.service';
import { RegisterPage } from '../register/register.page';
import { NgForm } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
userdata: any;
constructor(
private modalC: ModalController,
private authService: AuthService,
private navCtrl: NavController,
private alertService: AlertService,
// private router: Router
) { }
ngOnInit() {
}
// Dismiss Login Modal
dismissLogin() {
this.modalC.dismiss();
}
// On Register button tap, dismiss login modal and open register modal
async registerModal() {
this.dismissLogin();
const registerModal = await this.modalC.create({
component: RegisterPage
});
return await registerModal.present();
}
login(form: NgForm) {
this.authService.login(form.value.account_number, form.value.password).subscribe(
data => {
this.userdata = data;
this.alertService.presentToast("Logged In");
console.log('this is loggin in userdata', data, "and this is the stored auth-token", this.userdata.message);
},
error => {
console.log(error, "logged in");
},
() => {
this.dismissLogin();
this.navCtrl.navigateRoot('/dashboard');
console.log('this is this.userdata', )
}
);
}
}
Here is aslo my app.module.ts
import { NgModule, ErrorHandler } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy, } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '#angular/common/http';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
#NgModule({
declarations: [
AppComponent,],
entryComponents: [
],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
HttpClientModule
],
providers: [
StatusBar,
SplashScreen,
NativeStorage,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
You can simply use javascript local storage and also use Ionic native storge for your purpose.
Mostly is used localStorage.set(). In case just want to store strings.
u also use Ionic native storage for the object and many things.
https://ionicframework.com/docs/native/native-storage
I am also getting the same error , NO provider for the class
AppHttpInterceptor even i added the provider in the app module and done every small thing to get it worked.
Please go through the code
http.interceptor.ts
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse, HttpErrorResponse } from '#angular/common/http';
import { Injectable } from "#angular/core"
import { Observable } from "rxjs";
import { tap, catchError } from "rxjs/operators";
import { ToastrService } from 'ngx-toastr';
import { of } from 'rxjs/observable/of';
#Injectable()
export class AppHttpInterceptor implements HttpInterceptor {
constructor(public toasterService: ToastrService) { }
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
console.log('start')
return next.handle(req).pipe(
tap(evt => {
console.log(evt);
alert();
if (evt instanceof HttpResponse && evt.body && evt.body.success) {
this.toasterService.success(evt.body.success.message, evt.body.success.title, { positionClass: 'toast-bottom-center' });
}
}),
catchError((err: any) => {
console.log('err')
if (err instanceof HttpErrorResponse) {
try {
this.toasterService.error(err.error.message, err.error.title, { positionClass: 'toast-bottom-center' });
} catch (e) {
this.toasterService.error('An error occurred', '', { positionClass: 'toast-bottom-center' });
}
//log error
}
return of(err);
}));
}
}
role.service.ts
import { AppHttpInterceptor } from './http.interceptor';
import { Injectable } from '#angular/core';
import { Headers, Http, RequestOptions, Response } from "#angular/http";
import { environment } from '../../environments/environment';
#Injectable()
export class RolesService {
private apiUrl = environment.apiUrl
constructor(private http: Http, private ads: AppHttpInterceptor) { }
getRoles() {
let url = this.apiUrl + '/api/v1/role/all';
return this.http.get(url, this.jwt()).map((response: Response) => {
//console.log('service', response)
// this.ads.displayToast(response.json());
return response;
});
}
}
}
I have added the provider for the hTTp Interceptors in the app module as below
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { ThemeComponent } from './theme/theme.component';
import { LayoutModule } from './theme/layouts/layout.module';
import { BrowserAnimationsModule } from "#angular/platform-browser/animations";
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ScriptLoaderService } from "./_services/script-loader.service";
import { ThemeRoutingModule } from "./theme/theme-routing.module";
import { AuthModule } from "./auth/auth.module";
import { SharedService } from './_services/shared.service';
import { ToastrModule } from 'ngx-toastr';
import { AppHttpInterceptor } from './_services/http.interceptor';
#NgModule({
declarations: [
ThemeComponent,
AppComponent
],
imports: [
LayoutModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
ThemeRoutingModule,
AuthModule,
HttpClientModule,
ToastrModule.forRoot({
timeOut: 2000,
positionClass: 'toast-bottom-right',
preventDuplicates: true,
autoDismiss: true,
newestOnTop: true,
progressBar: true,
closeButton: true,
tapToDismiss: true
}),
],
providers: [ScriptLoaderService, SharedService,
[{
provide: HTTP_INTERCEPTORS, useClass: AppHttpInterceptor, multi: true
}]
],
bootstrap: [AppComponent]
})
export class AppModule { }
I am trying to integrate the Angular HTTP Client to my simple angular CRUD App. I want the app to generate a picture of a dog (taken from an API) and to display it in the browser. Each picture has a set of properties (text, id, date, URL). Everything works fine except I cannot fetch the data (for the URL) from an API (it works if I provide the URL for the images as hard coded data, but I want dynamically added data from the API). The API does not require any authentication, it's free and the fetch request works, I think I am not doing something right. The API is this:
https://dog.ceo/dog-api/
and I am fetching from this link
https://dog.ceo/api/breeds/image/random;
The request returns an object with a status and a message (in the message is the link and I want to pass that link as an URL property to the image component). The Images' properties are being initialized through the form component.
I am using angular 7 and am on a windows 8 machine, I also have the following setup in my app:
-a service
-form component
-images component
-I also have an interface for the image which is imported in both form/image components and in the service but I can t seem to make it work.
//Here's my code so far:
//Interface (image.ts)
export interface Image {
id: string;
text: string;
url: string;
date: any;
}
//Service (image-service.ts)
import { Injectable } from "#angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "#angular/common/http";
#Injectable({
providedIn: "root"
})
export class ImageService {
images: Image[];
private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});
selectedImage = this.imageSource.asObservable();
imageUrl: string = "https://dog.ceo/api/breeds/image/random";
constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}
getImages(): Observable<Image[]> {
return of(this.images);
}
setFormImage(image: Image) {
this.imageSource.next(image);
}
addImage(image: Image) {
this.images.unshift(image);
}
//I have to modify this function but I can't seem to manage T__T
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}
//Images component (images-component.ts)
import { Component, OnInit } from "#angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
#Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image[];
//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}
ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}
onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}
//Form component (img-form-component.ts)
import { Component, OnInit } from "#angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "#angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "#angular/common/http";
#Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;
isNew: boolean = true;
constructor(private imageService: ImageService) {}
ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});
}
onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};
//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}
generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
generateUrl() {
let url = this.imageService.getUrl();
return url;
}
}
/*Please let me know If I explained throughly enough the issue, and sorry if I seem clumsy and explaining, I am fairly new to Angular and am trying to learn it. This app is part of the process.
Thanks
*/
Thanks Chau Tran for your answer, however it's not working for my issue, or I am not implementing it right. Here's what I tried:
//*function in the service*
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
// * functions in the form component*
generateUrl() {
return this.imageService.getUrl().toPromise();
}
// *image instantiation*
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: await this.generateUrl()
}
If I try like this I get the following errors:
Module parse failed: The keyword 'yield' is reserved (33:21)
You may need an appropriate loader to handle this file type.
| text: this.text,
| date: new Date(),
url: yield this.generateUrl()
| //this.generateUrl()
| };
'await' expression is only allowed within an async function
What does it mean 'appropriate loader to handle this file type' ? And what 'file type'? I m lost here. Also, can I make the onSubmit() in the constructor asynchronous? How would an implementation of that look (solving my url issue as well).
I also tried to modify the generateUrl() so as to subscribe to the getUrl() observable in the service but I am either not subscribing 'correctly' or something else is 'off':
I also tried like this (as if to subscribe to the observable getUrl() in the service, if I'm doing something wrong, someone please point it out, I can't figure it out myself:
generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}
I put `url` = response[1] because that's how the response is formatted in the dog API. So, what am I doing wrongly, can someone please explain this to me ? Thanks
As Chau Tran suggested, I will include a bit more code maybe this can help someone figure what I can't on my own:
Here's my app.component.ts:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'dogapp';
}
Here's my package.json:
{
"name": "dogapp",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"#angular/animations": "~7.1.0",
"#angular/common": "~7.1.0",
"#angular/compiler": "~7.1.0",
"#angular/core": "~7.1.0",
"#angular/forms": "~7.1.0",
"#angular/platform-browser": "~7.1.0",
"#angular/platform-browser-dynamic": "~7.1.0",
"#angular/router": "~7.1.0",
"bootstrap": "4.0.0-beta.2",
"core-js": "^2.5.4",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"popper.js": "^1.14.6",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"#angular-devkit/build-angular": "~0.11.0",
"#angular/cli": "~7.1.4",
"#angular/compiler-cli": "~7.1.0",
"#angular/language-service": "~7.1.0",
"#types/node": "~8.9.4",
"#types/jasmine": "~2.8.8",
"#types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.1.6"
}
}
Here's my image-service.ts:
import { Injectable } from "#angular/core";
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { Image } from "../models/Image";
import { HttpClient, HttpHeaders } from "#angular/common/http";
#Injectable({
providedIn: "root"
})
export class ImageService {
images: Image[];
private imageSource = new BehaviorSubject<Image>({
id: null,
text: null,
url: null,
date: null
});
selectedImage = this.imageSource.asObservable();
imageUrl: string = "https://dog.ceo/api/breeds/image/random";
constructor(private http: HttpClient) {
this.images = [
{
id: "1",
text: "Generated Pet 1",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("10/25/2017 12:54:32")
},
{
id: "2",
text: "Generated Pet 2",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/26/2017 12:58:33")
},
{
id: "3",
text: "Generated Pet 3",
url: "https://source.unsplash.com/1600x900/?pet",
date: new Date("12/28/2017 12:59:30")
}
];
}
getImages(): Observable<Image[]> {
return of(this.images);
}
setFormImage(image: Image) {
this.imageSource.next(image);
}
addImage(image: Image) {
this.images.unshift(image);
}
getUrl(): Observable<any> {
return this.http.get(this.imageUrl);
}
}
Here's my images.component.ts:
import { Component, OnInit } from "#angular/core";
//import interface/model
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
#Component({
selector: "app-images",
templateUrl: "./images.component.html",
styleUrls: ["./images.component.css"]
})
export class ImagesComponent implements OnInit {
images: Image[];
//inject service as dependency into the constructor;
constructor(private imageService: ImageService) {}
ngOnInit() {
this.imageService.getImages().subscribe(images => {
this.images = images;
});
}
onSelect(image: Image) {
this.imageService.setFormImage(image);
}
}
Here's my image-form.component.ts:
import { Component, OnInit } from "#angular/core";
import { Image } from "../../models/Image";
//import service
import { ImageService } from "../../services/image.service";
import { getUrlScheme } from "#angular/compiler";
import { Observable } from "rxjs";
import { of } from "rxjs";
import { HttpClient, HttpHeaders } from "#angular/common/http";
#Component({
selector: "app-img-form",
templateUrl: "./img-form.component.html",
styleUrls: ["./img-form.component.css"]
})
export class ImgFormComponent implements OnInit {
id: string;
text: string;
url: string;
date: any;
isNew: boolean = true;
//imageUrl: string = "https://dog.ceo/api/breeds/image/random";
constructor(private imageService: ImageService) {}
ngOnInit() {
//subscribe to selectedImage observable
this.imageService.selectedImage.subscribe(image => {
if (image.id !== null) {
this.isNew = false;
this.id = image.id;
this.text = image.text;
this.url = image.url;
this.date = image.date;
}
});
//this.imageService.getUrl.subscribe(url => (this.url = url));
}
onSubmit() {
//check if new image
if (this.isNew) {
//create new image
const newImg = {
id: this.generateId(),
text: this.text,
date: new Date(),
url: this.generateUrl()
};
//add the image
this.imageService.addImage(newImg);
} else {
//create image to be updated
const updImg = {
id: this.id,
text: this.text,
date: new Date(),
url: this.url
};
//update image
// this.imageService.updateImage(updImg);
}
}
generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
generateUrl() {
let url = this.imageService.getUrl().subscribe(response => {
return response;
let url = response[1];
return url;
});
console.log(url);
return url;
}
}
Here's my interface:
export interface Image {
id: string;
text: string;
url: string;
date: any;
}
I cannot manage to make a get request to the dog API (https://dog.ceo/dog-api/) to fetch a URL and pass it as a property to each image, once it is being instantiated (data for each image instantiation is being initialized in the image-form component.ts). Thanks for any help =).
generateUrl() {
let url = this.imageService.getUrl();
return url;
}
Your imageService.getUrl returns an Observable but you're trying to assign it to the url property on newImage which I assume url is a string.
Modify your getUrl to return a url or make sure you handle asynchronous data in generateUrl() before returning url.
There are couple of ways, but in your case, I'll mention one that works right away (but it's kinda dirty):
const newImage = {
...
url: await this.generateUrl()
}
...
generateUrl() {
return this.imageService.getUrl().toPromise();
}