I'm learning MEAN stack. I want to perform CRUD operations and I'm using mongoose. I am following this question on stackoverflow. I want to delete a document by specific value. In my case it is an article with a unique articleid which should get deleted. Unknowingly I'm doing some terrible mistake with params. Please correct me.
Sample document in mongodb.
{
_id: objectId("5d77de7ff5ae9e27bd787bd6"),
articleid:"art5678",
title:"Installing JDK 8 in Ubuntu 18.04 and later",
content:"<h2>Step 1: Add repository</h2><p><strong>$ sudo add-apt-repository pp..."
date:"Tue, 10 Sep 2019 17:33:51 GMT"
contributor:"Tanzeel Mirza",
__v:0
}
article.component.html
<div class="row mt-5">
<div class="col-md-4 mb-3" *ngFor="let article of articles;">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title">{{article.title}}</h5>
<a (click)="onPress(article.articleid)" class="btn btn-danger">Delete</a>
</div>
</div>
</div>
</div>
(click)="onPress(article.articleid") calls a method in ts file.
article.component.ts
import { Component, OnInit } from '#angular/core';
import { ArticleService } from '../article.service';
#Component({
selector: 'app-articles',
templateUrl: './articles.component.html',
styleUrls: ['./articles.component.css']
})
export class ArticlesComponent implements OnInit {
articles = []
constructor(private _articleService: ArticleService) { }
ngOnInit() {
this._articleService.getEvents()
.subscribe(
res => this.articles = res,
err => console.log(err)
)
}
onPress(id) {
this._articleService.deleteArticle()
.subscribe (
data => {
console.log("hello");
}
);
}
}
I have created a service article.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ArticleService {
private _deleteUrl = "http://localhost:3000/api/delete/:id";
constructor(private http: HttpClient) { }
getAllArticles() {
...
}
deleteArticle(id) {
return this.http.delete<any>(this._deleteUrl);
}
}
And here is my api.js
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Article = require('../models/article');
const dbstring = ...
mongoose.connect(dbstring, { useNewUrlParser: true }, err => {
...
})
router.delete('/delete/:id', (req, res) => {
let articleData=req.params.id;
console.log(articleData); //Output: {}
console.log('on delete url '+articleData); //Output: on delete url undefined
Article.deleteOne({articleid: articleData}, (error, article) => {
if(error) {
console.log(error)
}
else {
if(!article) {
res.status(401).send('Something went wrong')
}
else {
//res.json(article);
}
}
})
})
module.exports = router;
Ok dont write the code for me, but please at least tell me some study material.
Ok. I did more and more research and figured out the problem. Here are the changes.
api.js
router.delete('/delete/:id', (req, res) => {
let articleId=req.params.id;
Article.deleteOne({articleid: articleId}, (error, article) => {
if(error) {
console.log(error)
}
else {
if(!article) {
...
}
else {
...
}
}
})
})
and article.service.ts
private _deleteUrl = "http://localhost:3000/api/delete";
deleteArticle method should be.
deleteArticle(id) {
return this.http.delete<any>(this._deleteUrl+'/'+id);
}
Related
I'm trying to implement MongoDB, Next.JS and Passport.js, However upon starting my app and while trying to login, I get this 500 status error:
Cannot access 'getSession' before initialization
The preamble for all this is I am using a hook which calls/api/user to see if the req.user has been set by passport thus creating the 'user' below. And if one exists you get access to the other links/routes in the app.
This is the function declaration regarding the getSession function in the session.js file. So I suspect this is what is causing the circular dependencies issue, naturally.
import MongoStore from "connect-mongo";
import nextSession from 'next-session';
import { getMongoClient } from "./mongodb";
const mongoStore = MongoStore.create({
clientPromise: getMongoClient(),
stringify: false,
});
const getSession = nextSession({
store: mongoStore,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: 2 * 7 * 24 * 60 * 60, // 2 weeks,
path: "/",
sameSite: "strict",
},
touchAfter: 1 * 7 * 24 * 60 * 60, // 1 week
});
export default function session(req, res, next) {
getSession(req, res);
next();
}
And this is the auth file with the passport file, passport initialization and session.
import passport from '../lib/passport'
import session from '../lib/session'
const auths = [session, passport.initialize(), passport.session()];
export default auths;
/component/Layout
export default function Layout({ children, showFooter = false }) {
const [user, { mutate }] = useCurrentUser();
async function handleLogout() {
axios
.get('/api/logout').then(() => {
mutate({ user: null })
Router.push('/',)
}).catch(err => console.log('err', err))
}
return <>
<div className="shadow bg-base-200 drawer h-screen">
<div className="flex-none hidden lg:block">
<ul className="menu horizontal">
{user
?
<>
<li>
<Link href="/profile">
<a>Profile</a>
</Link>
</li>
<li>
<Link href="/dashboard">
<a>Dashboard</a>
</Link>
</li>
<li>
<a role="button" onClick={handleLogout}>
Logout
</a>
</li>
<li className="avatar">
<div className="rounded-full w-10 h-10 m-1">
<img src="https://i.pravatar.cc/500?img=32" />
</div>
</li></>
:
<>
<li>
<Link href="/login">
<a>Login</a>
</Link>
</li>
<li>
<Link href="/registration">
<a>Register</a>
</Link>
</li>
</>
}
</ul>
</div>
</div>
</>;
}
This is the hook itself:
import useSWR from 'swr';
export const fetcher = (url) => {
try {
return fetch(url).then((res) => {
console.log('res', res)
return res.json()
})
} catch (error) {
console.log('error', error)
}
}
export function useCurrentUser() {
const { data, mutate } = useSWR('/api/user', fetcher);
const user = data?.user;
return [user, { mutate }];
}
export function useUser(id) {
const { data } = useSWR(`/api/users/${id}`, fetcher, {
revalidateOnFocus: false,
});
return data?.user;
}
And this is the api: /api/user:
import nextConnect from 'next-connect'
import auth from '/middleware/auth'
const handler = nextConnect()
handler
.use(auth)
.get((req, res) => {
console.log("req.user ", req.user);
if (req.user) {
res.json({ user: req.user })
} else {
res.json({ user: null })
}
})
export default handler
Also this is my repo
And this is a link to the app on vercel.
The record is added without data, but it is null - Angular 5 and Mongodb ^3.0.1
http://localhost:3000/api/users
{
"status":200,
"data":[
{
"_id":"5a63f4da17fc7e9e5548da70",
"name":"Jonson Doeal",
"password":"password"
},
{
"_id":"5a63faf417fc7e9e5548da71",
"name":"Jonson Bol",
"password":"password"
},
{
"_id":"5a64f44de87b3e2f80437c6b",
"name":"aaaa",
"password":"aaaa"
},
{
"_id":"5a67e03bb1d1941d7451c0fa",
"name":null,
"password":"Highway 37"
}
],
"message":null
}
server/routes/api.js:
const express = require('express');
const router = express.Router();
const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;
// Connect 27017
const connection = (closure) => {
return MongoClient.connect('mongodb://localhost:27017/mean', (err, client) => {
if (err) return console.log(err);
let db = client.db('mean');
closure(db);
});
};
// Error handling
const sendError = (err, res) => {
response.status = 501;
response.message = typeof err == 'object' ? err.message : err;
res.status(501).json(response);
};
// Response handling
let response = {
status: 200,
data: [],
message: null
};
// Get users
router.get('/users', (req, res) => {
connection((db) => {
db.collection('users')
.find()
.toArray()
.then((users) => {
response.data = users;
res.json(response);
})
.catch((err) => {
sendError(err, res);
});
});
});
router.post("/users", (req, client) =>{
const myobj = { name: req.body.userName, password: "Highway 37" };
connection((db) => {
db.collection('users').insertOne(myobj, (err, doc) =>{
});
});
});
module.exports = router;
req.body.userName ----> is null :
{"_id":"5a67e03bb1d1941d7451c0fa","name":null,"password":"Highway
37"}],"message":null}
db.collection('users').insertOne( req.body, (err, doc) =>{ } ---> it does not add any values :
{"_id":"5a689b767803052e00e76ded"}],"message":null}
auth/auth.service.ts:
import {Injectable} from '#angular/core';
import {Router} from '#angular/router';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {User} from './user';
import {Http, Headers, RequestOptions, Response} from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class AuthService {
private loggedIn = new BehaviorSubject<boolean>(false);
result: any;
get isLoggedIn() {
return this.loggedIn.asObservable();
}
constructor(private router: Router, private _http: Http) {}
registration(newUser: User): Promise<void | User> {
return this._http.post('/api/users', newUser)
.toPromise()
.then(response => response.json().data as User);
}
getUsers() {
return this._http.get('/api/users').map(result => this.result = result.json().data);
}
}
auth/user.ts:
export interface User {
userName: string;
password: string;
}
registration/registration.ts:
<form class="example-form" (ngSubmit)="onSubmitRegistration(user)">
<mat-input-container class="full-width-input">
User * <input matInput name="user-userName" (ngModel)="user.userName" required>
</mat-input-container>
<mat-input-container class="full-width-input">
Password * <input matInput name="user-password" type="password" (ngModel)="user.password" required>
</mat-input-container>
<mat-card-footer>
<button mat-raised-button color="primary" type="submit">Zapisz</button>
</mat-card-footer>
<br />
</form>
registration/registration.component.ts:
import {AuthService} from './../auth/auth.service';
import {Component, OnInit, Input} from '#angular/core';
import {FormGroup, FormBuilder, Validators} from '#angular/forms';
import {User} from '../auth/user';
#Component({
selector: 'app-registration',
templateUrl: './registration.component.html',
styleUrls: ['../login/login.component.css']
})
export class RegistrationComponent implements OnInit {
private formSubmitAttempt: boolean;
errorMessage: string;
#Input()
user: User;
#Input()
createHandler: Function;
constructor(private fb: FormBuilder, private authService: AuthService) {}
ngOnInit() {}
onSubmitRegistration(user: User) {
this.authService.registration(user).then((newUser: User) => {
this.createHandler(newUser);
console.log('onSubmitRegistration ' + newUser);
});
}
}
Does this method 'onSubmitRegistration' send correctly values? The console does not display a message :
console.log('onSubmitRegistration ' + newUser);
How to correctly add data to the database?
solution:
the data was badly sent to the server
registration/registration.component.ts:
<form class="example-form" [formGroup]="registrationForm" (ngSubmit)="onSubmitRegistration()">
User * <input matInput formControlName="userName" required>
Password * <input matInput type="password" formControlName="password" required>
<button mat-raised-button color="primary" type="submit">Zapisz</button>
</form>
registration/registration.component.ts:
registrationForm: FormGroup;
// ...
onSubmitRegistration(user: User) {
this.authService.registration(this.registrationForm.value);
}
auth/auth.service.ts:
registration(newUser: User): Promise<User> {
return this._http.post('/api/users', newUser)
.toPromise()
.then(response => response.json().data as User);
}
I am trying to implement a custom validator that checks if the keyed username exists. However, I am running into a problem where the control is always invalid. I have confirmed the webApi is returning the correct values and the validator function is stepping into the proper return statements.
My custom validator is as follows:
import { Directive, forwardRef } from '#angular/core';
import { AbstractControl, ValidatorFn, NG_VALIDATORS, Validator, FormControl } from '#angular/forms';
import { UsersService } from '../_services/index';
import { Users } from '../admin/models/users';
function validateUserNameAvailableFactory(userService: UsersService): ValidatorFn {
return (async (c: AbstractControl) => {
var user: Users;
userService.getUserByName(c.value)
.do(u => user = u)
.subscribe(
data => {
console.log("User " + user)
if (user) {
console.log("Username was found");
return {
usernameAvailable: {
valid: false
}
}
}
else {
console.log("Username was not found");
return null;
}
},
error => {
console.log("Username was not found");
return null;
})
})
}
#Directive({
selector: '[usernameAvailable][ngModel]',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => UserNameAvailableValidator), multi: true }
]
})
export class UserNameAvailableValidator implements Validator {
validator: ValidatorFn;
constructor(private usersService: UsersService) {
}
ngOnInit() {
this.validator = validateUserNameAvailableFactory(this.usersService);
}
validate(c: FormControl) {
console.log(this.validator(c));
return this.validator(c);
}
}
And the form looks like:
<form #userForm="ngForm" (ngSubmit)="onSubmit()" novalidate>
<div class="form form-group col-md-12">
<label for="UserName">User Name</label>
<input type="text" class="form-control" id="UserName"
required usernameAvailable maxlength="50"
name="UserName" [(ngModel)]="user.userName"
#UserName="ngModel" />
<div [hidden]="UserName.valid || UserName.pristine" class="alert alert-danger">
<p *ngIf="UserName.errors.required">Username is required</p>
<p *ngIf="UserName.errors.usernameAvailable">Username is not available</p>
<p *ngIf="UserName.errors.maxlength">Username is too long</p>
</div>
<!--<div [hidden]="UserName.valid || UserName.pristine" class="alert alert-danger">Username is Required</div>-->
</div>
</form>
I have followed several tutorials proving this structure works, so I am assuming that I am possibly messing up the return from within the validator function.
Also, is there a way I can present the list of errors (I have tried using li with ngFor, but I get nothing from that)?
So, there was something wrong with using .subscribe the way I was. I found a solution by using the following 2 links:
https://netbasal.com/angular-2-forms-create-async-validator-directive-dd3fd026cb45
http://cuppalabs.github.io/tutorials/how-to-implement-angular2-form-validations/
Now, my validator looks like this:
import { Directive, forwardRef } from '#angular/core';
import { AbstractControl, AsyncValidatorFn, ValidatorFn, NG_VALIDATORS, NG_ASYNC_VALIDATORS, Validator, FormControl } from '#angular/forms';
import { Observable } from "rxjs/Rx";
import { UsersService } from '../_services/index';
import { Users } from '../admin/models/users';
#Directive({
selector: "[usernameAvailable][ngModel], [usernameAvailable][formControlName]",
providers: [
{
provide: NG_ASYNC_VALIDATORS,
useExisting: forwardRef(() => UserNameAvailableValidator), multi: true
}
]
})
export class UserNameAvailableValidator implements Validator {
constructor(private usersService: UsersService) {
}
validate(c: AbstractControl): Promise<{ [key: string]: any }> | Observable<{ [key: string]: any }> {
return this.validateUserNameAvailableFactory(c.value);
}
validateUserNameAvailableFactory(username: string) {
return new Promise(resolve => {
this.usersService.getUserByName(username)
.subscribe(
data => {
resolve({
usernameAvailable: true
})
},
error => {
resolve(null);
})
})
}
}
This is now working. However, I now have an issue where the the control temporarily invalidates itself while the async validator is running.
Return for true should be:
{usernameAvailable: true}
or null for false.
I learn Angular2 and now i try to get my data from a rest service. My rest service works and i can get data from there.
But angular always give my this error message: JSON.parse: unexpected character at line 1 column 1 of the JSON data
This is my Service:
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Person } from '../_models/person';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class PersonService {
private headers: Headers;
constructor(private http: Http)
{
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
}
getPersons(): Observable<Person[]> {
return this.http.get('http://localhost:54612/api/Person')
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleError(error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
This is the component for the view:
import { Component, OnInit } from '#angular/core';
import { Person } from '../_models/person';
import { PersonService } from '../_services/person.service';
#Component({
moduleId: module.id,
templateUrl: 'login.component.html',
providers: [ PersonService ]
})
export class LoginComponent implements OnInit {
errorMessage: string;
personen: Person[] = [];
constructor(private personService: PersonService) { }
ngOnInit() { this.getPersons(); }
getPersons() {
this.personService.getPersons()
.subscribe(
personen => this.personen = personen,
error => this.errorMessage = <any>error);
}
}
And this is my view:
<h1>Tour of Heroes</h1>
<h3>Heroes:</h3>
<ul>
<li *ngFor="let person of personen">{{person.IDPerson}}</li>
</ul>
<p class="error" *ngIf="errorMessage">{{errorMessage}}</p>
Your server is return XML. The reason is probably because it's the default when you don't explicitly set the Accept to something else. You have set it in the Headers to JSON, but you never add the headers to the request. You need to do
this.http.get('http://localhost:54612/api/Person', { headers: this.headers });
Try this snippet, in your getPersons()
return this.http.get('http://localhost:54612/api/Person',{headers: this.headers })
.map((response:Response) => response.json())
I have an email input and I want to create a validator to check, through an API, if the entered email it's already in the database.
So, I have:
A validator directive
import { Directive, forwardRef } from '#angular/core';
import { Http } from '#angular/http';
import { NG_ASYNC_VALIDATORS, FormControl } from '#angular/forms';
export function validateExistentEmailFactory(http: Http) {
return (c: FormControl) => {
return new Promise((resolve, reject) => {
let observable: any = http.get('/api/check?email=' + c.value).map((response) => {
return response.json().account_exists;
});
observable.subscribe(exist => {
if (exist) {
resolve({ existentEmail: true });
} else {
resolve(null);
}
});
});
};
}
#Directive({
selector: '[validateExistentEmail][ngModel],[validateExistentEmail][formControl]',
providers: [
Http,
{ provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => ExistentEmailValidator), multi: true },
],
})
export class ExistentEmailValidator {
private validator: Function;
constructor(
private http: Http
) {
this.validator = validateExistentEmailFactory(http);
}
public validate(c: FormControl) {
return this.validator(c);
}
}
A component
import { Component } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { ExistentEmailValidator } from '../../directives/existent-email-validator';
#Component({
selector: 'user-account',
template: require<string>('./user-account.component.html'),
})
export class UserAccountComponent {
private registrationForm: FormGroup;
private registrationFormBuilder: FormBuilder;
private existentEmailValidator: ExistentEmailValidator;
constructor(
registrationFormBuilder: FormBuilder,
existentEmailValidator: ExistentEmailValidator
) {
this.registrationFormBuilder = registrationFormBuilder;
this.existentEmailValidator = existentEmailValidator;
this.initRegistrationForm();
}
private initRegistrationForm() {
this.registrationForm = this.registrationFormBuilder.group({
email: ['', [this.existentEmailValidator]],
});
}
}
And a template
<form novalidate [formGroup]="registrationForm">
<input type="text" [formControl]="registrationForm.controls.email" name="registration_email" />
</form>
A've made other validator this way (without the async part) and works well. I think te problem it's related with the promise. I'm pretty sure the code inside observable.subscribe it's running fine.
What am I missing?
I'm using angular v2.1
Pretty sure your problem is this line:
...
email: ['', [this.existentEmailValidator]],
...
You're passing your async validator to the synchronous validators array, I think the way it should be is this:
...
email: ['', [], [this.existentEmailValidator]],
...
It would probably be more obvious if you'd use the new FormGroup(...) syntax instead of FormBuilder.