Angular2 dynamic form with remote metadata - forms

I created a dynamic form following the instructions in the angular cookbook and then I've tried to create the form with metadata that I have in my database.
I made an HTTP request to the get field types, names, ids, etc. but when I try to build the form as in the angular example, nothing happens or I get errors on console.
Here's the code from the tutorial:
export class AppComponent {
questions: any[];
constructor(service: QuestionService) {
this.questions = service.getQuestions();
}
}
And this is what I did:
export class AppComponent implements OnInit {
campos: any[] = [];
constructor(private servico: FormDadosService) {}
ngOnInit() {
this.servico.getCampos().subscribe(this.processaCampos);
}
processaCampos(dados) {
for (let i = 0; i < dados.length; i++) {
this.campos.push(new CampoBase({
nome: dados[i].ZI2_NOME,
label: dados[i].ZI2_DESC,
ordem: dados[i].ZI2_ORDEM,
obrigatorio: dados[i].ZI2_OBRIGAT,
tamanho: dados[i].ZI2_TAM,
valor: '',
tipoCampo: dados[i].ZI2_TIPO
}))
}
}
}
I am getting this error:
error_handler.js:50EXCEPTION: Cannot read property 'push' of undefined
I think I need to know a way to render the form after all data about it has arrived from my HTTP request.

I made it work this way:
export class AppComponent implements OnInit {
campos: any[] = [];
constructor(private servico: FormDadosService) { }
ngOnInit() {
this.servico.getCampos().subscribe((data) => {
data.forEach(campo => {
this.campos.push(new CampoBase({
valor: '',
nome: campo.ZI2_CAMPO,
label: campo.ZI2_DESC,
tipoCampo: campo.ZI2_TIPO,
tamanho: campo.ZI2_TAM
}))
});
});
}
}
This question can be marked as solved.
Thanks everyone.

Related

Create a common loader by intercepting http request (HttpInterceptors)

I am creating a common loader in ionic 3 but there is a problem because of manually using loader.dismiss()
Instead of creating a loader using loaderCtrl on very http request in ionic I'm planning to make only one loader. I am using a httpInterceptor and when the request is intercepted i created and present the loader. And i check if the event is of type HttpRequest, if yes the loader is dismissed.
This works fine when only http request is made on any page i.e the request is made it is intercepted the loader is presented later when the response is obtained the loader is dismissed.
But now if there are 2 request made on 1 page i gate the error of removeView not Found.
/loaderInterceptor.ts
#Injectable()
export class HttpLoaderInterceptor implements HttpInterceptor {
headersConfig: any;
loader: any
constructor(public loadingCtrl: LoadingController) { }
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
this.loader = this.loadingCtrl.create({
content: "Please wait",
});
this.loader.present()
return next.handle(req).pipe(tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
this.loader.dismiss();
}
},
(err: any) => {
this.loader.dismiss();
}));
}
}
The dismiss method is called twice as 2 response are obtained and the 2nd time there is no loader to be dismissed so we get an error.
Please help.
In my think, the request success before loading bar reason, so I created one service use to solve it. My source code is below:
import { Injectable } from '#angular/core';
import { LoadingController } from '#ionic/angular';
#Injectable({
providedIn: 'root'
})
export class LoadingService {
private loaders = [];
private badLoaders = 0;
constructor(
private loadingController: LoadingController
) {
}
async startLoading() {
if (this.badLoaders > 0) {
this.badLoaders --;
} else {
await this.loadingController.create({
message: 'Loading ...',
}).then(loader => {
this.loaders.push(loader);
loader.present().then(() => {
if (this.badLoaders > 0) {
this.badLoaders --;
this.endLoading();
}
});
});
}
}
endLoading() {
let loader = this.loaders.pop();
if (loader) {
loader.dismiss();
} else {
this.badLoaders ++;
}
}
}
You can try it, use LoadingService.startLoading instead this.loadingCtrl.create and LoadingService.endLoading instead this.loader.dismiss();.

Ionic 4. Alternative to NavParams

I am using ionic 4. It does not accept to receive data using navparams.
Here is my sender page method:
//private route:Router
gotoFinalView(intent) {
this.route.navigateByUrl(`${intent}`, this.destination);
}
Receiver page line;
//private navParams:NavParams
this.destination = navParams.data;
What is the right approach to doing this in ionic 4. I am also uncertain whether gotoFinalView method is valid.
This is how I solved my problem:
I created a Service with a setter and getter methods as;
import { Injectable } from "#angular/core";
#Injectable({
providedIn: "root"
})
export class MasterDetailService {
private destn: any;
constructor() {}
public setDestn(destn) {
this.destn = destn;
}
getDestn() {
return this.destn;
}
}
Injected the Service and NavController in the first page and used it as;
gotoFinalView(destn) {
this.masterDetailService.setDestn(destn);
this.navCtrl.navigateForward("destn-page");
}
Extracted the data at the final page by;
constructor(
private masterDetailService: MasterDetailService
) {
this.destination = this.masterDetailService.getDestn();
}
This is the efficient way to solve your problem
user Angular Routers concepts in your application.
just declare your router like the following
Your app routing module like the following
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import {ViewComponent} from "./crud/view/view.component";
import {CreateComponent} from "./crud/create/create.component";
import {UpdateComponent} from "./crud/update/update.component";
import {ReadComponent} from "./crud/read/read.component";
const routes: Routes = [
{path: '', component: ViewComponent},
{path: 'create', component: CreateComponent},
{path: 'update/:id', component: UpdateComponent},
{path: 'view/:id', component: ReadComponent}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
:id is the parameter what i want to send to that page.
this.router.navigate([link + '/' + id]);
share your parameter like this in your first page.
In your second page inject the activated route using DI(Dependency Injection)
constructor(private actRoute: ActivatedRoute)
Then Get your parameters using the following code
this.productID = this.actRoute.snapshot.params['id'];
This is the simple way. You can send multiple parameter at a time.
{path: 'update/:id/:name/:price', component: UpdateComponent}
and get those parameters like the following
this.productID = this.actRoute.snapshot.params['id'];
this.productName = this.actRoute.snapshot.params['name'];
this.productPrice = this.actRoute.snapshot.params['price'];
While Routing you can write like this:
this.router.navigate(["/payment-details",{
prev_vehicle_type: this.vehicle_type,
prev_amt: this.amt,
prev_journey:this.whichj
}]);
To get this parameters on the next page you can write:
constructor(
public router: Router,
public activateroute: ActivatedRoute){
this.activateroute.params.subscribe((data: any) => {
console.log(data);
this.vehicle_type = data.prev_vehicle_type;
this.amt = data.prev_amt;
this.whichj = data.prev_journey;
});
}
ionic 4 navigation with params
sender page
1. import the following
import {NavController} from '#ionic/angular';
import { NavigationExtras } from '#angular/router';
constructor(private navCtrl:NavController)
sender page
gotonextPage()
gotonextPage()
{
let navigationExtras: NavigationExtras = {
state: {
user: 'name',
parms:Params
}
};
this.navCtrl.navigateForward('pageurl',navigationExtras);
}
4.Receiver Page
import { ActivatedRoute, Router } from '#angular/router';
constructor( private route: ActivatedRoute, private router: Router)
{
this.route.queryParams.subscribe(params => {
if (this.router.getCurrentNavigation().extras.state) {
this.navParams = this.router.getCurrentNavigation().extras.state.parms;
}});
}
Send data with Router service and extract with global variable, history
//sender component
// private router: Router
nextPage() {
this.router.navigate(['history'],
{ state: [{ name: "covid-19", origin: "china" },{ name: "ebola", origin: "us" }] }
)
}
//receiver page
ngOnInit() {
let data = history.state;
console.log("data-->",data);
// ** data**
//0:{name: "covid-19", origin: "china"} 1: {name: "ebola", origin: "us"} navigationId: 2
}
The item, icon and title variables you want to send should be written in the state in this way.
this.nav.navigateForward('/myUrl', {
state: {
'items': this.substances,
'icon': ICONS.substance,
'title': 'Etken Maddeler'
}
});
We take incoming variables this way.
//receive
constructor(
protected route: ActivatedRoute,
protected router: Router,
) {
this.selectedItem = null;
this.route.paramMap.subscribe(params => {
let st = this.router.getCurrentNavigation().extras.state;
if (st) {
this.selectedItem = st.items;
}
});
}
Very very simply, you can do something like this:
In the "calling screen" :
this.router.navigate(['url', {model: JSON.stringify(myCustomObject)}])
In the "called screen" :
this.myvar = JSON.parse(this.activatedRoute.snapshot.paramMap.get('model')
Et voilĂ  !
//in home.ts
import{ Router,ActivatedRoute, NavigationExtras } from '#angular/router';
getProductStatics(productObject : any) {
var object1 = {
id: productObject,
}
const navigationExtras: NavigationExtras = {state : {object:
JSON.stringify(object1)}};
this.router.navigate(["/product-statics"], navigationExtras);
}
//in product-statics.ts
import{ Router,ActivatedRoute,NavigationExtras } from '#angular/router';
if(self.router.getCurrentNavigation().extras.state) {
var object1
= this.router.getCurrentNavigation().extras.state.object;
var object = JSON.parse(object1);
var newObjectData = object.id;
}

Ionic Searchbar with PHP API

It works but i anot getting the results it should sort. I am getting the same results regardless what i type in the searchbar
I want it to sort like autocomplete. to show results of what i type in the search bar
search.ts
#Component({ selector: "page-search", templateUrl: "search.html" })
export class SearchPage {
filter: string = '';
public userDetails: any;
public resposeData: any;
public dataSet: any;
public userSet: any;
public mediaSet: any;
public noRecords: boolean;
userPostData = {
uid: "",
token: "",
username: "",
bio: ""
};
constructor(
public common: Common,
public navCtrl: NavController,
public app: App,
public menu: MenuController,
public authService: AuthService,
public http: Http,
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen
) {
this.initializeItems();
this.mostmediaList();
}
initializeItems() {
return this.userPostData;
}
getItems(ev: any) {
this.initializeItems();
let val = ev.target.value;
if (val && val.trim() != '') {
this.authService.postData(this.userPostData, "userGroupSearch").then(
result => {
this.resposeData = result;
if (this.resposeData.allArtistsData) {
this.userSet = this.resposeData.allArtistsData;
console.log(this.userSet);
} else {
console.log("No access");
}
},
);
}
}
Since your code is wrapped into
if (this.resposeData.items) {
//some code
}
we know for sure that this.resposeData is not an array, since it has an items member (otherwise your code inside the if would not be executed and hence you would not get an error as in the case we have).
Since you call the parameter items at
this.userSet = this.resposeData.filter((items) => {
//some code
};
it is safe to assume that you wanted to filter this.resposeData.items instead of this.resposeData. So, you will need to make sure it is an array at the if
if (this.resposeData.items && Array.isArray(this.resposeData.items)) {
//some code
}
and filter this.resposeData.items instead of this.resposeData:
this.userSet = this.resposeData.items.filter((items) => {
//some code
};

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 use node-simple-schema reactively?

Given that there is not much examples about this, I am following the docs as best as I can, but the validation is not reactive.
I declare a schema :
import { Tracker } from 'meteor/tracker';
import SimpleSchema from 'simpl-schema';
export const modelSchema = new SimpleSchema({
foo: {
type: String,
custom() {
setTimeout(() => {
this.addValidationErrors([{ name: 'foo', type: 'notUnique' }]);
}, 100); // simulate async
return false;
}
}
}, {
tracker: Tracker
});
then I use this schema in my component :
export default class InventoryItemForm extends TrackerReact(Component) {
constructor(props) {
super(props);
this.validation = modelSchema.newContext();
this.state = {
isValid: this.validation.isValid()
};
}
...
render() {
...
const errors = this.validation._validationErrors;
return (
...
)
}
}
So, whenever I try to validate foo, the asynchronous' custom function is called, and the proper addValidationErrors function is called, but the component is never re-rendered when this.validation.isValid() is supposed to be false.
What am I missing?
There are actually two errors in your code. Firstly this.addValidationErrors cannot be used asynchronously inside custom validation, as it does not refer to the correct validation context. Secondly, TrackerReact only registers reactive data sources (such as .isValid) inside the render function, so it's not sufficient to only access _validationErrors in it. Thus to get it working you need to use a named validation context, and call isValid in the render function (or some other function called by it) like this:
in the validation
custom() {
setTimeout(() => {
modelSchema.namedContext().addValidationErrors([
{ name: 'foo', type: 'notUnique' }
]);
}, 100);
}
the component
export default class InventoryItemForm extends TrackerReact(Component) {
constructor(props) {
super(props);
this.validation = modelSchema.namedContext();
}
render() {
let errors = [];
if (!this.validation.isValid()) {
errors = this.validation._validationErrors;
}
return (
...
)
}
}
See more about asynchronous validation here.