I've built a app in Angular 8, but I have encountered a strange issue where I cannot inject a service into one of my components. Basically i want to show all my login/register means under one page without routing by just managing fields. But this error is coming up again and again.
Uncaught Error: Can't resolve all parameters for AuthTestComponent: ([object Object], [object Object], ?).
My auth.service.ts file:-
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { Router } from '#angular/router'
#Injectable({
providedIn: 'root'
})
export class AuthService {
authUrl = "API"
constructor(private http: HttpClient, private router: Router) { }
registerUser(email: string, password: string) {
return this.http.post<any>(this.authUrl + "user-register", {
email: email,
password: password,
// c_password: password,
token: true
}).pipe(catchError(this.handleError))
}
LoginUser(email: string, password: string) {
return this.http.post<any>(this.authUrl + "user-login", {
email: email,
password: password,
access_token: true
}).pipe(catchError(this.handleError))
}
loggedIn() {
return !!localStorage.getItem('access_token')
}
getToken() {
return localStorage.getItem('access_token')
}
logoutUser() {
localStorage.removeItem('access_token');
this.router.navigate(['login'])
}
private handleError(errorRes: HttpErrorResponse) {
let errorMessage = "An Unkown error occured!";
if (!errorRes.error.error.errors || !errorRes.error.error.email[0]) {
return throwError(errorMessage)
}
switch (errorRes.error.error.email[0]) {
case 'The email has already been taken.':
errorMessage = 'The email already exists!'
break;
}
// switch (errorRes.error.error[0]) {
// case 'Password or Email not matched':
// errorMessage = 'Please Sign Up to continue!'
// case 'INVALID_PASSWORD':
// errorMessage = 'This password is not correct.'
// }
return throwError(errorMessage);
}
sendPasswordResetLink(email: string) {
return this.http.post<any>(this.authUrl + "updatepassword", {
email: email
}).pipe(catchError(this.handleError))
}
}
My component file:-
import { Component, Inject } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
// import { MatDialogRef, MAT_DIALOG_DATA } from '#angular/material/dialog';
import { $animations } from './login-animations';
import { $providers } from './login-providers';
import { $pages } from './login-pages';
import { from } from 'rxjs';
import { AuthService } from '../services/auth/auth.service';
import { Router } from '#angular/router';
export type loginAction = 'register' | 'signIn' | 'forgotPassword' | 'signOut';
#Component({
selector: 'app-auth-test',
templateUrl: './auth-test.component.html',
styleUrls: ['./auth-test.component.css'],
animations: $animations
})
export class AuthTestComponent {
readonly providers = $providers;
private pages = $pages;
private page: loginAction;
// private code: string;
readonly form: FormGroup;
// private password2: FormControl;
private email: FormControl;
private password: FormControl;
public error = null;
// private newPassword: FormControl;
isLoading = false;
public progress = false;
constructor(private auth: AuthService, private router: Router, private action: loginAction) {
// Form controls
// this.password2 = new FormControl(null, Validators.required);
this.email = new FormControl(null, [Validators.required, Validators.email]);
this.password = new FormControl(null, Validators.required);
// this.newPassword = new FormControl(null, Validators.required);
// Empty form group
this.form = new FormGroup({});
// Populates the form according to the page
this.switchPage(this.page = action);
}
get currentPage() { return this.pages[this.page || 'signIn']; }
private switchPage(page: loginAction) {
// Removes all the controls from the form group
Object.keys(this.form.controls).forEach(control => {
this.form.removeControl(control);
});
// Add the relevant controls to the form according to selected page
switch (this.page = page) {
case 'register':
this.form.addControl('email', this.email);
this.form.addControl('password', this.password);
// this.form.addControl('password2', this.password2);
break;
default:
// case 'signIn':
// this.form.addControl('email', this.email);
// this.form.addControl('password', this.password);
// break;
// case 'forgotPassword':
// this.form.addControl('email', this.email);
// break;
/*
case 'resetPassword':
this.form.addControl('newPassword', this.newPassword);
break;
*/
}
}
// private showError(error: string) {
// this.error = error;
// this.progress = false;
// setTimeout(() => this.error = null, 5000);
// }
public activate(action: loginAction) {
this.progress = true;
switch (action) {
default:
// case 'signIn':
// this.signIn(this.email.value, this.password.value);
// break;
case 'register':
this.registerNew(this.email.value, this.password.value);
break;
// case 'forgotPassword':
// this.forgotPassword(this.email.value);
// break;
/*
case 'resetPassword':
this.resetPassword( this.code, this.newPassword.value );
break;
*/
}
}
private registerNew(email: string, password: string) {
// Registering a new user with a email/password
this.auth.registerUser(email, password).subscribe(
res => {
console.log(res);
localStorage.setItem('token', res.token)
this.isLoading = false;
this.router.navigate(['login']);
},
errorMessage => {
console.log(errorMessage);
this.error = errorMessage;
this.isLoading = false;
}
);
}
}
HTML form file -:
<div [#vanish]="page">
<h1 class="mat-title">{{ currentPage.title }}</h1>
<p class="mat-small">{{ currentPage.message }}</p>
</div>
<form [formGroup]="form" fxLayout="column" fxLayoutAlign="space-around stretch" fxLayoutGap="10px"
(ngSubmit)="activate(page)" *ngIf="page !== 'promptEmail' && page !== 'verifyEmail' && page !== 'recoverEmail'">
<!-- ERROR MESSAGE -->
<mat-error *ngIf="error" #inflate>{{ error }}</mat-error>
<!-- NAME
<mat-form-field appearance="legacy" *ngIf="form.contains('confirm')" #inflate>
<mat-label>Full name</mat-label>
<input matInput formControlName="confirm">
<mat-error *ngIf="form.controls.confirm.errors?.required">
Please specify your name here
</mat-error>
</mat-form-field> -->
<!-- EMAIL -->
<mat-form-field appearance="legacy" *ngIf="form.contains('email')" #inflate>
<mat-label>Email</mat-label>
<input matInput formControlName="email">
<mat-error *ngIf="form.controls.email.errors?.required">
Please specify an email address
</mat-error>
<mat-error *ngIf="form.controls.email.errors?.email">
Ooops! it looks like this is not a valid email
</mat-error>
</mat-form-field>
<!-- PASSWORD -->
<mat-form-field appearance="legacy" *ngIf="form.contains('password')" #inflate>
<mat-label>Password</mat-label>
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password">
<mat-icon matSuffix (click)="hidePassword = !hidePassword">
{{ hidePassword ? 'visibility_off' : 'visibility'}}
</mat-icon>
<mat-error *ngIf="form.controls.password.errors?.required">
A password is required
</mat-error>
</mat-form-field>
<!-- confirm PASSWORD -->
<mat-form-field appearance="legacy" *ngIf="form.contains('password')" #inflate>
<mat-label>Password</mat-label>
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password2">
<mat-icon matSuffix (click)="hidePassword = !hidePassword">
{{ hidePassword ? 'visibility_off' : 'visibility'}}
</mat-icon>
<mat-error *ngIf="form.controls.password2.errors?.required">
A password is required
</mat-error>
<mat-hint class="mat-link" align="end" (click)="switchPage('forgotPassword')" *ngIf="page == 'signIn'"
#inflate>Forgot password?</mat-hint>
</mat-form-field>
<!-- NEW EMAIL -->
<!-- <mat-form-field appearance="legacy" *ngIf="form.contains('newEmail')" #inflate>
<mat-label>New email</mat-label>
<input matInput formControlName="newEmail">
<mat-error *ngIf="form.controls.newEmail.errors?.required">
A new email is required
</mat-error>
<mat-error *ngIf="form.controls.newEmail.errors?.email">
This email looks wrong
</mat-error>
</mat-form-field> -->
<!-- NEW PASSWORD -->
<!-- <mat-form-field appearance="legacy" *ngIf="form.contains('newPassword')" #inflate>
<mat-label>New password</mat-label>
<input matInput formControlName="newPassword">
<mat-error *ngIf="form.controls.newPassword.errors?.required">
A new password is required
</mat-error>
</mat-form-field> -->
<!-- ACTION BUTTON -->
<button mat-stroked-button color="primary" type="submit" [disabled]="!form.valid" class="btn">
{{ currentPage.caption }}
</button>
<mat-progress-bar *ngIf="progress" mode="indeterminate" #inflate></mat-progress-bar>
</form>
<p class="mat-small" *ngIf="page == 'signIn'">
Are you a new user? <span class="mat-link" (click)="switchPage('register')">Register</span>
</p>
<p class="mat-small" *ngIf="page === 'register' || page === 'forgotPassword'">
Already have login and password? <span class="mat-link" (click)="switchPage('signIn')">Sign-in</span>
</p>
<!-- SIGN-IN PROVIDERS -->
<div fxLayout="column" fxLayoutAlign="center center" *ngIf="page == 'signIn'" #inflate>
<p class="mat-small">or sign-in with:</p>
<div fxLayout="row wrap" fxLayoutAlign="center center" fxLayoutGap="10px">
<button mat-icon-button *ngFor="let p of providers" (click)="signInWith(p.name)">
<mat-icon class="providers" [fontSet]="p.icon.split(':')[0]" [fontIcon]="p.icon.split(':')[1]"
[ngStyle]="{ color: p.color }">
</mat-icon>
</button>
</div>
</div>
The error trace provides a clue about what's going on: ([object Object], [object Object], ?). In Angular, it's objects all the way down (even a module is just some syntatical sugar around an object).
Since the first items in the error messages are objects, we can assume that dependency injection was successful for first two services referenced in the constructor. The ? in the error message (the third position) indicates there may be an issue with injecting the final dependency, i.e., private action: loginAction.
The Angular guide on dependency injection (DI) provides some context for how it operates by calling out that "dependencies are services or objects". The error you're seeing might be caused by trying to inject a non-injectable entity, i.e., a string.
It might be helpful to see how the component is implemented in your code, e.g., the corresponding HTML. Aside from the InjectionToken example provided in one of the other responses, you might also investigate whether an Input property (see Angular Input on the component allows you to pass the necessary page action).
Seems like the problem is here:
private action: loginAction
loginAction is a type and can not be injected.
For example, you can use InjectionToken
const LOGIN_ACTION = new InjectionToken<loginAction>('Login action token');
...
#Inject(LOGIN_ACTION) private action: loginAction
Related
I'm building a simple html form using "react-hook-form" library: https://react-hook-form.com/
I've incorporated "react-bootstrap-typeahead" into the html form but haven't been able to register this component with 'useForm' hook. Hence, "react-bootstrap-typeahead" input data is ignored during onSubmit.
"react-bootstrap-typeahead" doesn't provide a "name" prop which makes it difficult to register the component.
I've read the 'useForm' documentation on the different options for registering this type of components but still don't understand how to achieve this: https://react-hook-form.com/get-started#Registerfields
Does anybody have faced such challenge before?
It would be great to see a working example to get a better idea on how to implement "react-bootstrap-typeahead" + "react-hook-form" in my application. Thanks!
Here's my sample code:
import useForm from 'react-hook-form';
import { Typeahead } from 'react-bootstrap-typeahead';
import 'react-bootstrap-typeahead/css/Typeahead.css';
const myForm = (props) => {
const { register, handleSubmit, errors } = useForm();
const onSubmit = data => {
// api post request with form data
})
};
const mydata = [ "one", "two", "three" ];
return (
<>
<form onSubmit={handleSubmit(onSubmit)} >
<div className="form-group">
{/* Here I'm registering text input using ref: */}
<input type="text" className="form-control" name="name" ref={register({ required: true })} />
</div>
<div className="form-group mb-0">
{/* How can I register the below component with useForm? */}
<Typeahead
id="multiple-typeahead"
clearButton
multiple
options={mydata}
/>
</div>
<button type="submit">Save</button>
</form>
</>
);
}
This is how i was able to register the component:
import useForm from 'react-hook-form';
import { useForm, Controller } from "react-hook-form";
import 'react-bootstrap-typeahead/css/Typeahead.css';
const myForm = (props) => {
const { register, handleSubmit, errors, control } = useForm();
const onSubmit = data => {
// api post request with form data
})
};
const mydata = [ "one", "two", "three" ];
return (
<>
<form onSubmit={handleSubmit(onSubmit)} >
<div className="form-group">
<input type="text" className="form-control" name="name" ref={register({ required: true })} />
</div>
<div className="form-group mb-0">
<Controller
as={Typeahead}
control={control}
name="typeahead_component"
rules={{ required: true }}
id="multiple-typeahead"
clearButton
multiple
options={mydata}
defaultValue=""
/>
</div>
<button type="submit">Save</button>
</form>
</>
);
}
I am using react final form for validation purpose for login page which has forgot password and register link as well, now when I am clicking forgot password or register link ,it should not trigger any validation even though I am not filling my user name and password .I have tried t keep forgot password and register link away from tag but it is still triggering the validation on click of forgot password and register link .It should only trigger the validation when I m hitting submit button.
It should not ask to validate the form when I am clicking on any hyper link on the page as hyperlinks does not have any validations.
Here is the code sample
loginPage = () => {
const {t: translate} = this.props;
const {
match: {
params: {
authUrlKey = ''
} = {},
} = {},
} = this.props;
return (
<Form
onSubmit={ (values)=> this.validateUserCredentials(values)}
render={({ handleSubmit}) => (
<form onSubmit={handleSubmit}>
<button className="hidden" type="submit"/>
<h1 className="hw-block--pb">{translate('login.heading')}</h1>
<p className="hw-text-lead hw-block--pb-small">{translate('login.text')}</p>
{ this.state.description !=='' && <p className="hw-text-lead hw-block--pb-small">{this.state.description}</p> }
<div className="hw-grid">
<div className="hw-grid__item hw-one-whole hw-medium--one-fifth hw-large--one-sixth">
<label className="hw-label">{translate('login.landcode')}
<Field name="landcode" component={Dropdown} options={getCountryList()} onOptionSelect={this.onCountrySelect}/>
</label>
</div>
<div className="hw-grid__item hw-one-whole hw-medium--four-fifths hw-large--five-sixths">
<label className="hw-label">{translate('login.mobileNumber')}
<Field type="text" component={InputType}
validate={composeValidators(mobileNumberRequired, validMobileNumberWithISDCode)}
placeholder={translate('login.mobileNumberPlaceHolder')} name="phoneNumber"/>
</label>
</div>
</div>
<label className="hw-label">{translate('login.password')}
<Field type="password" component={InputType} validate={passwordRequired} placeholder={translate('login.passwordPlaceHolder')} name="password"/>
</label>
<Link className="hw-link" to={{ pathname: '/password/reset', state: {authUrlKey} }}>{translate('login.forgotPassword')}</Link>
<ErrorInfo error={this.state.error} errorMessage={this.state.errorMessage} translate={translate}/>
<div className="hw-block hw-block--mt-small">
<div className="hw-grid">
<div className="hw-grid__item hw-small--one-whole hw-medium--one-quarter hw-block--mb-smaller">
<button className="hw-button hw-button--primary hw-button--full" type="submit">{translate('login.loginButton')}</button>
</div>
<div className="hw-grid__item hw-one-whole hw-medium--three-quarters hw-block--mt-smaller">
<Link className="hw-link"
to={{ pathname: '/register', state: {authUrlKey} }}>{translate('login.registerButton')}</Link>
</div>
</div>
</div>
</form>)}
/>
)}
validations function used in code
export const validMobileNumberWithISDCode = (fieldValue='') => {
const value = trimValue(fieldValue);
const regex1 = /^\+?((45)|(46)|(47))?( )?\d{8,10}$/
return (regex1.test(value))? undefined : message[root.lang].validMobileNumber;
}
export const validMobileNumber = (fieldValue='') => {
const value = trimValue(fieldValue);
const regex1 = /^\d{8,10}$/;
return (regex1.test(value))? undefined : message[root.lang].validMobileNumber;
}
export const mobileNumberRequired = (fieldValue='') => {
const value = trimValue(fieldValue);
return value ? undefined : message[root.lang].mobileNumberRequired;
}
export const passwordRequired = (fieldValue='') => {
const value = trimValue(fieldValue);
return value ? undefined: message[root.lang].passwordRequired;
}
export const required =(fieldValue)=> {
const value = trimValue(fieldValue);
return value ? undefined : message[root.lang].required;
}```
validateUserCredentials -> This function does not contains any validation.It is used to retrieve form values and send it to server
React Final Form calls your validation function on every value change in the form, to ensure that the form validity is always up to date. Since you did not include the code for your validation function, I cannot ascertain what you are attempting to do. Your validation function should be very cheap to run (e.g. required fields, value length, etc.). The actual authentication should happen on submit.
I can't send data to backend I don't know why, the problem in the console looks like that exactly at the login and register component this same issue is appearing. Maybe the ngsubmit is formated wrong but I don't know how to post the (ngSubmit)="onSubmit(f)" by the patter that it needs to represent.
This is the error in the console and the components which I'm using.
TypeError: Cannot set property 'router' of undefined
at AuthInceptor (auth.interceptor.ts:11)
at eval (module.ngfactory.js? [sm]:1)
at _callFactory (core.js:21283)
at _createProviderInstance (core.js:21237)
at resolveNgModuleDep (core.js:21198)
at NgModuleRef_.push../node_modules/#angular/core/fesm5/core.js.NgModuleRef_.get
auth.service.ts
constructor(private router: Router, private httpClient: HttpClient) {}
LogIn(email: string, password: string) {
const reqHeader = new HttpHeaders({'Content-Type': 'application/json', 'No-Auth': 'True'});
return this.httpClient.post<any>(this.apiAddress + 'account/login', {
Email: email,
Password: password
}, {headers: reqHeader})
.catch((e: any) => Observable.throw(this.errorHandler(e)));
}
Register(name: string, email: string, password: string, role: string) {
const reqHeader = new HttpHeaders({'Content-Type': 'application/json', 'No-Auth': 'True'});
return this.httpClient.post<any>(this.apiAddress + 'account/register', {
Name: name,
Email: email,
Password: password,
Role: role
}, {headers: reqHeader})
.catch((e: any) => Observable.throw(this.errorHandler(e)));
}
errorHandler(error: any): void {
console.log(error);
}
auth.inceptor.ts
constructor(private router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('No-Auth') === 'True') {
return next.handle(req.clone());
}
if (localStorage.getItem('userToken') != null) {
const clonedreq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('userToken'))
});
return next.handle(clonedreq)
.do(
succ => { },
err => {
if (err.status === 401) {
this.router.navigate(['/login']);
}
}
);
} else {
this.router.navigate(['/login']);
}
}
log-in form
<form class="example-form" (ngSubmit)="onSubmit(email.value, password.value)" #f="ngForm">
<mat-card-content>
<table class="example-full-width" cellspacing="0">
<tr>
<td>
<mat-form-field class="example-full-width">
<input matInput placeholder="Email" #email ngModel name="email" required>
</mat-form-field>
</td>
</tr>
<tr>
<td><mat-form-field class="example-full-width">
<input matInput placeholder="Password" #password ngModel type="password" name="password" required>
</mat-form-field></td>
</tr></table>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button type="submit" color="primary">Login</button>
<div *ngIf="isLoginError" class="red-text center error-message">Error Incorrect email or password</div>
</mat-card-actions>
</form>
Submit function
onSubmit(email, password): void {
this.authService.LogIn(email, password)
.subscribe((data: any) => {
localStorage.setItem('userToken', data.token);
localStorage.setItem('role', data.role);
this.router.navigate(['../Home']);
console.log(data);
},
(err: HttpErrorResponse) => {
this.isLoginError = true;
});
}
I am using Angular2 and MVC. I am following along with a lot of the tutorial on angular.io but changing some things to implement my own solution. Basically I have a table that shows clients and their identifier number. Using mock data, I was able to retrieve the clients and add new ones. The new ones would update my clientList and show the new client in my display table.
Once I started converting this to hit the server to get and post the data, I started to have issues.
I am able to retrieve the data perfectly fine from the server and it gives me the output I expect. When I try to post a new one to the server, it goes into my post method perfectly fine. It pushes the value into my clientList, but doesnt update my display table.
I'd like to add that I am not actually adding the data to my list on the server side, I am simply just returning a new object to try to get it to update my view.
Here's my service:
import {Injectable} from 'angular2/core';
import {Client} from './client';
import {RequestOptions, Http, Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class ClientService {
constructor(private http: Http) { }
getClients(): Observable<Client[]> {
return this.http.get('/Client/GetClients')
.map(this.extractData);
}
addClient(client: Client): Observable<Client> {
let clientUrl = '/Client/AddClient';
let body = JSON.stringify({ client });
let header = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: header });
return this.http.post(clientUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if (res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || {};
}
private handleError(error: any) {
// In a real world app, we might send the error to remote logging infrastructure
let errMsg = error.message || 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
My app component:
#Component({
selector: 'my-app',
templateUrl: 'Scripts/typescript/app.component.html',
directives: [ClientFormComponent, MODAL_DIRECTIVES],
providers: [ClientService, HTTP_PROVIDERS]
})
#Injectable()
export class AppComponent implements OnInit {
constructor(private _clientService: ClientService) { }
currentClient: Client;
#ViewChild('modal')
modal: ModalComponent;
public clients: Client[];
public errorMessage: string;
ngOnInit() {
this.getClients();
}
getClients() {
this._clientService.getClients().subscribe(clients => this.clients = clients, error => this.errorMessage = <any>error);
}
deleteClient() {
var index = this.clients.indexOf(this.currentClient, 0);
if (index > -1) {
this.clients.splice(index, 1)
}
}
close() {
this.modal.close();
}
open(client: Client) {
this.currentClient = client;
this.modal.open();
And my component with the call to the service when I click an add button:
#Component({
selector: 'client-form',
templateUrl: 'Scripts/typescript/client-form.component.html'
})
export class ClientFormComponent
{
constructor(private _clientService: ClientService) { }
#Input() clientList;
model = <Client>{ name: 'Bob', npi: 12345678 };
submitted = false;
active = true;
errorMessage: string;
onSubmit() { this.submitted = true; }
ngOnInit() {
this.getClients();
}
getClients() {
this._clientService.getClients()
.subscribe(
clients => this.clientList = clients,
error => this.errorMessage = <any>error);
}
addClient() {
this._clientService.addClient(this.model)
.subscribe(
client => this.clientList.push(client),
error => this.errorMessage = <any>error);
}
}
Here is my template for displaying the table, which is the app.component.html:
<h1>Client/NPI Cross Reference</h1>
<client-form></client-form>
<table class="table">
<tr style="font-weight: bold;">
<td>Client</td>
<td>NPI</td>
<td>Date Added</td>
<td></td>
</tr>
<tr *ngFor="#client of clients;">
<td>{{client.name}}</td>
<td>{{client.npi}}</td>
<td>{{client.dateAdded}}</td>
<td>
<span style="color: red; font-size: 16px; cursor: pointer;" class="glyphicon glyphicon-trash" aria-hidden="true" (click)="open(client)"></span>
</td>
</tr>
</table>
<modal #modal [animation]="animationsEnabled" (onClose)="deleteClient()" (onDismiss)="dismissed()">
<modal-header [show-close]="true">
<h4 class="modal-title">Delete</h4>
</modal-header>
<modal-body>
Are you sure you want to delete this entry?
</modal-body>
<modal-footer>
<button type="button" class="btn btn-primary" (click)="modal.close()">Yes</button>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">No</button>
</modal-footer>
</modal>
Here is my form template html:
<div class="container">
<form *ngIf="active" (ngSubmit)="onSubmit()" #clientForm="ngForm">
<div class="form-group" style="float: left;">
<label for="clientid">Client Id:</label>
<input type="text" class="form-control" required [(ngModel)]="model.name" ngControl="name" #name="ngForm" #newClient>
<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
Name is required
</div>
</div>
<div class="form-group" style="float: left; padding-left: 10px;">
<label for="npi">NPI:</label>
<input type="number" class="form-control" required [(ngModel)]="model.npi" ngControl="npi" #newNpi>
</div>
<div style="float: left; margin: 25px 0 0 10px;">
<button type="submit" class="btn btn-default" (click)="addClient()" [disabled]="!clientForm.form.valid">Add</button>
</div>
</form>
</div>
<br style="clear:both;" />
On the server side, I am simply just returning a json client:
[HttpPost]
public JsonResult AddClient()
{
var client = new Models.Client();
client.name = "test";
client.npi = 12345;
client.dateAdded = DateTime.Now.ToShortDateString();
return Json(client);
}
When using the mock data, it would automatically update my table and I'd be able to see my new client in the table. But now that I am using the server, it adds it to the clientList, but it doesnt actually change the view of my table.
How can I get it to change my table and update it to show it's been added?
Maybe the point is that you are calling twice the method getClients() of your ClientService, once from the the AppComponent and once from the ClientFormComponent.
This means that you will get 2 different arrays, one held in the AppComponent and one held in the ClientFormComponent.
If what I say is correct, then when you add a new client via the POST command, then the ClientFormComponent updates its array but this has no effect on the AppComponent array (which is a different object).
The solution in this case is probably:
1) make the AppComponent pass its clients array to the ClientFormComponent such as <client-form [clientList]="clients"></client-form> (which is probably the reason why clientList is preceded by #Input())
2) Avoid like hell resetting the clientList from within the ClientFormComponent (which is currently happening in the line this.clientList = clients)
I hope I am not missing something and that this can help.
PS: I guess you do not need #Injectable() in the AppComponent, since it is a Component
Here is what I think is happening. When you call getClients() inside ClientFormComponent, it replaces the clientsList with a new array. But, the one in the view is the array assigned by getClients() inside AppComponent. One way to make sure is to comment the line inside ClientFormComponent:
ngOnInit() {
//this.getClients();
}
Change the line:
public clients: Client[];
to
public clients: Client[] = [];
I'm a new developer for groovy grails and I have a table named user under the database test.
I succeed to login to that database by using grails but I couldn't succeed to register the new user.
Here is my register.gsp
<g:form controller="user" action="registration">
<fieldset class="form">
<div class="fieldcontation ${hasErrors(bean: userInstance, field: 'fullName', 'error')}">
<label for="fullName">
<g:message code="endUser.fullName.label" default="Full Name" />
</label>
<g:textField name="fullName" value="${userInstance?.fullName}"/>
</div>
<div class="fieldcontation ${hasErrors(bean: userInstance, field: 'userName', 'error')}">
<label for="userName">
<g:message code="endUser.userName.label" default="User Name" />
</label>
<g:textField name="userName" value="${userInstance?.userName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: userInstance, field: 'password', 'error')} ">
<label for="password">
<g:message code="endUser.password.label" default="Password"/>
</label>
<g:field type="password" name="password" value="${userInstance?.password}"/>
</div>
</fieldset>
<fieldset class="buttons">
<g:submitButton name="register" class="save" value="Register" />
</fieldset>
</g:form>
here is my UserController.groovy
package test
import java.sql.SQLClientInfoException;
import javax.activation.DataSource;
import grails.converters.JSON
class UserController {
def index() {
redirect(action:"login")
}
def register = {}
def login = {}
def registration = {
def b = new User(fullName:params.fullName, userName:params.userName, password:params.password)
b.save()
render (b as JSON)
}
def authenticate = {
def user = User.findByUserNameAndPassword(params.userName, params.password)
if (user){
def userMap = [:]
userMap.put("login", "true")
userMap.put("name", user.fullName)
userMap.put("password", user.password)
render (userMap as JSON)
}else{
flash.message = "Sorry, ${params.userName}, Please try again."
redirect(action:"login")
}
}
def logout = {
flash.message = "Goodbye ${session.user.fullName}"
session.user = null
redirect(action:"login")
}
}
After this method , I had this error
http://postimg.org/image/gbitzsokp/
But I couldn't understand what it tried to say
here is my DataSource.groovy
dataSource {
pooled = true
jmxExport = true
driverClassName = "com.mysql.jdbc.Driver"
username = "admin"
password = "admin"
}
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = false
cache.provider_class='net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
development {
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
url = "jdbc:mysql://localhost:3306/test"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/test"
}
}
production {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/test"
}
}
}
My domain class User.groovy
package test
class User {
String userName
String password
String fullName
String toString(){
"${fullName}"
}
static constraints = {
fullName()
userName(unique: true)
//password(password=true)
}
}
Plug-in's
plugins {
build ":tomcat:7.0.55"
compile ":scaffolding:2.1.2"
compile ':cache:1.1.8'
compile ":asset-pipeline:1.9.9"
compile ":simpledb:0.5"
runtime ":hibernate4:4.3.6.1" // or ":hibernate:3.6.10.18"
runtime ":database-migration:1.4.0"
runtime ":jquery:1.11.1"
}
I can immediately assure every information you need
Thank you
My Grails version is 2.4.4
As #saw303 mentioned, GORM provides what you're looking for. So there's no need to write SQL statements.
Workflow
I'm assuming the workflow you need is something like this:
The register action renders register.gsp, the registration form.
When the registration form is submitted the registration action handles the request. If all goes well, the user is set in the session scope and the browser is redirected somewhere, such as the home page. Otherwise, the browser is redirected to the register action and the validation errors are displayed in the registration form.
Here's how to create such a workflow.
Registration action
Begin by making a number of changes to the registration action.
import grails.transaction.Transactional
class UserController {
// NOTE: Making the method Transactional avoids having to flush saves.
#Transactional
def registration() {
def user = new User(params).save()
if(user.hasErrors()) {
/*
* On failure, redirect back to registration form,
* and pass the User instance to the GSP page in the
* flash scope so that validation errors can be rendered.
*/
flash.userInstance = user
redirect action: 'register'
} else {
/* On success, place User instance in session scope,
* and redirect to home page.
*/
session.user = user
redirect uri: '/'
}
}
}
I added the Transactional annotation so the flushing of GORM saves are handled automatically.
registration is now a method instead of a Closure. It's the new way.
register.gsp
To render the validation errors in the registration form, change the hasErrors() calls from bean: userInstance to model: flash?.userInstance For example, for the fullName property, do ${hasErrors(model: flash?.userInstance, field: 'fullName', 'error')}