Use property as form validator in Angular2 (data-driven) - forms

I'm having a hard time trying to set a max value using the data driven approach in Angular2.
I want to set the max value of the input to a property called userLimit, which I get from firebase. This is my code:
component.ts
import { Component, OnInit, AfterViewInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from "#angular/forms";
import { FiredbService } from '../services/firedb.service';
import { AuthService } from '../services/auth.service';
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2/database';
#Component({
selector: 'my-dashboard',
styleUrls: ['./recibirpago.component.scss'],
templateUrl: './recibirpago.component.html'
})
export class RecibirpagoComponent implements OnInit, AfterViewInit {
myForm2: FormGroup;
uid: string;
userLimit: any;
constructor(private fb: FormBuilder,
private dbfr: FiredbService,
private db: AngularFireDatabase,
private authService: AuthService) {
this.myForm2 = this.fb.group({
email: ['', Validators.email],
clpmount: ['', [Validators.required, Validators.max(this.userLimit)]]
});
}
ngOnInit() {
this.uid = this.authService.getUserUid();
}
ngAfterViewInit() {
this.dbfr.getUserLimit(this.uid).subscribe(snapshot => {
this.userLimit = snapshot.val().accountLimit;
console.log(this.userLimit);
})
}
If I write, for example, Validators.max(5000) it works, but if I try to get the data from Firebase it doesn't work.
Thanks for your help!

The problem is that the constructor is executing before the ngAfterViewInit so you don't have the value of the userLimit at that point.
Instead use the setVAlidators method within the subscribe where you get the data.
Something like this:
constructor
this.myForm2 = this.fb.group({
email: ['', Validators.email],
clpmount: ['', Validators.required] // <-- not here
});
ngAfterViewInit
ngAfterViewInit() {
this.dbfr.getUserLimit(this.uid).subscribe(snapshot => {
this.userLimit = snapshot.val().accountLimit;
console.log(this.userLimit);
const clpControl = this.myForm2.get(`clpmount');
clpControl.setValidators(Validators.max(this.userLimit)); // <-- HERE
clpControl.updateValueAndValidity();
})
}
NOTE: Syntax was not checked.

Related

How to creat a dynamic text

I have a list of titles, and I would like these texts to alternate as I switch to ion-select. How do I do this?
I have no idea how to do this, not even what part of the code to show. I made an attempt here but whenever I use the console the value returned is undefined.
My typescrit code:
import { Titulo } from './../Services/services.service';
import { DataService } from './data.service';
import { Component, OnInit, ViewChild} from '#angular/core';
import {IonSlides} from '#ionic/angular';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-libertacao',
templateUrl: './libertacao.page.html',
styleUrls: ['./libertacao.page.scss'],
})
export class LibertacaoPage implements OnInit {
#ViewChild(IonSlides) slides: IonSlides;
PAGINA_SELECIONADA: number;
// tslint:disable-next-line: variable-name
index_atual: number;
titulo: Titulo;
constructor(private dataservice: DataService, private activatedRoute: ActivatedRoute ){
}
ngOnInit() {
const id = this.activatedRoute.snapshot.paramMap.get('id');
this.titulo = this.dataservice.getTituloById(parseInt(id, 10));
}
OnChange(event: any)
{
if (event.detail.value === this.PAGINA_SELECIONADA) {
this.slides.slideTo(this.PAGINA_SELECIONADA);
}
else {}
console.log(this.titulo);
}
slideChanged() {
this.slides.getActiveIndex().then((index) => {
this.index_atual = index;
});
}
}

passing parameter between pages ionic

Hi i´m new in ionic and I am trying to pass the scann information form one page to another, the thing its that when I execute the program I have a console.log to check if the info its passed correctly but on chrome console said undefined, letme paste my code:
home.ts where i try to send the info from the scan:
import { Component } from '#angular/core';
import { NavController,Platform } from 'ionic-angular';
import { BarcodeScanner } from '#ionic-native/barcode-scanner';
import { TabsPage } from '../tabs/tabs';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
private barcodeText:String;
private barcodeFormat:String;
private platform:Platform;
private navController:NavController;
constructor(private barcodeScanner: BarcodeScanner,public navCtrl: NavController,platform:Platform) {
this.platform = platform;
this.navController = navCtrl;
}
doScan(){
console.log('scannig product barcode');
this.platform.ready().then(() => {
this.barcodeScanner.scan().then((result) => {
if (!result.cancelled) {
this.barcodeText = result.text;
this.scanningDone(this.barcodeText)
}
}, (error) => {
console.log('error when scanning product barcode');
});
});
}
scanningDone(data){
this.navController.push(TabsPage,{
data:data
});
}
main.ts where the info suppose to go:
import { Component } from '#angular/core';
import { NavController, NavParams , ToastController} from 'ionic-angular';
import { BarcodeScanner } from '#ionic-native/barcode-scanner';
import { DetailsPage } from '../details/details';
import { Http } from '#angular/http'
#Component({
selector: 'main',
templateUrl: 'main.html'
})
export class MainPage {
information: any[];
item:any;
private bcData;
constructor(public navCtrl: NavController, private http: Http,public params:NavParams) {
this.bcData = params.get('data');
console.log(params.get('data'));
let localData = http.get(this.bcData).map(res => res.json().items);
localData.subscribe(data => {
this.information = data;
})
}
on the console.log(params.get('data')); its where I get the undefinied on the console.
you could have a method in your TabsPage that handles opening and closing pages like this:
openPages(Page, Data){
this.navCtrl.push(Page,Data);
}
Then in your scanningDone Method:
scanningDone(data){
this.tabsPage.openPages(MainPage,{
data:data
});
}
How about using localStorage
look at this as well

Angular 2 schema forms

I started to write Angular 2 application. I use there angular2 schema forms. It generates me bootstrap-like form well, however I'm not able to style it in any manner.
Component where I use schema forms body
import {Component, Input, OnInit} from '#angular/core';
import { HttpClient } from '../../shared/HttpClient.service';
#Component({
selector: 'stylists-section',
templateUrl: './stylists-section.component.html',
styleUrls: ['./stylists-section.component.scss'],
})
export class StylistsSectionComponent implements OnInit{
#Input() quantity: number;
#Input() col: string;
#Input() query: string;
#Input() title: string;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get('data/Users', {'isStylist': true})
.subscribe(response => {
this.stylists = response;
}, err => {
console.error('our error: ', err);
})
}
I tried to style it through this scss file. What I do wrong?

angular2-mdl table component with server side data

I experiment with Angular 2 - Material Design Lite especially with the table component but I can not figure out how would I pass data from server on ajax request. Here is the example provided for table initialisation.
How would I pass data from restAPI to table component?
Here I have a kind of working example. I placed the initial data on my Component Init method where I call the DataService which populates the table. I'm not sure if is the right workaround but at this point I have data in table.
import { Component, ViewChild, ViewContainerRef, OnInit, Pipe, PipeTransform } from '#angular/core';
import { MdDialog, MdDialogConfig, MdIcon } from "#angular/material";
import { AuthenticationService, DialogsService, DataService } from '../../../services/';
import { RouterModule, Routes, Router } from '#angular/router';
import {
IMdlTableModelItem,
MdlDefaultTableModel
} from 'angular2-mdl';
export interface ITableItem extends IMdlTableModelItem {
username: string;
email: string;
role: string;
unitPrice: number;
}
#Component({
selector: 'employees',
templateUrl: 'app/layouts/secure/employees/employees.html',
providers: [DialogsService, MdIcon]
})
export class EmployeesComponent implements OnInit {
public message: string;
public employees: any[];
public result: any;
public showSearchBar: false;
public tableData:[ITableItem];
public selected;
public tableModel = new MdlDefaultTableModel([
{key:'username', name:'Username', sortable:true},
{key:'email', name:'Email', sortable:true},
{key:'role', name:'Role', sortable:true},
{key:'status', name:'Status', sortable:true},
{key:'unitPrice', name:'Test', numeric:true}
]);
constructor(
private dialogsService: DialogsService,
public viewContainerRef: ViewContainerRef,
private _dataService : DataService,
private router: Router
) {
}
openDialog() {
this.dialogsService
.confirm('User Form', 'Are you sure you want to do this?', this.viewContainerRef)
.subscribe(res => this.result = res);
}
toggleSearch() {
console.log(this)
}
ngOnInit() {
var self = this;
this._dataService
.GetAll('employees')
.subscribe( data => {
data = Object.keys(data).map((key)=>{ return data[key]})
this.employees = data;
this.tableData = data;
this.tableModel.addAll(this.tableData);
}, error => console.log(error),
() => function ( data ) {
this.tableData = this.employees;
this.tableModel.addAll(this.tableData);
this.selected = this.tableData.filter( data => data.selected);
},
);
}
generateArray(obj){
return Object.keys(obj).map((key)=>{ return obj[key]});
}
selectionChanged($event){
this.selected = $event.value;
}
}
#fefe made it a little more difficult than it had to be, at least with the current version. The magic of the as keyword can do the heavy lifting.
For example my class setup looks like:
import...
export interface IUnreadMessage extends IMdlTableModelItem {
messageId: number;
subject: string;
from: string;
}
#Component ...
export class ...
private unreadMessagesTable = new MdlDefaultTableModel([
{key: 'messageId', name: 'Message ID'},
{key: 'subject', name: 'Subject'},
{key: 'from', name: 'From'}
]);
Then in my ajax call I have:
...ajax call here).subscribe(value => {
const messages = value as Array<IUnreadMessage>;
this.unreadMessagesTable.addAll(messages);
},
error => {
...error handler here...
});
Make sure your interface is EXACTLY (including case) the same as your returned ajax data and it should hook right up!

How to perform async validation using reactive/model-driven forms in Angular 2

I have an email input and I want to create a validator to check, through an API, if the entered email it's already in the database.
So, I have:
A validator directive
import { Directive, forwardRef } from '#angular/core';
import { Http } from '#angular/http';
import { NG_ASYNC_VALIDATORS, FormControl } from '#angular/forms';
export function validateExistentEmailFactory(http: Http) {
return (c: FormControl) => {
return new Promise((resolve, reject) => {
let observable: any = http.get('/api/check?email=' + c.value).map((response) => {
return response.json().account_exists;
});
observable.subscribe(exist => {
if (exist) {
resolve({ existentEmail: true });
} else {
resolve(null);
}
});
});
};
}
#Directive({
selector: '[validateExistentEmail][ngModel],[validateExistentEmail][formControl]',
providers: [
Http,
{ provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => ExistentEmailValidator), multi: true },
],
})
export class ExistentEmailValidator {
private validator: Function;
constructor(
private http: Http
) {
this.validator = validateExistentEmailFactory(http);
}
public validate(c: FormControl) {
return this.validator(c);
}
}
A component
import { Component } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { ExistentEmailValidator } from '../../directives/existent-email-validator';
#Component({
selector: 'user-account',
template: require<string>('./user-account.component.html'),
})
export class UserAccountComponent {
private registrationForm: FormGroup;
private registrationFormBuilder: FormBuilder;
private existentEmailValidator: ExistentEmailValidator;
constructor(
registrationFormBuilder: FormBuilder,
existentEmailValidator: ExistentEmailValidator
) {
this.registrationFormBuilder = registrationFormBuilder;
this.existentEmailValidator = existentEmailValidator;
this.initRegistrationForm();
}
private initRegistrationForm() {
this.registrationForm = this.registrationFormBuilder.group({
email: ['', [this.existentEmailValidator]],
});
}
}
And a template
<form novalidate [formGroup]="registrationForm">
<input type="text" [formControl]="registrationForm.controls.email" name="registration_email" />
</form>
A've made other validator this way (without the async part) and works well. I think te problem it's related with the promise. I'm pretty sure the code inside observable.subscribe it's running fine.
What am I missing?
I'm using angular v2.1
Pretty sure your problem is this line:
...
email: ['', [this.existentEmailValidator]],
...
You're passing your async validator to the synchronous validators array, I think the way it should be is this:
...
email: ['', [], [this.existentEmailValidator]],
...
It would probably be more obvious if you'd use the new FormGroup(...) syntax instead of FormBuilder.