All files are attached as below,
component.html - This is component file
component.ts - This is component ts file
admin-service - This is service file
admin.ts - This is model file
add-que.html - This is component html file.
<div>
<form [formGroup]="adminForm" (ngSubmit)="newQuestion()">
<div class="form-group">
<label for="exampleInputEmail1">Question</label>
<input formControlName="description" type="description" placeholder="Enter question"
class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
<small class="text-danger"
*ngIf="!adminForm.get('description').valid && adminForm.get('description').touched">
Please Enter a Question</small>
</div>
<div class="form-group m-auto">
<div class="col-6">
(a)<input formControlname="alternatives" type="text">
(b)<input formControlname="alternatives" type="text">
</div>
<div class="col-6">
(c)<input formControlname="alternatives" type="text">
(d)<input formControlname="alternatives" type="text">
</div>
</div>
<div class="m-auto">
<input type="submit" value="Add" class="btn btn-primary"/>
</div>
</form>
</div>
**add-que.ts** - This is component ts file.
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-add-question',
templateUrl: './add-question.component.html',
styleUrls: ['./add-question.component.css']
})
export class AddQuestionComponent implements OnInit {
adminForm = new FormGroup({
description: new FormControl("", [Validators.required]),
alternatives: new FormControl("", [Validators.required])
});
constructor(private adminService: AdminService, private router: Router) { }
ngOnInit(): void {
}
newQuestion(){
if(this.adminForm.valid){
this.adminService.addQue(this.adminForm.value).subscribe(res => {
this.adminForm.reset();
this.router.navigate(["/admin"]);
})
}
}
}
**admin-service.ts** - This is service ts file.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Admin } from "../model/admin";
#Injectable({
providedIn: 'root'
})
export class AdminService {
private ROOT_URL = "http://localhost:3300/questions";
private httpOptions = {
headers: new HttpHeaders().set("Content-Type", "application/json")
};
constructor(private http: HttpClient) { }
getQuestion(): Observable<Admin[]> {
return this.http.get<Admin[]>(this.ROOT_URL);
}
getQue(id: string){
return this.http.get<Admin>(`${this.ROOT_URL}/${id}`);
}
addQue(admin){
return this.http.post<any>(this.ROOT_URL, admin, this.httpOptions);
}
}
**admin.ts** - This is model ts file.
export interface Admin {
description: String,
alternatives: [
{
text: {
type: String,
required: true
},
isCorrect: {
type: Boolean,
required: true,
default: false
}
}
]
}
I did created add-question component with form as above. I did tried to add new question through form in angular but not get anything. i did attached my files as above.
you sould'nt use this.router.navigate(["/admin"]); to try "refresh a component". That's no work (there are no changes). In general you has a component with a ngOnInit. Simply get out the code under a ngOnInit in a function and call this function in subscribe, e.g.
ngOnInit()
{
this.init()
}
init()
{
..here you initialize your variables
}
submit(){
...
.subscribe(res=>{
....
this.init()
})
}
Related
I created all-question component it is visible but when i click on particular question view that redirect to that id but not got details on the id page. here i attached some files,
Admin Service file
Model file
Detail question component file
Detail question ts file
All question component file
All question ts file
admin.service.ts - this is admin service file
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Admin } from "../model/admin";
#Injectable({
providedIn: 'root'
})
export class AdminService {
private ROOT_URL = "http://localhost:3300/questions";
private httpOptions = {
headers: new HttpHeaders().set("Content-Type", "application/json")
};
constructor(private http: HttpClient) { }
getQuestion(): Observable<Admin[]> {
return this.http.get<Admin[]>(this.ROOT_URL);
}
getQue(id: string){
return this.http.get<Admin>(`${this.ROOT_URL}/${id}`);
}
addQue(admin){
return this.http.post<any>(this.ROOT_URL, admin, this.httpOptions);
}
}
**model=>admin.ts** - this is model file
export interface Admin {
description: String,
// alternatives: String
alternatives: [
{
text: {
type: String,
required: true
},
isCorrect: {
type: Boolean,
required: true,
default: false
}
}
]
}
**que-detail.comp.html** - this is question detail component file
<div class="card">
<div class="card-body">
<h4>Question: {{admin.description}}</h4>
<h4>Options: {{admin.alternatives}}</h4>
</div>
</div>
**que-detail.comp.ts** - this is question details ts file
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Subscription } from 'rxjs';
import { Admin } from '../model/admin';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-question-detail',
templateUrl: './question-detail.component.html',
styleUrls: ['./question-detail.component.css']
})
export class QuestionDetailComponent implements OnInit {
id: string;
admin: Admin;
adminSub$: Subscription;
constructor(private adminService: AdminService, private route: ActivatedRoute ) { }
ngOnInit(): void {
this.id = this.route.snapshot.paramMap.get("id");
this.adminSub$ = this.adminService.getQue(this.id).subscribe(admin => {
this.admin = admin;
});
}
}
**all-que.comp.html** - this is all question component file
<div class="card bg-dark text-white my-4 dashboard-bg">
<div class="card-body text-center">
<ul class="questions" *ngIf="admin$ | async as questions">
<li *ngFor="let question of questions">
<h4 class="card-title my-2">{{question.description}}</h4>
<h5 *ngFor="let x of question.alternatives">{{x.text}}</h5>
<a [routerLink]="question._id">View</a>
</li>
</ul>
</div>
</div>
**all-que.comp.ts** - this is all question ts file
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
import { Admin } from '../model/admin';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-all-question',
templateUrl: './all-question.component.html',
styleUrls: ['./all-question.component.css']
})
export class AllQuestionComponent implements OnInit {
admin$: Observable<Admin[]>;
constructor(private adminService: AdminService) { }
ngOnInit(): void {
this.admin$ = this.adminService.getQuestion()
}
}
Your Question Details Component should be like,
<div class="card">
<div class="card-body">
<h4>Question: {{admin.description}}</h4>
<h5>Options:</h5>
<ng-container *ngFor="alternative of admin.alternatives">
<h6>{{alternative.text}}</h6>
</ng-container>
</div>
You are just using the undefined questions variable ig.
Hey I am trying to display a simple error message on my login page if the login fails. Following is my login.component.html:
<div class="container shadow login-container">
<div class="row">
<div class="col-sm-12 text-center">
<div class="error-message">
<app-server-error [errorMessage]="error" ></app-server-error> -----> not displaying on screen
</div>
<div class="login-form-container">
<div class="login-input-container">
<input [(ngModel)]="user.email" type="email" placeholder="Enter your email" class="buddha-input"/>
</div>
<div class="login-input-container">
<input [(ngModel)]="user.password" type="password" placeholder="Enter password" class="buddha-input"/>
</div>
<div class="login-input-container">
<button (click)="tryLogin()" class="login-form-button">Login</button>
</div>
</div>
</div>
</div>
</div>
Following is my server-error.component.html:
<p>
{{errorMessage}}
</p>
Following is my server-error.component.ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-server-error',
templateUrl: './server-error.component.html',
styleUrls: ['./server-error.component.css']
})
export class ServerErrorComponent implements OnInit {
#Input() public errorMessage: string;
constructor() { }
ngOnInit() {
}
}
"Error" is not showing up on the screen and I am not getting errors on console either. Please do let me know how I can fix this? Thank you
#Input() public errorMessage: string; expects error to be a string.
Define error like this in .ts
error = 'error message'
Working Demo
With [errorMessage]="error", error should be a variable.
So you need to define it in your component.
But if you want to display the string "error",
then pass it like this [errorMessage]="'error'" or errorMessage="error"
The other posted answer may solve your problem but I would go with Service which will be responsible for showing an error from everywhere in the Application and also you can have a full control on the error component as it is centralized at a one place:
error.service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class ErrorService {
constructor() { }
private errorMessages = new BehaviorSubject('');
errorMessage = this.errorMessages.asObservable();
showError(message: string) {
this.errorMessages.next(message)
}
}
Error.Component.ts:
import { Component, OnInit, Input } from '#angular/core';
import {ErrorService } from "../error.service";
#Component({
selector: 'app-server-error',
templateUrl: './server-error.component.html',
styleUrls: ['./server-error.component.css']
})
export class ServerErrorComponent implements OnInit {
private errorMessage : any;
constructor(public errorService : ErrorService) { }
ngOnInit() {
this.errorService.errorMessage.subscribe(value => {
this.errorMessage = value;
})
}
}
App.Component.html:
// show it in app.component
<div class="error-message">
<app-server-error></app-server-error>
</div>
Use (in Login Component):
import { Component, OnInit } from "#angular/core";
import {ErrorService } from "../error.service";
#Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.css"]
})
export class LoginComponent implements OnInit {
constructor(public errorService: ErrorService) {}
user = {};
ngOnInit() {}
tryLogin(){
this.errorService.showError('An error');
}
}
I'm trying to display the data for every corresponding name(data from json-server) like firstName, lastName, email etc.
user.component.html - this is where I want to display the corresponding data
<div>
<h2>{{selectedUser.firstName | uppercase}} Details</h2>
<div><span>id: </span>{{selectedUser.id}}</div>
<div>
<label>First name:
<input [(ngModel)]="selectedUser.firstName" placeholder="First Name" />
</label>
</div>
<div>
<label>Last name:
<input [(ngModel)]="selectedUser.lastName" placeholder="Last Name" />
</label>
</div>
<div>
<label>Email:
<input [(ngModel)]="selectedUser.email" placeholder="example.com" />
</label>
</div>
<div>
<label>Start date:
<input [(ngModel)]="selectedUser.startDate" placeholder="Start date" />
</label>
</div>
</div>
user.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { User } from '../user';
import { DataService } from '../data.service';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
users: any
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.getUsers()
.subscribe(data => this.users = data);
}
}
home.component.html - this is where I display the name for each user and when I click I want to display the content on the user page
<h2>Users</h2>
<ul class="users">
<a routerLink="/user"><li *ngFor="let user of users['users']"
[class.selected]="user === selectedUser"
(click)="onSelect(user)">
<span class="badge">{{user.id}}</span> {{user.firstName}} {{user.lastName}}
</li> </a>
</ul>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Router } from '#angular/router';
import { User } from '../user';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
public users = [];
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.getUsers()
.subscribe(data => this.users = data);
}
}
And here is the data.service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Observable } from 'rxjs';
import { User } from "./user";
#Injectable({
providedIn: 'root'
})
export class DataService {
selectedUser: User;
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]>{
return this.http.get<User[]>("http://localhost:3000/users");
}
onSelect(user: User): void {
console.log(user);
this.selectedUser = user;
}
}
It's work when I display the names on the home page.
I've also made this display of data on a single page(home.component) and it worked, but now I must to display it on another page. I've tried to do this with BehaviourSubject but I got stuck.
You are incorrectly accessing the property in your .html files. Example:
<h2>Users</h2>
<ul class="users">
<a routerLink="/user"><li *ngFor="let user of users['users']"
[class.selected]="user === selectedUser"
(click)="onSelect(user)">
<span class="badge">{{user.id}}</span> {{user.firstName}} {{user.lastName}}
</li> </a>
</ul>
Should be:
<h2>Users</h2>
<ul class="users">
<a routerLink="/user"><li *ngFor="let user of users['users']"
[class.selected]="user === dataService.selectedUser"
(click)="dataService.onSelect(user)">
<span class="badge">{{user.id}}</span> {{user.firstName}} {{user.lastName}}
</li> </a>
</ul>
You can let the DataService cache the users for you in a BehaviorSubject and all components bind to users$ property of the DataService.
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
import { HttpClient, HttpResponse, HttpErrorResponse } from '#angular/common/http';
import { User } from "./user";
#Injectable({
providedIn: 'root'
})
export class DataService {
isLoading$ = new BehaviorSubject<boolean>(false);
users$ = new BehaviorSubject<User[]>(undefined);
constructor(private http: HttpClient) { }
loadUsers(): void {
this.isLoading$.next(true);
this.http.get<User[]>("http://localhost:3000/users", { observe: "response" }).subscribe(
(res: HttpResponse<User[]>) => {
this.users$.next(res.body);
},
(res: HttpErrorResponse) => this.onError(res),
() => this.isLoading$.next(false)
);
}
private onError(res: HttpErrorResponse) {
console.log(res.message);
}
}
I am building my first Angular app and need some help. The component I'm working on is an image search box. The user enters search query, request is sent to API, API responds with JSON data. Why is my *ngFor loop not working? The iterable is updated when the server sends response.
image-search.component.ts
import { Component, OnInit } from '#angular/core';
import { ImageSearchService } from './image-search.service';
import { Image } from '../shared/image';
#Component({
selector: 'vb-image-search',
templateUrl: './image-search.component.html',
styleUrls: ['./image-search.component.css'],
providers: [ImageSearchService]
})
export class ImageSearchComponent implements OnInit {
images: Image[] = [];
constructor(private ImageSearchService: ImageSearchService) { }
ngOnInit() {
}
getImages(query: string) {
this.ImageSearchService.getImages(query)
.subscribe(function(images) {
this.images = images;
});
}
onClick(query:string) {
this.getImages(query);
}
}
image-search.service.ts
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Image } from '../shared/image';
#Injectable()
export class ImageSearchService {
constructor(private http: Http) {}
getImages(query: string): Observable<any[]> {
return this.http.get(`http://localhost:3000/api/search/${query}`)
.map(this.extractData)
}
private extractData(res: Response) {
let body = res.json();
return body.data.map(e => new Image(e.farmID, e.serverID, e.imageID, e.secret)) || {};
}
}
image.ts
export class Image {
constructor(public farmID: string, public serverID: string, public imageID: string, public secret: string) {
this.farmID = farmID;
this.serverID = serverID;
this.imageID = imageID;
this.secret = secret;
}
}
image-search.component.html
<div class="col-lg-6 col-md-6">
<div class="input-group">
<input type="text" [(ngModel)]="query" class="form-control" placeholder="Search for images..." />
<span class="input-group-btn">
<button (click)="onClick(query)" class="btn btn-default" type="button">Go!</button>
</span>
</div>
<h2>Images</h2>
<div *ngFor="let image of images">
{{image.imageID}}
</div>
</div>
The reason is very simple. In typescript the function call back loses the current scope if you use function(){} so instead you have to used => {} to retain the current scope. So please modify your current getImages method as mentioned below:
getImages(query: string) {
this.ImageSearchService.getImages(query)
.subscribe(images => {
this.images = images;
});
}
I'm currently using Angular Forms version 2.0.0 and trying to make a contact us modal with a contact form inside.
Immediately after the ContactComponent loads, I get:
EXCEPTION: this.form._updateTreeValidity is not a function
I've already seen some other stack posts suggesting that using FormGroup instead of FormBuilder to init the form object in the component constructor is now standard with the new API so I've updated that.
I import ReactiveFormsModule and FormsModule along with all the form related components and the error doesn't seem to be module related.
My TypeScript isn't throwing errors in compile time and Visual Studio Intellisense seems to be able to find all FormGroup functions just fine so why is this happening at runtime?...
My code:
contact.component.ts:
import { Component, Input, ViewChild } from '#angular/core';
import { ApiService } from '../../../services/api.service';
import { ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { FormsModule, ReactiveFormsModule, FormGroup, FormControl, Validators } from '#angular/forms';
import 'rxjs/Rx';
declare var jQuery: any;
#Component({
selector: 'my-contact',
templateUrl: 'app/modules/footer/contact/contact.html'
})
export class ContactComponent {
private contactForm: FormGroup;
private invalidEmail: boolean;
private invalidSubject: boolean;
private invalidMessage: boolean;
constructor(private apiService: ApiService, private router: Router, private route: ActivatedRoute) {
this.contactForm = new FormGroup({
emailControl: new FormControl('', <any>Validators.required),
subjectControl: new FormControl('', <any>Validators.required),
messageControl: new FormControl('', <any>Validators.required)
});
}
submit() {
if (this.contactForm.valid) {
this.apiService.sendMessage(this.contactForm.controls['emailControl'].value, this.contactForm.controls['subjectControl'].value, this.contactForm.controls['messageControl'].value);
}
if (!this.contactForm.controls['emailControl'].valid) {
this.invalidEmail = true;
}
if (!this.contactForm.controls['subjectControl'].valid) {
this.invalidSubject = true;
}
if (!this.contactForm.controls['messageControl'].valid) {
this.invalidMessage = true;
}
}
ngOnInit() {
this.invalidEmail = false;
this.invalidSubject = false;
this.invalidMessage = false;
}
}
contact.html:
<modal-header class="c-no-border" [show-close]="true">
<h4 class="modal-title text-uppercase">Send us a message</h4>
</modal-header>
<form novalidate #contactForm [formGroup]="contactForm" (ngSubmit)="submit()">
<div class="modal-body">
<div class="form-group">
<label for="email" class="control-label">Email</label>
<input name="email" formControlName="emailControl" placeholder="" type="text" class="c-square form-control c-margin-b-20" id="email">
<div class="c-font-red-1" *ngIf="invalidEmail" style="position: absolute;">*Required</div>
<label for="subject" class="control-label">Subject</label>
<input name="subject" formControlName="subjectControl" placeholder="" type="text" class="c-square form-control c-margin-b-20" id="subject">
<div class="c-font-red-1" *ngIf="invalidSubject" style="position: absolute;">*Required</div>
<textarea formControlName="messageControl" style="resize: vertical;" class="c-square form-control c-margin-b-20" id="content" (keyup.enter)="submit()"></textarea>
<div class="c-font-red-1" *ngIf="invalidMessage" style="position: absolute;">*Required</div>
</div>
</div>
<modal-footer class="c-no-padding">
<button type="button" class="btn c-btn-square c-btn-bold c-btn-uppercase pull-right">Cancel</button>
<button type="submit" class="btn c-theme-btn c-btn-square c-btn-bold c-btn-uppercase pull-right" style="margin-right: 10px;">Send</button>
</modal-footer>
</form>
app.module.ts:
import { NgModule, enableProdMode } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { Ng2Bs3ModalModule } from 'ng2-bs3-modal/ng2-bs3-modal';
import { QueuesModule } from './modules/queues/queues.module';
import { OrderModule } from './modules/order/order.module';
import { AccountModule } from './modules/account/account.module';
import { AdminModule } from './modules/admin/admin.module';
import { routing } from './app.routing';
import { GridModule } from '#progress/kendo-angular-grid';
import { SplashComponent } from './modules/splash/splash.component';
import { ContactComponent } from './modules/footer/contact/contact.component';
import { SharedModule } from './shared/shared.module';
import { EmailValidator } from './shared/utilities/custom-validators'
import { CookieService } from 'angular2-cookie/services/cookies.service';
import { HttpModule, Response } from '#angular/http';
import { StringService } from './services/string.service';
import { ApiService } from './services/api.service';
import { UserService } from './services/user.service';
import { OrderService } from './services/order.service';
import { OrderGuard } from './services/order-guard.service';
import { FooterComponent } from './modules/footer/footer.component';
import { ErrorComponent } from './modules/error/error.component';
import { CustomFormsModule } from "ng2-validation";
#NgModule({
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
QueuesModule,
OrderModule,
AccountModule,
AdminModule,
routing,
GridModule,
SharedModule,
Ng2Bs3ModalModule,
CustomFormsModule
],
declarations: [
AppComponent,
SplashComponent,
FooterComponent,
ErrorComponent,
ContactComponent
],
providers: [
StringService,
ApiService,
UserService,
CookieService,
OrderService,
OrderGuard
],
bootstrap: [AppComponent],
exports: [
]
})
export class AppModule {
}
Binding the template variable #contactForm appears to cause a name conflict and blow up the template processor as it tries to turn the attached template variable into an NgForm on the backend. Everywhere I have seen model driven forms used there is no template variable binding on the form, whereas there is a #tv="ngForm" utilized in template driven forms. It appears there was a miss on the mixing of the two forms approaches which resulted in the error.
Simply removing it will resolve the issue.
When you incorrectly add to your template formGroup="..." instead of [formGroup]="..." you'll also get this error message.
It's been a while, but for me the problem was passing the formGroup to the template instead of referencing it directly.
E.g. this is not working
<ng-template #selectField
let-field
let-group
let-frmName="frmName">
<ng-container [formGroup]="group">
<mat-form-field fxFlex="32"
floatLabel="always">
<mat-label>{{field.label}}</mat-label>
<mat-select [formControlName]="frmName"
[required]="field.required">
<mat-option *ngFor="let option of field.options"
[value]="option.value">
{{option.description}}
</mat-option>
</mat-select>
</mat-form-field>
</ng-container>
</ng-template>
If I use directly my formGroup instance it works just fine.
Hope this helps.
I had a dynamic form. I got above error since I didn't init it inside the ngOnInit()
Solution:
checkInForm: FormGroup;
ngOnInit(){
this.checkInForm = this.formBuilder.group({});
}
Sometime this error is caused when you use a [formGroup] to pass the string of the formGroup name. Use formGroupName instead.
This can happen when you're using the input name formControl or formGroup on a custom component which isn't a form control.
Change your custom component to accept a different input name:
Change this
#Input()
formGroup: string
// ^ Causes issues
To this
#Input()
group: string
I forgot to pass my form to the reusable input <app-input [form]="myForm"></app-input>
app-input.component.html
<form [formGroup]="form">
<input [type]="type" [placeholder]="placeholder [formControlName]="formControlName" />
</form>
app-input.component.ts
#Input() type: string = '';
#Input() placeholder: string = '';
#Input() formControlName: string = ''
#Input() form: FormGroup = {} as FormGroup;