I have created login function accordingly but the login function is not working. Whenever I enter the sample input (phoneNumber and password) it did not directing me to the HomePage. Details about API has been provided in the pictures below. Is there anything I am missing out? Please HELP me to solve this out.
API details:
HTML CODE:
<ion-header>
<ion-navbar>
<ion-title>
Login
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<form name="form"(ngSubmit)="login()" novalidate>
<ion-item>
<ion-label fixed>Phone Number</ion-label>
<ion-input type="number" name="phoneNumber" value="" [(ngModel)]="model.phoneNumber" required></ion-input>
</ion-item>
<ion-item>
<ion-label fixed>Password</ion-label>
<ion-input type="password" name="password" value="" [(ngModel)]="model.password" required></ion-input>
</ion-item>
<ion-item>
<button ion-button>Enter</button>
</ion-item>
</form>
</ion-list>
</ion-content>
.TS CODE:
import { Component } from '#angular/core';
import { NavController, AlertController, LoadingController, ToastController } from 'ionic-angular';
import { HomePage } from '../home/home';
import { apiService } from '../../providers/api-service.service';
import { AuthService } from '../../providers/auth-service.service';
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class LoginPage {
model : any = {};
response: any[] = [];
constructor(public navCtrl: NavController, public apiService: apiService, public toastCtrl: ToastController, public alertCtrl: AlertController, public loadingCtrl: LoadingController, public AuthService: AuthService){}
loginUser(){
this.AuthService.login();
}
logoutUser(){
this.AuthService.logout();
}
nextPage(data){
this.navCtrl.push(HomePage,data).catch(err => {
let alert = this.alertCtrl.create({
title: 'Something',
subTitle: 'something something',
buttons: ['OK']
});
alert.present();
});
}
errorToast(){
let toast = this.toastCtrl.create({
message: 'Cannot login',
duration: 3000
});
toast.present();
}
logoutout(){
let loader = this.loadingCtrl.create({
content: "Loging out.....",
duration: 1000
})
loader.present();
this.loginUser();
}
login(){
this.apiService.apiCall(this.model.phoneNumber, this.model.password)
.then(data => {
this.logoutout();
this.nextPage(data);
})
.catch(error => {
this.errorToast();
})
}
}
PROVIDERS CODE:
api-service.service.ts
import {Injectable} from '#angular/core';
import {Http, Headers, RequestOptions} from '#angular/http';
import 'rxjs/add/operator/map'
#Injectable()
export class apiService{
constructor(private http: Http){}
apiCall(phoneNumber, password){
let headers = new Headers({
"X-Auth-PhoneNumber": '',
"X-Auth-Password": '',
"SW-Version": '',
"Device-Id": '',
"Device-Model": ''
})
let options = new RequestOptions({
headers: headers
});
return this.http.get('https://api.keyway.com.my/client/mobile/verification'+ phoneNumber + password, options)
.map(res => res.json())
.toPromise();
}
}
auth-service.service.ts
import {Injectable} from '#angular/core';
#Injectable()
export class AuthService{
private isLoggedIn = false;
constructor(){}
login(): void{
this.isLoggedIn = true;
}
logout(): void{
this.isLoggedIn = false;
}
authenticated():boolean{
return this.isLoggedIn;
}
}
Because you are sending username and password with request URL but as per request defination you need to send in headers so,please made some changes like below -
apiCall(phoneNumber, password){
let headers = new Headers({
"X-Auth-PhoneNumber": phoneNumber',
"X-Auth-Password": password,
"SW-Version": '',
"Device-Id": '',
"Device-Model": ''
})
let options = new RequestOptions({
headers: headers
});
return this.http.get('https://api.keyway.com.my/client/mobile/verification', options)
.map(res => res.json())
.toPromise();
}
Also try to console output after request.
login(){
this.apiService.apiCall(this.model.phoneNumber, this.model.password)
.then(data => {
console.log(data); // Console Output
this.logoutout();
this.nextPage(data);
})
.catch(error => {
console.log(error); // Console Error output
this.errorToast();
})
}
Related
I'm trying to add user authentication to every page in my NextJS project (pages, not app.) This tutorial was very helpful (and is exactly what I want to do) - https://alexsidorenko.com/blog/next-js-protected-routes/ - but I'm having trouble integrating Supabase's default auth UI and capabilities into that model (https://supabase.com/docs/guides/auth/auth-helpers/nextjs).
My basic goal is to move authentication branching into _app.tsx, rather than on each page:
// _app.tsx
import { useEffect, useState } from "react";
import { createBrowserSupabaseClient } from '#supabase/auth-helpers-nextjs'
import { SessionContextProvider, useUser, useSession, useSupabaseClient, Session } from '#supabase/auth-helpers-react'
import { Auth, ThemeSupa } from '#supabase/auth-ui-react'
import { AppProps } from 'next/app'
import { UserContext } from "#components/user"
function MyApp({Component, pageProps}: AppProps<{ initialSession: Session }>) {
const [supabase] = useState(() => createBrowserSupabaseClient())
const session = useSession()
const user = useUser()
console.log("session:" + session);
console.log("user:" + user);
useEffect(() => {
if (
pageProps.protected
) {
return <Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} theme="dark" />
}
}, [])
return (
<SessionContextProvider supabaseClient={supabase} session={session} initialSession={pageProps.initialSession}>
<Component {...pageProps} />
</SessionContextProvider>
)
}
export default MyApp
A page I want to protect (for example, the index page) looks like this:
// index.tsx
import Account from "#components/account";
const Home = () => {
return (
<div>
<Account session={session} />
</div>
)
}
export async function getStaticProps(context) {
return {
props: {
protected: true,
},
}
}
export default Home
And then the Account component that's included on the index page is the Supabase out of the box profile panel, although it could be any content:
// #components/account.tsx
import { useState, useEffect } from 'react'
import { useUser, useSupabaseClient, Session } from '#supabase/auth-helpers-react'
import { Database } from '#utils/database.types'
type Profiles = Database['public']['Tables']['profiles']['Row']
export default function Account({ session }: { session: Session }) {
const supabase = useSupabaseClient<Database>()
const user = useUser()
const [loading, setLoading] = useState(true)
const [username, setUsername] = useState<Profiles['username']>(null)
useEffect(() => {
getProfile()
}, [session])
async function getProfile() {
try {
setLoading(true)
if (!user) throw new Error('No user')
let { data, error, status } = await supabase
.from('profiles')
.select(`username`)
.eq('id', user.id)
.single()
if (error && status !== 406) {
throw error
}
if (data) {
setUsername(data.username)
}
} catch (error) {
alert('Error loading user data!')
console.log(error)
} finally {
setLoading(false)
}
}
async function updateProfile({
username,
}: {
username: Profiles['username']
}) {
try {
setLoading(true)
if (!user) throw new Error('No user')
const updates = {
id: user.id,
username,
updated_at: new Date().toISOString(),
}
let { error } = await supabase.from('profiles').upsert(updates)
if (error) throw error
alert('Profile updated!')
} catch (error) {
alert('Error updating the data!')
console.log(error)
} finally {
setLoading(false)
}
}
return (
<div>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="text" value={session.user.email} disabled />
</div>
<div>
<label htmlFor="username">Username</label>
<input id="username" type="text" value={username || ''} onChange={(e) => setUsername(e.target.value)} />
</div>
<div>
<button onClick={() => updateProfile({ username })} disabled={loading} >
{loading ? 'Loading ...' : 'Update'}
</button>
</div>
<div>
<button onClick={() => supabase.auth.signOut()}>
Sign Out
</button>
</div>
</div>
)
}
I think I have a fundamental misunderstanding of the relationship between protected routes and Supabase's use of session and user.
Any help would be very much appreciated.
I'd recommend using Next.js middleware for this: https://supabase.com/docs/guides/auth/auth-helpers/nextjs#auth-with-nextjs-middleware
import { createMiddlewareSupabaseClient } from '#supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
// We need to create a response and hand it to the supabase client to be able to modify the response headers.
const res = NextResponse.next()
// Create authenticated Supabase Client.
const supabase = createMiddlewareSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
// Check auth condition
if (session?.user.email?.endsWith('#gmail.com')) {
// Authentication successful, forward request to protected route.
return res
}
// Auth condition not met, redirect to home page.
const redirectUrl = req.nextUrl.clone()
redirectUrl.pathname = '/'
redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
return NextResponse.redirect(redirectUrl)
}
export const config = {
matcher: '/middleware-protected/:path*',
}
I'm a beginner in Vue, and I implemented Auth0 to my Web App using Vue3.
My issue: after logging in, my API call to retrieve data get an unauthorized error 403. If I reload the page, everything is working fine.
What should I do to avoid reloading the page to get authenticated directly?
Here are my scripts:
Main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './index.css'
import dayjs from 'dayjs'
import Datepicker from 'vue3-date-time-picker'
import 'vue3-date-time-picker/dist/main.css'
import { setupAuth } from './auth/index.js'
import authConfig from './auth/config.js'
function callbackRedirect(appState) {
router.push(appState && appState.targetUrl ? appState.targetUrl : '/' );
}
setupAuth(authConfig, callbackRedirect).then((auth) => {
let app = createApp(App).use(router);
app.config.globalProperties.$dayjs = dayjs;
app.component('Datepicker', Datepicker);
app.use(auth).mount('#app');
})
My App.vue script:
<template>
<div v-if="isAuthenticated">
<NavBar />
<router-view/>
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
export default {
components: { NavBar },
data(){
return {
isAuthenticated: false,
}
},
async mounted(){
await this.getAccessToken()
},
methods: {
async getAccessToken(){
try {
const accessToken = await this.$auth.getTokenSilently()
localStorage.setItem('accessToken', accessToken)
this.isAuthenticated = true
} catch (error) {
console.log('Error occured while trying to retrieve Access Token...', error)
}
},
},
}
</script>
and my Home.vue loading the data:
<template>
<div class="home">
<div class="py-10">
<header>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold leading-tight text-gray-900">Monitoring Dashboard</h1>
</div>
</header>
<main>
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<h3 class="m-5 text-lg leading-6 font-medium text-gray-900">Main KPIs</h3>
<div class="md:grid md:grid-cols-3 md:gap-6">
<div v-for="(item, index) in stats" :key="index" class="md:col-span-1">
<div class="bg-white p-5 border-gray-50 rounded-lg shadow-lg mb-5">
<span class="text-sm font-medium text-gray-500 truncate">{{ item.name }}</span>
<p class="mt-1 text-3xl font-bold text-gray-900">{{ parseFloat(item.stat.toFixed(2)) }}</p>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</template>
<script>
import _ from 'lodash'
import ProductsService from '../services/products.service'
export default {
name: 'Home',
data(){
return{
user: '',
products: '',
stats: '',
}
},
async mounted(){
await this.readProducts()
await this.buildStats()
},
methods: {
async readProducts(){
let temp = null
try {
temp = await ProductsService.readProducts()
this.products = temp.data
} catch (error) {
console.log('Error: cannot retrieve all products...')
}
},
async buildStats(){
//Nb products
const nbProducts = this.products.length
//Nb offers & Uniq NbRetailers
let nbOffers = 0
let retailers = []
for(let product of this.products){
for(let offer of product.offers){
retailers.push(offer.retailer)
nbOffers += 1
}
}
const nbRetailers = _.uniq(retailers).length
this.stats = [
{ name: 'Number of Retailers', stat: nbRetailers },
{ name: 'Number of Products', stat: nbProducts },
{ name: 'Number of Offers', stat: nbOffers },
]
},
},
watch: {
products: function(){
this.buildStats()
}
}
}
</script>
My ./auth/index.js file:
import createAuth0Client from '#auth0/auth0-spa-js'
import { computed, reactive, watchEffect } from 'vue'
let client
const state = reactive({
loading: true,
isAuthenticated: false,
user: {},
popupOpen: false,
error: null,
})
async function loginWithPopup() {
state.popupOpen = true
try {
await client.loginWithPopup(0)
} catch (e) {
console.error(e)
} finally {
state.popupOpen = false
}
state.user = await client.getUser()
state.isAuthenticated = true
}
async function handleRedirectCallback() {
state.loading = true
try {
await client.handleRedirectCallback()
state.user = await client.getUser()
state.isAuthenticated = true
} catch (e) {
state.error = e
} finally {
state.loading = false
}
}
function loginWithRedirect(o) {
return client.loginWithRedirect(o)
}
function getIdTokenClaims(o) {
return client.getIdTokenClaims(o)
}
function getTokenSilently(o) {
return client.getTokenSilently(o)
}
function getTokenWithPopup(o) {
return client.getTokenWithPopup(o)
}
function logout(o) {
return client.logout(o)
}
export const authPlugin = {
isAuthenticated: computed(() => state.isAuthenticated),
loading: computed(() => state.loading),
user: computed(() => state.user),
getIdTokenClaims,
getTokenSilently,
getTokenWithPopup,
handleRedirectCallback,
loginWithRedirect,
loginWithPopup,
logout,
}
export const routeGuard = (to, from, next) => {
const { isAuthenticated, loading, loginWithRedirect } = authPlugin
const verify = () => {
// If the user is authenticated, continue with the route
if (isAuthenticated.value) {
return next()
}
// Otherwise, log in
loginWithRedirect({ appState: { targetUrl: to.fullPath } })
}
// If loading has already finished, check our auth state using `fn()`
if (!loading.value) {
return verify()
}
// Watch for the loading property to change before we check isAuthenticated
watchEffect(() => {
if (loading.value === false) {
return verify()
}
})
}
export const setupAuth = async (options, callbackRedirect) => {
client = await createAuth0Client({
...options,
})
try {
// If the user is returning to the app after authentication
if (
window.location.search.includes('code=') &&
window.location.search.includes('state=')
) {
// handle the redirect and retrieve tokens
const { appState } = await client.handleRedirectCallback()
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
callbackRedirect(appState)
}
} catch (e) {
state.error = e
} finally {
// Initialize our internal authentication state
state.isAuthenticated = await client.isAuthenticated()
state.user = await client.getUser()
state.loading = false
}
return {
install: (app) => {
app.config.globalProperties.$auth = authPlugin
},
}
}
I would like to create an upload progress bar with Axios.
Everything is working fine with server sending and response.
The problem is that I don't know how to capture the progress percentage (which is correctly calculated) from my exported object.
My file upload.js:
import axios from 'axios'
const baseUrl = 'http://localhost:80/upload.php'
const config = {
Headers: {'Content-Type': 'multipart/form-data'},
onUploadProgress: progressEvent => {
return parseInt(Math.round((progressEvent.loaded / progressEvent.total) * 100))
}
}
export default {
send (data) {
return axios.post(baseUrl, data, config)
}
}
My Vue component:
<template>
<div>
<label>File:</label>
<input type="file" id="file" ref="file" #change="changeFile()" />
<button #click="submit()">Upload</button>
<br />
<progress max="100" :value.prop="uploadPercentage"></progress>
</div>
</template>
<script>
import upload from '../services/upload.js'
export default {
name: 'Upload',
data: () => ({
file: null,
uploadPercentage: 0
}),
methods: {
submit () {
const formData = new FormData()
formData.append('file', this.file)
upload.send(formData)
.then(res => {
console.log(res.data)
})
.catch(() => {
console.log('Failure')
})
},
changeFile () {
this.file = this.$refs.file.files[0]
}
}
}
</script>
How to retreive, from the component submit method, the info sent by the onUploadProgress in order to update the data uploadPercentage?
Thanks.
Regards.
You need to pass a function to your send operation that will be called later.
See example below
const config = {
Headers: {'Content-Type': 'multipart/form-data'},
onUploadProgress: progressEvent => {
var progress= parseInt(Math.round((progressEvent.loaded / progressEvent.total) * 100));
if (config.onProgress)
config.onProgress(progress);
}
}
export default {
send (data, onProgress) {
config.onProgress= onProgress;
return axios.post(baseUrl, data, config)
}
}
Then you upload code will be
upload.send(formData,(pogress)=>{
// Update your uploadPercentage here
})
.then(res => {
console.log(res.data)
})
.catch(() => {
console.log('Failure')
})
Hymn-home code:
<ion-header>
<ion-toolbar>
<ion-title>
<ion-buttons>
<img src="./assets/icon/logo.png" id="logo2">
Enyimba Z'Omutukirivu
</ion-buttons>
</ion-title>
<ion-buttons slot="end">
<ion-button href="/searh-hymn">
<ion-icon name="search" slot="icon-only">
</ion-icon></ion-button>
<ion-button><ion-icon name="ellipsis-vertical-outline" slot="icon-only"></ion-icon></ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-virtual-scroll [items]= "hymn">
<ion-item
*virtualItem="let song"
button
detail
[routerLink]="['/', 'hymn-detail', song.id]"
>
<ion-label>{{ song.title }}</ion-label>
</ion-item>
</ion-virtual-scroll>
</ion-content>
hymn-home.ts code
import { Component, OnInit } from '#angular/core';
import { HymnServiceService } from '../hymn-service.service';
import { NavController } from '#ionic/angular';
import { Router } from '#angular/router';
import { Hymn } from '../hymn.model';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-hymn-home',
templateUrl: './hymn-home.page.html',
styleUrls: ['./hymn-home.page.scss']
})
export class HymnHomePage implements OnInit {
hymn: Hymn[];
public hymnsub: Subscription;
constructor(public HymnService: HymnServiceService,
private router: Router,
public navCtrl: NavController) { }
ngOnInit() {
}
ionViewWillEnter() {
this.HymnService.fetchHymns().subscribe(data => {
console.dir(data)
this.hymn = data;
});
}
}
hymn-detail html code
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="/hymn-home" icon="chevron-back-outline"></ion-back-button>
</ion-buttons>
<ion-buttons>
<img src="./assets/icon/logo.png" id="logo2">
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-grid>
<ion-row>
<ion-col size="12" size-sm="8" offset-sm="2">
<ion-item>{{ hymn?.title }}</ion-item>
<ion-item>
{{ hymn?.details }}
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
Hymn detail.ts
import { Component, OnInit } from '#angular/core';
import { HymnServiceService } from '../hymn-service.service';
import { Router, ActivatedRoute } from '#angular/router';
import { Hymn } from '../hymn.model';
import { Subscription } from 'rxjs';
import { NavController } from '#ionic/angular';
#Component({
selector: 'app-hymn-detail',
templateUrl: './hymn-detail.page.html',
styleUrls: ['./hymn-detail.page.scss'],
})
export class HymnDetailPage implements OnInit {
hymn : Hymn;
hymnId: string;
public hymnsub: Subscription;
constructor(public hymnService: HymnServiceService,
private actroute: ActivatedRoute, private router: Router,
private navCtrl: NavController) { }
ngOnInit() {
this.actroute.paramMap.subscribe(paramMap => {
this.hymnId = paramMap.get('hymnId');
this.hymnsub = this.hymnService
.readHymn(paramMap.get('hymnId')).subscribe(hymn => {
console.log(hymn);
this.hymn = hymn;
})
});
}
}
hymnservice.ts
import { Injectable } from '#angular/core';
import { map, tap, take } from 'rxjs/operators';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Hymn } from './hymn.model';
interface hymnData{
id: string;
numb: number;
title: string;
details: string;
}
#Injectable({
providedIn: 'root'
})
export class HymnServiceService {
private _hymns = new BehaviorSubject<Hymn[]>([]);
constructor(public http: HttpClient) { }
fetchHymns() {
let url = "http://localhost/MyApp/php/read.php";
let request = this.http.get<{[key: string] : hymnData}>(url);
return request.pipe(map(resData => {
const hymns = [];
for (const key in resData) {
if (resData.hasOwnProperty(key)) {
hymns.push (
new Hymn(resData[key].id, resData[key].numb,
resData[key].title, resData[key].details)
)
}
}
return hymns;
}),
tap(hymns => {
this._hymns.next(hymns);
}));
}
readHymn(id: string) {
let url = "http://localhost/MyApp/php/read.php";
let request = this.http.get<hymnData>(url);
return request.pipe(map(hymnData => {
return new Hymn(id, hymnData.numb, hymnData.title, hymnData.details);
})
);
}
}
hymn model.ts
export class Hymn {
constructor(
public id: string,
public numb: number,
public title: string,
public details: string
){}
}
and my php file.
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-with');
header('Content-Type: application/json; charset=utf-8');
define('DB_NAME', 'hymnsdb');
define('DB_USER', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', 'localhost');
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if($conn->connect_error) {
die("Connection failed " . $conn->connect_error);
}
$query = "SELECT * FROM `songs` ";
$result = mysqli_query($conn, $query);
$data = array();
while ($row=mysqli_fetch_object($result)) {
//$output = array();
//$output = $result->fetch_all(MYSQLI_ASSOC);
//echo json_encode($output);
$data[] = $row;
}
echo json_encode($data);
echo mysqli_error($conn);
//else {
//echo json_encode("No Hymns");
//}
$conn->close();`enter code here`
?>
I really need help. In details page console it brings title, numb,id, details = null.
I'm trying to do a simple form post to my controller but I keep getting the following error : cannot access username of undefined. As far as i can tell i'm initializing the usermodel in my login component but have no idea why it's still erroring.
Does anyone have any ideas?
html
<form #loginForm="ngForm" (ngSubmit) ="OnSubmit(loginForm)">
<div class="form-row">
<div class="form-group col-md-6">
<input type="text" name="username" #username="ngModel" [(ngModel)]="user.username" class="form-control" />
</div>
<div class="form-group col-md-6">
<input type="text" name="password" #password="ngModel" [(ngModel)]="user.password" class="form-control" />
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-block btn-lg">login</button>
</div>
user.model.ts
export class UserModel {
username: string;
password: string;
}
user.service.ts
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { UserModel } from './user.model';
import { Inject, Injectable } from '#angular/core';
#Injectable()
export class UserService {
constructor(private http: HttpClient) { }
postUser(user: UserModel, #Inject('BASE_URL') baseUrl: string) {
return this.http.post(baseUrl + '/Test/Register', new HttpHeaders({ 'username': user.username, 'password': user.password })).subscribe(result => {
console.error(result)
}, error => console.error(error));
}
login.component.ts
import { Component, Inject, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { UserModel } from './user.model';
import { UserService } from './user.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
user: UserModel
constructor(private userService: UserService) { }
ngOnInit() {
this.resetForm();
}
resetForm(form?: NgForm) {
if (form != null) {
form.reset();
this.user = {
username: '',
password: ''
}
}
}
onSubmit(form: NgForm) {
this.userService.postUser(form.value, "https://localhost:44327/");
};
This is caused by that you did not initialize the this.user when loading this page.
Try this:
resetForm(form?: NgForm) {
if (this.user == undefined) {
this.user = new UserModel();
}
if (form != null) {
form.reset();
this.user = {
username: '',
password: ''
}
}
}