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: ''
}
}
}
Related
So in my i18n-validators.js file I want to export validators with translated messages to my language of choice and use them in my vue component to validate a form.
My code:
// import * as VuelidateValidators from 'https://cdn.jsdelivr.net/npm/#vuelidate/validators';
// import * as VueI18n from 'https://unpkg.com/vue-i18n#9';
const messages = {
en: {
validations: {
required: 'The field {property} is required.',
}
},
cs: {
validations: {
required: 'Toto pole {property} je povinné',
}
},
}
const i18n = VueI18n.createI18n({
locale: 'cz',
fallbackLocale: 'en',
messages
})
const withI18nMessage = VuelidateValidators.createI18nMessage({
t: VueI18n.createI18n().global.t.bind(i18n)
})
export const required = withI18nMessage(VuelidateValidators.required)
Console:
Not found 'validations.required' key in 'en-US' locale messages. vue-i18n#9
Fall back to translate 'validations.required' key with 'en' locale. vue-i18n#9
Not found 'validations.required' key in 'en' locale messages.
And I want the validator to throw me the specified message instead of the "validations.required" message
First make sure you have installed vuelidade and vue-i18n
Following your example, you can change the file above to:
import * as validators from "#vuelidate/validators";
import { createI18n } from "vue-i18n";
const { createI18nMessage } = validators;
const messages = {
en: {
validations: {
required: "The field {property} is required.",
},
},
cs: {
validations: {
required: "Toto pole {property} je povinné",
},
},
};
const i18n = createI18n({
locale: "cs",
fallbackLocale: "en",
messages,
});
const withI18nMessage = createI18nMessage({ t: i18n.global.t.bind(i18n) });
export const required = withI18nMessage(validators.required);
as a component you can follow this one as example:
<template>
...
<div class="mb-3">
<input
v-model="formData.name"
className="form-control"
placeholder="Insert your name.."
/>
</div>
<span v-for="error in v$.name.$errors" :key="String(error.$uid)">
<span class="text-danger">{{ error.$message }}</span>
</span>
<div class="mt-5 submit">
<button class="btn btn-primary btn-sm" type="button" #click="submitForm">
Next
</button>
</div>
...
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";
import useVuelidate from "#vuelidate/core";
import { required } from "#/utils/validators/i18n-validators";
export default defineComponent({
name: "InitialDataForm",
setup() {
const formData = reactive({
name: "",
});
const rules = {
name: { required },
};
const v$ = useVuelidate(rules, formData);
return {
formData,
v$,
};
},
methods: {
async submitForm() {
const result = await this.v$.$validate();
if (result) {
alert("validation passed");
}
},
},
});
</script>
and now you should be able to see the translated message:
I read the other stackoverflow questions but does not help. I use pinia=2.0.27. I could extend the example if neccessary.
I have a component with a button with a spinner.
<template>
....
<button
type="submit"
class="btn btn-primary"
:disabled="loading ? true : false"
#click="submitForm"
>
<span
v-show="loading"
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
<span v-show="loading"> Wait </span>
<span v-show="!loading">
Create certificate
</span>
</button>
....
</template>
<script setup>
import { storeToRefs } from "pinia";
import { useUserStore } from "#/store/UserStore";
const { loading } = storeToRefs(useUserStore());
</script>
<script>
export default {
async submitForm() {
try {
await useUserStore().createCertificate();
} catch (error) {
console.log(error);
}
},
};
</script>
This is the pinia store:
import { defineStore } from "pinia";
import forge from "node-forge";
export const useUserStore = defineStore({
id: "UserStore",
state: () => {
return {
loading: false,
};
},
actions: {
async createCertificate() {
this.loading = true;
try {
const keys = forge.pki.rsa.generateKeyPair(2048);
} catch (error) {
console.log(error);
} finally {
this.loading = false;
}
},
},
});
The problem is, that the doStuff function already runs and the loading property is still false inside the component and the spinner is very late visible.
I've got a view and a component. I'm trying to do auth here.
As a user, I input username and password, click login. This emits the information to the parent component, which makes a fetch request to API gateway in AWS. This fetch response has a header X-Session-Id that I'm interested in.
I've got the emit bit working fine.
However, I'm unable to pass the header value back to the component, and I'm unable to set new_password_required to true, which would add a new input field for a new password, as well as replace the login button with a reset password button.
I feel like I need to do this with props, but I'm unable to successfully pass the values from parent to child.
Also, should the reset password bit have its own component?
Here's my code below. This is my first frontend, so I'm not familiar with how I am supposed to share it (e.g. with a playground). Also, I'm trying to stick to vanilla vue for now since I'm learning (I've only get vue-router installed I think)
parent:
<template>
<div id="app" class="small-container">
<login-form #login:user="loginUser($event)" />
</div>
</template>
<script>
import LoginForm from "#/components/LoginForm.vue";
export default {
name: "Login",
components: {
LoginForm
},
data() {
return {
session_id: String,
new_password_required: Boolean
};
},
methods: {
async loginUser(loginEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/login/user",
{
method: "POST",
body: JSON.stringify(loginEvent)
}
);
const data = await response.json();
console.log(data);
if (data.headers["X-Session-Id"] != null) {
this.session_id = data.headers["X-Session-Id"];
this.new_password_required = true;
}
} catch (error) {
console.error(error);
}
},
async resetPassword(resetPasswordEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/reset/user/password",
{
method: "POST",
body: JSON.stringify(resetPasswordEvent)
}
);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
}
};
</script>
Component:
<template>
<div id="login-form">
<h1>Serverless App</h1>
<form>
<label for="email_address">Email Address:</label><br />
<input
v-model="login_details.email_address"
type="text"
id="email_address"
name="email_address"
/><br />
<label for="password">Password:</label><br />
<input
v-model="login_details.password"
type="password"
id="password"
name="password"
/>
<label v-if="new_password_required" for="new_password"
>New Password:</label
><br />
<input
v-if="new_password_required"
v-model="login_details.new_password"
type="password"
id="new_password"
name="new_password"
/>
</form>
<button v-if="!new_password_required" #click="loginUser($event)">
Login
</button>
<button v-if="new_password_required" #click="resetPassword($event)">
Reset Password
</button>
</div>
</template>
<script>
export default {
name: "LoginForm",
props: {
session_id: String,
new_password_required: {
type: Boolean,
default: () => false
}
},
data() {
return {
login_details: {
email_address: "",
password: "",
new_password: ""
}
};
},
methods: {
loginUser() {
console.log("testing loginUser...");
const loginEvent = {
email_address: this.login_details.email_address,
password: this.login_details.password
};
this.$emit("login:user", loginEvent);
},
resetPassword() {
console.log("testing resetPassword...");
const resetPasswordEvent = {
email_address: this.login_details.email_address,
password: this.login_details.password,
new_password: this.login_details.new_password,
session_id: this.login_details.sessionId
};
this.$emit("reset:Password", resetPasswordEvent);
}
}
};
</script>
Your child component looks good, however, you need to pass the props through in the parent component as shown here:
<login-form #login:user="loginUser($event)" :session-id="xidhere"
:new-password-required="newPasswordRequired"/>
As these values are updated in the parent component, the child component should be updated.
As a note, name your props using camel case, and then use kebab-case in your HTML.
So your login-form props should be updated to:
props: {
sessionId: String,
newPasswordRequired: {
type: Boolean,
default: () => false
}
},
Also, as you are emitting the event to parent, there may be no need to send the session id to the child, just add this to your api call before you send it.
Figured it out. I created a new child component for resetting password. Perhaps it can be dry'd up a bit? I'm new at this. Happy for any pointers :)
PARENT
<template>
<div id="app" class="small-container">
<login-form v-if="!new_password_required" #login:user="loginUser($event)" />
<reset-password-form
v-if="new_password_required"
:session_id="session_id"
#reset:password="resetPassword($event)"
/>
</div>
</template>
<script>
import LoginForm from "#/components/LoginForm.vue";
import ResetPasswordForm from "#/components/ResetPasswordForm.vue";
export default {
name: "Login",
components: {
LoginForm,
ResetPasswordForm
},
data() {
return {
session_id: "",
new_password_required: false
};
},
methods: {
async loginUser(loginEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/login/user",
{
method: "POST",
body: JSON.stringify(loginEvent)
}
);
const data = await response.json();
console.log(data);
if (data.headers["X-Session-Id"] != null) {
this.session_id = data.headers["X-Session-Id"];
this.new_password_required = true;
}
} catch (error) {
console.error(error);
}
},
async resetPassword(resetPasswordEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/reset/user/password",
{
method: "POST",
body: JSON.stringify(resetPasswordEvent)
}
);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
}
};
</script>
CHILD: login-form
<template>
<div id="login-form">
<h1>Serverless Release Dashboard</h1>
<form>
<label for="email_address">Email Address:</label><br />
<input
v-model="login_details.email_address"
type="text"
id="email_address"
name="email_address"
/><br />
<label for="password">Password:</label><br />
<input
v-model="login_details.password"
type="password"
id="password"
name="password"
/>
</form>
<button #click="loginUser($event)">
Login
</button>
</div>
</template>
<script>
export default {
name: "LoginForm",
data() {
return {
login_details: {
email_address: "",
password: ""
}
};
},
methods: {
loginUser() {
console.log("testing loginUser...");
const loginEvent = {
email_address: this.login_details.email_address,
password: this.login_details.password
};
this.$emit("login:user", loginEvent);
}
}
};
</script>
CHILD: reset-password-form
<template>
<div id="reset-password-form">
<h1>Serverless Release Dashboard</h1>
<form>
<label for="email_address">Email Address:</label><br />
<input
v-model="login_details.email_address"
type="text"
id="email_address"
name="email_address"
/><br />
<label for="password">Password:</label><br />
<input
v-model="login_details.password"
type="password"
id="password"
name="password"
/>
<label for="new_password">New Password:</label><br />
<input
v-model="login_details.new_password"
type="password"
id="new_password"
name="new_password"
/>
</form>
<button #click="resetPassword($event)">
Reset Password
</button>
</div>
</template>
<script>
export default {
name: "ResetPasswordForm",
props: {
email_address: String,
password: String,
session_id: String
},
data() {
return {
login_details: {
email_address: "",
password: "",
new_password: "",
session_id: ""
}
};
},
methods: {
resetPassword() {
console.log("testing resetPassword...");
const loginEvent = {
email_address: this.email_address,
password: this.password,
new_password: this.login_details.new_password,
session_id: this.session_id
};
this.$emit("reset:password", loginEvent);
}
}
};
</script>
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 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();
})
}