Unable to bind ngModel - javascript

I just started learning Angular and everything is pretty new to me. I have started a course and I got stuck with one part. When I try to do two way property binding, the ngModel is not working as expected.
This is the user.component.ts file:
import { Component, OnInit } from '#angular/core';
import { User } from '../../models/User';
#Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css'],
})
export class UsersComponent implements OnInit {
user: User = {
firstName: '',
lastName: '',
age: 0,
address: {
street: '',
city: '',
state: ''
}
}
users: User[];
showExtended: boolean = true;
loaded: boolean = false;
enableAdd: boolean = true;
showUserForm: boolean = false;
constructor() {
this.users = this.getUserDefaults();
}
ngOnInit(): void {
this.loaded = true;
}
private getUserDefaults() {
return [
{
firstName: 'John',
lastName: 'Doe',
age: 65,
address: {
street: '50 Main st',
city: 'Boston',
state: 'MA',
},
isActive: true,
registered: new Date('01/02/2018 08:30:00'),
hide: true
},
{
firstName: 'Kevin',
lastName: 'Johnson',
age: 34,
address: {
street: '20 School st',
city: 'Lynn',
state: 'MA',
},
isActive: false,
registered: new Date('03/11/2017 06:20:00'),
hide: true
},
{
firstName: 'Karen',
lastName: 'Williams',
age: 26,
address: {
street: '55 Mill st',
city: 'Miami',
state: 'FL',
},
isActive: true,
registered: new Date('11/02/2016 10:30:00'),
hide: true
},
];
}
addUser(user: User) {
this.users.push(user);
}
// toggleHide(user: User) {
// user.hide = !user.hide;
// }
onSubmit(e: any) {
console.log(123)
e.preventDefault()
}
fireEvent(e: any) {
console.log(e.type)
console.log(e.target.value)
}
}
This is the users.component.html file
<button (click)="showUserForm = !showUserForm" class="btn btn-dark mb-3">Add User</button>
<div class="card card-body mb-3" *ngIf="showUserForm">
<h2>Add User</h2>
<form (submit)="onSubmit($event)">
<div class="form-group">
<label>First Name</label>
<input type="text" class="form-control" name="firstName" [(ngModel)]="user.firstName">
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" class="form-control" name="lastName" [(ngModel)]="user.lastName">
</div>
<div class="form-group">
<label>Age</label>
<input type="number" class="form-control" name="age" [(ngModel)]="user.age">
</div>
<div class="form-group">
<label>Street Address</label>
<input type="text" class="form-control" name="street" [(ngModel)]="user.address.street">
</div>
<div class="form-group">
<label>City</label>
<input type="text" class="form-control" name="city" [(ngModel)]="user.address.city">
</div>
<div class="form-group">
<label>State</label>
<input type="text" class="form-control" name="state" [(ngModel)]="user.address.state">
</div>
<button (click)="addUser({ firstName: 'Mary', lastName: 'Jackson', isActive:true})" [disabled]="!enableAdd" class="btn btn-block mb-3">
Add New User
</button>
</form>
</div>
<h2>Users</h2>
<ul class="list-unstyled" *ngIf="loaded && users.length > 0">
<li class="card card-body mb-2" *ngFor="let user of users" [class.bg-light]="user.isActive">
<h3>{{ user.firstName }} {{ user.lastName }} <small *ngIf="user.age && user.address"><button (click)="user.hide = !user.hide" class="btn btn-dark btn-sm"><i [ngClass]="user.hide ? 'fa fa-plus' : 'fa fa-minus'" class="fa fa-plus"></i></button></small></h3>
<ul class="list-group" *ngIf="!user.hide && user.age && user.address">
<li class="list-group-item">Age: {{ user.age }}</li>
<li class="list-group-item">
Address: {{ user.address.street }}, {{ user.address.city }},
{{ user.address.state }}
</li>
<li class="list-group-item">Joined: {{ user.registered | date }}</li>
</ul>
</li>
</ul>
<h4 *ngIf="users.length == 0">No Users Found</h4>
<h4 *ngIf="!loaded">Loading Users...</h4>
And this is the interface
export interface User {
firstName: string,
lastName: string,
age?: number,
address?: {
street?: string,
city?: string,
state?: string
},
isActive?: boolean,
registered?: any,
hide?: boolean
}
In the html file where I have the inputs the ngModel is not working as expected. I am trying to bind these inputs with
user: User = {
firstName: '',
lastName: '',
age: 0,
address: {
street: '',
city: '',
state: ''
}
}
ngModel for firstName, lastName and age works just fine. But when I try to bind ngModel with user.address.street or user.address.city or user.address.state, I get the follwoing error:
Error: src/app/components/users/users.component.html:19:89 - error TS2532: Object is possibly 'undefined'.
19 <input type="text" class="form-control" name="street" [(ngModel)]="user.address.street">
~~~~~~
src/app/components/users/users.component.ts:5:16
5 templateUrl: './users.component.html',
~~~~~~~~~~~~~~~~~~~~~~~~
Error occurs in the template of component UsersComponent.
Does anybody know why is this happening and how to fix this problem? Thank you in advance.

Related

Update Specific Component rather than whole page

I am currently making a small application where users can add contacts to a list. i am able to add the contacts to a list and it shows up in a separate div. at the moment when i click save, i have some code which basically has await and the whole app refreshes. the problem i am currently having is when i insert data for a user and click save it refreshes the whole screen but instead when i click save i want it to only refresh the contact component.
I currently have a piece of code which essentially refreshes all the components which is okay but now i want to try and just refresh the component i have added data too.
i have included the code below:
`
<div class="container">
<div class="main-contact-list">
<h2>Contacts</h2>
<li *ngFor="let contact of contacts;">
<button type="button" (click)="getContact(contact.id)">
<span class="name"> {{contact.firstName}} {{contact.lastName}}</span>
</button>
</li>
</div>
<div class="form">
<form [formGroup]="fg">
<div class="actionBtns">
<button class="btn btn-primary" (click)="saveContact()">Edit/Save</button>
<div class="divider"></div>
<button class="btn btn-primary" (click)="deleteContact(contact.id)">Delete</button>
</div>
<div class="row" class="row-object">
<input type="text" class="form-control" [(ngModel)]="contact.firstName" name="firstName"
formControlName="firstName" placeholder="First Name" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.lastName" name="lastName"
formControlName="lastName" placeholder="Last Name" />
<div class="col">
<input type="text" class="form-control" [(ngModel)]="contact.emailAddress" name="emailAddress"
formControlName="emailAddress" placeholder="Email Address" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.address1" name="address1"
formControlName="address1" placeholder="Address1" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.address2" name="address2"
formControlName="address2" placeholder="Address2" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.city" name="city"
formControlName="city" placeholder="City" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.postCode" name="postCode"
formControlName="postCode" placeholder="Post Code" />
<div class="divider"></div>
</div>
</div>
</form>
<div class="activityForm" *ngIf="contact.id">
<app-activities [childItem]="contact"></app-activities>
</div>
</div>
`
`
import { Component, OnInit } from '#angular/core';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
FormsModule
} from '#angular/forms';
import { AppService } from '../app.service';
//interface for Contact
interface Contact {
id?: number;
firstName: string;
lastName: string;
emailAddress: string;
address1: string;
address2: string;
city: string;
postCode: string;
}
#Component({
selector: 'app-contact-details',
templateUrl: './contact-details.component.html',
styleUrls: ['./contact-details.component.css'],
})
export class ContactDetailsComponent implements OnInit {
constructor(private appService: AppService, private fb: FormBuilder) { }
fg!: FormGroup;
contacts: Contact[] = [];
contact: any = {};
//get the form field as a form control. it will useful for validation and etc
get firstNameField(): FormControl {
return this.fg.get('firstName') as FormControl;
}
get lastNameField(): FormControl {
return this.fg.get('lastName') as FormControl;
}
get emailAddressField(): FormControl {
return this.fg.get('emailAddress') as FormControl;
}
get address1Field(): FormControl {
return this.fg.get('address1') as FormControl;
}
get address2Field(): FormControl {
return this.fg.get('address2') as FormControl;
}
get postCodeField(): FormControl {
return this.fg.get('postCode') as FormControl;
}
async ngOnInit() {
this.initForm();
this.getContacts();
}
//form init. here we create the reactive form. https://angular.io/guide/reactive-forms
initForm(): void {
let id = Date.now() * Math.random();
this.fg = this.fb.group({
id: [id],
firstName: ['', [Validators.required]],
lastName: ['', [Validators.required]],
emailAddress: ['', [Validators.required, Validators.email]],
address1: ['', [Validators.required]],
address2: ['', [Validators.required]],
city: ['', [Validators.required]],
postCode: ['', [Validators.required]],
});
}
//save function
async saveContact() {
console.log(this.fg);
console.log(this.contact);
//if form doesn't have any validation error this if condition will executed
if (this.fg.valid) {
const newContact: Contact = {
firstName: this.fg.value.firstName,
lastName: this.fg.value.lastName,
emailAddress: this.fg.value.emailAddress,
address1: this.fg.value.address1,
address2: this.fg.value.address2,
city: this.fg.value.city,
postCode: this.fg.value.postCode,
};
if (this.contact?.id) {
this.appService
.editContacts(this.contact.id, newContact).subscribe();
} else {
this.appService
.addContacts(newContact).subscribe();
}
this.fg.reset(); //resetting the form array
await this.refresh();
} else {
console.log('this is invalid ');
}
}
refresh(): void {
window.location.reload();
}
async getContacts(): Promise<void> {
await this.appService
.getContacts()
.subscribe((contacts: any) => (this.contacts = contacts));
}
edit(id: number): void {
const data = this.contacts[id];
this.fg.patchValue(data);
this.refresh();
}
getContact(id: number | undefined) {
this.appService
.get(id)
.subscribe((contact: any) => (this.contact = contact));
}
selectContact(contact: Contact) {
this.contact = contact;
}
deleteContact(id: number | undefined) {
this.appService.deleteContact(id).subscribe();
this.refresh();
}
}
`
I did manage to figure this problem out. Most tutorials were stating that you basically use the router route so when something happens to refresh the route and it should show the new page.
Instead I just added #Input in front of my array and object, so when in my main HTML page I am using the *ngFor statement to iterate through the array, every time it is updated it automatically shows the new list

Having user input from vue.js stored in an array on javascript

vue.js
<template>
<input id="email" v-model="email" type="text" placeholder="Email">
<input id="name" v-model="name" type="text" placeholder="Name">
<input id="phone" v-model="phone" type="text" placeholder="Phone no.">
<input id="age" v-model="age" type="text" placeholder="Age">
<button onclick="createprofile()">Submit</button>
</template
javascript file
<script>
export const useProfileStore = defineStore('profile', {
state: () => {
return {
Name: {{ name }},
Phone: {{ phone }},
email: {{ email }},
age: {{ age }}
}
},
I needed the array like this so i could use it to create profiles for the users on the website.
const users = [
{
name: "Fred",
phone: "00997878",
email: "abc#t.com",
age: "20"
},
{
name: "Tom",
phone: "0998899",
email: "abc#t.com",
age: "23"
},
</script>
I thought it would be some kind of for loop for the array but i'm really not sure how to do it, any help would be great thanks.
You already have a user data and want to bind that in inputs to create a user profile ? If Yes, You can use v-for directive in the template to iterate over an array.
Demo :
new Vue({
el: '#app',
data: {
users: [{
name: "Fred",
phone: "00997878",
email: "abc#t.com",
age: "20"
},{
name: "Tom",
phone: "0998899",
email: "abc#t.com",
age: "23"
}]
},
methods: {
createprofile() {
console.log(this.users);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(user, index) in users" :key="index">
<input id="email" v-model="user.email" type="text" placeholder="Email">
<input id="name" v-model="user.name" type="text" placeholder="Name">
<input id="phone" v-model="user.phone" type="text" placeholder="Phone no.">
<input id="age" v-model="user.age" type="text" placeholder="Age">
</div><br>
<button #click="createprofile()">Submit</button>
</div>

v-model not working with javascript functions

I am new to Vue JS and have been learning it from the documentation provided. My project is a simple task adding web-app. On using the v-model directive, I'm not getting any output. My javascript function to add the task is apparently not being called.
<template>
<div id="text">
TASKS:
<form onsubmit="return addTodo()">
<input type="text" class="todo-input" placeholder="What's up" v-model="message">
<input type="date" class="todo-input" v-model="ddate" >
<input type="submit" value="Add">
</form>
<div v-for="(todo, index) in todos" :key="todo.id" class="todo-item">
<div>
{{todo.id}}{{todo.title}}{{todo.date}}
</div>
</div>
</div>
</div>
</template>
export default {
name: 'todo-list',
data () {
return {
message: '',
ddate: '',
idForTodo: 1,
todos: [
]
}
},
methods: {
addTodo(){
if(this.message.trim().length == 0){
return
}
this.todos.push({
id: this.idForTodo,
title: this.message,
completed: false,
editing: false,
date: this.ddate,
})
this.ddate = ''
this.message = ''
this.idForTodo++
},
}
}
Looks like someone edited the question with correct code while I was writing the answer. I tried and tested same code in code snippet and it's working.
const app = new Vue({
el: '#text',
data() {
return {
message: '',
ddate: '',
idForTodo: 1,
todos: [
]
}
},
methods: {
addTodo(){
console.log(this.message)
if(this.message.trim().length == 0){
return
}
this.todos.push({
id: this.idForTodo,
title: this.message,
completed: false,
editing: false,
date: this.ddate,
})
this.ddate = ''
this.message = ''
this.idForTodo++
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.0/vue.js"></script>
<div id="text">
TASKS:
<form>
<input type="text" class="todo-input" placeholder="What's up" v-model="message">
<input type="date" class="todo-input" v-model="ddate" >
<button v-on:click.prevent="addTodo">Add</button>
</form>
<div v-for="(todo, index) in todos" :key="todo.id" class="todo-item">
<div>
{{todo.id}} {{todo.title}} {{todo.date}}
</div>
</div>
</div>

Dynamic generated form with checkbox in Vue

I have dynamic generated form in Vue. Every loop have v-model. Everything work fine. But when I use checkboxes v-model work for all loops not on one like in input type text. Can You help me solved this problem? Below Vue code:
<fieldset>
<div class="form-row mb-2" v-for="input, index in journal" :key="index">
<div class="col-auto">
<label for="date">Data</label>
<Datepicker v-model="input.date" input-class="form-control" :input-attr="{id: 'date', name: 'date'}" style="width: 100%;" />
</div>
<div class="col-md-2">
<label for="timeStart">Od</label>
<Datepicker type="time" v-model="input.timeStart" format="HH:mm" input-class="form-control" :input-attr="{id: 'timeStart', name: 'timeStart'}" style="width: 100%;" />
</div>
<div class="col-md-2">
<label for="timeEnd">Do</label>
<Datepicker type="time" v-model="input.timeEnd" format="HH:mm" input-class="form-control" :input-attr="{id: 'timeEnd', name: 'timeEnd'}" style="width: 100%;" />
</div>
<div class="col-md-2">
<label for="players">Lista obecności</label>
<div class="form-check" v-for="item in input.players">
<input v-model="item.checked" type="checkbox" class="form-check-input" :id="'id-'+item.id+'set'+index">
<label class="form-check-label" :for="'id-'+item.id+'set'+index">{{ item.fullName }}</label>
</div>
</div>
<div class="col-auto">
<label for="description">Opis</label>
<textarea v-model="input.description" class="form-control" rows="7" id="description" placeholder="Opis"></textarea>
</div>
<div class="col-auto" #click="addInput" v-show="index == journal.length-1 && journal.length < 16">
<ButtonVue style="margin-top: 30px;" title="Dodaj" type="button" cancelWidth="true" color="btn-success"><i class="fas fa-plus"></i></ButtonVue>
</div>
<div class="col-auto align-self-start" #click="removeInput(index)" v-show="index || ( !index && journal.length > 1)">
<ButtonVue style="margin-top: 30px;" title="Usuń" type="button" cancelWidth="true" color="btn-danger"><i class="fas fa-minus"></i></ButtonVue>
</div>
</div>
</fieldset>
 
data() {
return {
contact: [],
journal: [{
date: "",
timeStart: "",
timeEnd: "",
players: "",
description: ""
}],
contacts: [],
}
},
Methods:
Method for creating dynamic form
addInput() {
this.journal.push({
date: "",
timeStart: "",
timeEnd: "",
players: this.contact,
description: ""
});
},
And here is the method which gets players from contacts
getContacts() {
this.pageLoader = true;
this.$http.get('/pkpar/get-contacts')
.then(({
data
}) => {
this.contacts = data.contacts;
for(let i=0; i<this.contacts.length; i++)
{
this.contact.push({'id': this.contacts[i]['id'], 'fullName' :
this.contacts[i]['fullName'], 'checked': true});
}
this.journal[0].players = this.contact;
this.pageLoader = false;
})
.catch(error => {
console.log(error);
});
},
Your addInput method creates and pushes new object into journal array, but each object created this way has a players property which references same array (this.contact)
The Difference Between Values and References in JavaScript
Easiest (but not most optimal) way to handle this is to create a copy of the array and objects inside for each new journal:
addInput() {
this.journal.push({
date: "",
timeStart: "",
timeEnd: "",
players: this.contact.map((player) => ({ ...player })),
description: ""
});
},

Vue.js message success Axios Javascript

i I want to insert my my in database.axios is perfect and enter data in db but I want a message 'data is entered' for the user ->
how it is important to insert the message.
thanks
<form>
<div class="well">
<h4> Add User</h4>
<div class="form-group">
<label class="pull-left"> First Name </label>
<input type="text" class="form-control" placeholder="First Name" v-model="User.first_name">
</div>
<div class="form-group">
<label class="pull-left"> Last Name </label>
<input type="text" class="form-control" placeholder="Last Namen" v-model="User.last_name">
</div>
<div class="form-group">
<label class="pull-left"> Email </label>
<input type="text" class="form-control" placeholder="Email " v-model="User.email">
</div>
</div>
<button type="submit" class="btn btn-large btn-block btn-primary full-width" #click="addToAPI">Submit</button>
<button class="btn btn-large btn-block btn-success full-width">Go User</button>
</form>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'hello',
data() {
return {
msg: 'Welcome to Your Vue.js App',
User: { first_name: '', last_name: '', email: '' }
}
},
methods: {
addToAPI() {
let newUser = {
first_name: this.User.first_name,
last_name: this.User.last_name,
email: this.User.email
}
console.log(newUser)
axios.post('http://localhost:3000/(localaccess)',newUser)
.then(response => {
console.log('success:', response)
})
.catch(error => {
console.log('error:', error)
})
}
}
}
</script>
Add a data object where you will store the message, like:
<script>
import axios from 'axios'
export default {
name: 'hello',
data() {
return {
msg: 'Welcome to Your Vue.js App',
User: { first_name: '', last_name: '', email: '' },
message: ''
}
},
methods: {
addToAPI() {
let self = this;
let newUser = {
first_name: this.User.first_name,
last_name: this.User.last_name,
email: this.User.email
}
axios.post('http://localhost:3000/(localaccess)',newUser)
.then(response => {
self.message = 'Data is entered'
})
.catch(error => {
self.message = 'Error'
})
}
}
}
</script>
And now add it to some place ih your template - {{message}}.

Categories