Sending multiple records to Web API through Ionic - ionic-framework

We are implementing a Investment Declaration module in mobile app. The Ionic developer has developed the investment declaration page which even has an option of multiple document adding. But now how will he send me(to API) the multiple details.
I have added the image of investment declaration form page for better understanding.
Investment Declaration Form image
This image is of our web application project. In web Application we are doing something like this:
Protected Sub btnApplyInvestment_Click(sender As Object, e As System.EventArgs) Handles btnApplyInvestment.Click
Try
Dim mEmpInvestmentDeclare As EmpInvestmentDeclare = ViewState("EmpInvestmentDeclare")
With mEmpInvestmentDeclare
.PayCareYearId = ViewState("yearId")
End With
Dim mEmpInvestmentDeclareDetails As EmpInvestmentDeclareDetails = mEmpInvestmentDeclare.EmpInvestmentDeclareDetails
Dim mEmpInvestmentDeclareDetail As EmpInvestmentDeclareDetail = Nothing
For Each rowHead As GridViewRow In gridDeclaredHead.Rows
Dim gridInvestment As GridView = Nothing
With rowHead
gridInvestment = .FindControl("gridInvestment")
If Not gridInvestment Is Nothing Then
Dim fieldIdHidden As HiddenField = Nothing
Dim txtAmount As TextBox = Nothing
For Each row As GridViewRow In gridInvestment.Rows
With row
fieldIdHidden = .FindControl("fieldIdHidden")
txtAmount = .FindControl("txtAmount")
If fieldIdHidden.Value = 26 Then
Dim m = 12
End If
If Not fieldIdHidden Is Nothing AndAlso Not txtAmount.Text.Length = 0 Then
'If CInt(txtAmount.Text) > 0 Then
mEmpInvestmentDeclareDetail = mEmpInvestmentDeclareDetails.GetEmpInvestmentDeclareDetailById(fieldIdHidden.Value)
With mEmpInvestmentDeclareDetail
.DeclaredAmount = Val(txtAmount.Text)
.PayCareYearId = ViewState("yearId")
.ApplyEdit()
End With
'End If
End If
End With
Next
End If
End With
Next
Dim temp As EmpInvestmentDeclare = mEmpInvestmentDeclare.Clone
mEmpInvestmentDeclare = mEmpInvestmentDeclare.Save()
Please can someone suggest something similar to this. Here we can simply loop through our grid and make a collection of the details which we later on save it.
But in Ionic we don't have a grid nor I can loop it. I am just stuck with this module.

I think in your case, you should use FormGroup.
import { Component } from '#angular/core';
import {Validators, FormBuilder, FormGroup } from '#angular/forms';
#Component({
template: `
<form [formGroup]="todo" (ngSubmit)="logForm()">
<ion-item>
<ion-label>Todo</ion-label>
<ion-input type="text" formControlName="title"></ion-input>
</ion-item>
<ion-item>
<ion-label>Description</ion-label>
<ion-textarea formControlName="description"></ion-textarea>
</ion-item>
<button ion-button type="submit" [disabled]="!todo.valid">Submit</button>
</form>
`
})
export class FormsPage {
private todo : FormGroup;
constructor( private formBuilder: FormBuilder ) {
this.todo = this.formBuilder.group({
title: ['', Validators.required],
description: [''],
});
}
logForm(){
console.log(this.todo.value)
}
}

Related

CDK drag-n-drop auto scroll

I have an Angular 7 app, using CDK Drag-n-Drop to drag and drop rows in a very long list.
What should I do to allow the long list to auto scroll when the dragged item out of the current view?
Any sample code I can refer to?
I have faced the same issue, It happens anytime an outside element is scrollable. This is the open issue - https://github.com/angular/components/issues/16677. - I have slightly modified the solution mentioned in this link.
import { Directive, Input, ElementRef, AfterViewInit } from '#angular/core';
import { CdkDrag } from '#angular/cdk/drag-drop';
#Directive({
selector: '[cdkDrag],[actualContainer]',
})
export class CdkDropListActualContainerDirective {
#Input('actualContainer') actualContainer: string;
originalElement: ElementRef<HTMLElement>;
constructor(cdkDrag: CdkDrag) {
cdkDrag._dragRef.beforeStarted.subscribe( () => {
var cdkDropList = cdkDrag.dropContainer;
if (!this.originalElement) {
this.originalElement = cdkDropList.element;
}
if ( this.actualContainer ) {
const element = this.originalElement.nativeElement.closest(this.actualContainer) as HTMLElement;
cdkDropList._dropListRef.element = element;
cdkDropList.element = new ElementRef<HTMLElement>(element);
} else {
cdkDropList._dropListRef.element = cdkDropList.element.nativeElement;
cdkDropList.element = this.originalElement;
}
});
}
}
Template
<div mat-dialog-content class="column-list">
<div class="column-selector__list">
<div cdkDropList (cdkDropListDropped)="drop($event)">
<div
*ngFor="let column of data"
cdkDrag
actualContainer="div.column-list"
>
</div>
</div>
</div>
</div>
As mentioned here you just need to add cdkScrollable to your list container.

Ionic 2/3 Pushing items in Array based on ID

I'm looking to push items from one page to another, based on the category ID. Then I went to push all the contents from the array(who have the category ID that matches) to the next page.
I'm caught between using an if statement to pass the parameters to the second page, or to call the array in the second page and use an NgIf. Below is my code for a better visual. I'm fairly new to Ionic. Thank you for the help!
First Page
if(id = 1) {
category_id: this.referenceList.category_id = 1;
this.navCtrl.push(ReferencePage,{category_id:"category_id"});
}
else {
console.log("nope")
}
Second Page
<div *ngfor = let reference of referenceList></div>
<div *ngif = category.id === 1> {{reference.referenceField1}} {{reference.referenceField2}} {{reference.media}} </div>
Not sure which one to use and why. Thanks again! Happy to clarify if there's any confusion.
You stored the category_id in a variable, so use this:
this.navCtrl.push(ReferencePage,{category_id:category_id});
In the second page controller you need:
import ( NavParams ) from 'ionic-angular';
category_id: number;
constructor(public navParams: NavParams) {
...
}
ionViewDidLoad() {
this.category_id = this.navParams.get('category_id');
}
Also, your html should be (if I understand what you are doing):
<div *ngFor = let reference of referenceList>
<div *ngIf = category_id === 1>
{{reference.referenceField1}};
{{reference.referenceField2}};
{{reference.media}};
</div>
</div>

Angular 2 / PrimeNG - Expression has changed after it was checked. Binding NgModel on last invalid form control

I'm having a problem where when the very last element in my form has a value bound to it the error "Expression has changed after it was checked." is thrown.
I will preface by saying this is based off of the Angular 2 website example here -
https://angular.io/docs/ts/latest/cookbook/dynamic-form.html#!#top
The way my app works is first I build a dynamic form with controls in my form component based off a model.
My form components html loops the questions in the model like so
<form *ngIf="showForm" [formGroup]="formGroup">
<!-- questions-->
<div *ngIf="questions.length > 0">
<div *ngFor="let question of questions">
<question [question]="question" [formGroup]="formGroup"></question>
</div>
</div>
<button pButton type="submit" label="Submit" icon="fa-check-circle-o" iconPos="left"
[disabled]="!formGroup.valid" (click)="submitFinalForm()"></button>
</form>
Below is the question component html that uses the data that was passed in from the form component to display certain types of questions via ngSwitch
<label [attr.for]="question.field">
{{ question.question }}
</label>
<div [ngSwitch]="question.type">
<!-- Radio / Checkbox -->
<radio-checkbox-question *ngSwitchCase="1" [formGroup]="formGroup" [question]="question"></radio-checkbox-question>
</div>
Finally here is the radio-checkbox-question component
<div *ngIf="showQuestion" [formGroup]="formGroup">
<!-- Radio -->
<div *ngIf="model.radiocheckbox == 'radio'">
<div *ngFor="let label of model.labels; let i = index;">
<p-radioButton name="{{model.field}}"
value="{{i}}"
label="{{label}}"
formControlName="{{model.field}}"
[(ngModel)]="questionAnswerRadio"></p-radioButton>
</div>
</div>
</div>
Here is the actual component TS
import { Component, Input, OnInit } from "#angular/core";
import { FormGroup } from "#angular/forms";
import { RadioCheckboxQuestion } from "../Questions/radio.checkbox.question.model";
#Component({
selector: "radio-checkbox-question",
templateUrl: "radio.checkbox.component.html"
})
export class RadioCheckboxComponent implements OnInit {
#Input() question: any;
#Input() formGroup: FormGroup;
model: RadioCheckboxQuestion = new RadioCheckboxQuestion();
showQuestion: boolean = false;
questionAnswerRadio: string = "";
ngOnInit(): void {
// question essential properties
if (this.question.hasOwnProperty("field") && this.question["field"] &&
this.question.hasOwnProperty("labels") && this.question["labels"]) {
this.model.field = this.question["field"];
this.model.labels = this.question["labels"];
// assume always radio for debugging
this.model.radiocheckbox = "radio";
// set existing answer
if (this.question.hasOwnProperty("QuestionAnswer") && this.question["QuestionAnswer"]) {
if (this.model.radiocheckbox == "radio") {
this.questionAnswerRadio = this.question["QuestionAnswer"];
}
}
this.showQuestion = true;
}
}
}
I've also seen many SO issues like the following
Angular 2 dynamic forms example with ngmodel results in "expression has changed after it was checked" which basically state that [(ngModel)] should not be used with dynamic forms, but the primeNG documentation says the components can work with model driven forms and the only way to set the answer (that I know of) is [(ngModel)]. I believe what might happen here is since I set the only question in the formGroup to a value that the formGroup becomes valid in between the change detection and causes the error
Error in ./FormComponent class FormComponent - inline template:17:48 caused by: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.
From your template it looks like you are using both model drive (formControlName)
and template driven (ngModel).
<p-radioButton name="{{model.field}}"
value="{{i}}"
label="{{label}}"
formControlName="{{model.field}}"
[(ngModel)]="questionAnswerRadio"></p-
<radioButton>
Please select one way and try again.
I suggest you to remove the [(ngModel)]
The only way i've found to get the change detection to be happy with my multi-nested components and primeNG was to implement full change detection manually. What that basically means was in every component I had to add something like the following
import ChangeDetectorRef
constructor(private change: ChangeDetectorRef)
{}
ngOnInit() {
// code here that inits everything
this.change.markForCheck();
}
Anything less then this caused the change detection errors to pop-up in different and unique ways in the components that used primeNG.

Angular2 default 'selected' select option not being set

I am having an issue getting select boxes on my Angular2 application to show a default option '--please select--' on page load. I have managed to get this working before but I cannot seem to get this working in this particular instance. I'll show my code then explanations as I show it.
Here is my relevant controller code:
import {Component} from "#angular/core";
import {ProductService} from "../../services/product.service";
import {Subscription} from "rxjs";
import {ActivatedRoute} from "#angular/router";
#Component({
selector : 'product',
moduleId : module.id,
templateUrl : '/app/views/products/product-view.html'
})
export class ProductComponent {
private id:number;
private _subscription: Subscription;
public product;
private price;
private quantity = 0;
constructor(
private _productService: ProductService,
private _activatedRoute: ActivatedRoute
) {
}
getProduct(productId: number) {
this._productService.getProduct(productId)
.subscribe((response) => {
response.success.product.options.forEach((option) => {
this[option.name] = {
name: '-- please select --',
product_option_value_id: 0,
price: 0,
price_prefix: '+'
};
option.product_option_value.unshift({
name: '-- please select --',
product_option_value_id: 0,
price: 0,
price_prefix: '+'
});
});
this.product = response.success.product;
this.generatePrice();
});
}
changeOption(optionValueId, option) {
if(optionValueId != 0) {
let selectedOptionValue = option.product_option_value.filter((option) => {
return option.product_option_value_id == optionValueId;
});
this[option.name] = selectedOptionValue[0];
} else {
this[option.name] = {
name: '-- please select --',
product_option_value_id: 0,
price: 0,
price_prefix: '+'
};
}
this.generatePrice();
}
.....
Here I am getting back information about a product which includes 'options' in a form of an array. This array of objects is iterated over to create the select boxes in the view code which will come later. I add a default '-- please select --' object for each option and put it to the front of the array using unshift. I then also set the controller value for this in the line:
this[option.name] = {
name: '-- please select --',
product_option_value_id: 0,
price: 0,
rice_prefix: '+'
};
The relevant view code is as follows:
<div class='product-options'>
<div class='option' *ngFor='let option of product.options; let i = index'>
<p class='option-name' [innerHTML]='option.name'></p>
<select name='option.name' [ngModel]='option.name' (ngModelChange)='changeOption($event, option)' required>
<option *ngFor='let productOptionValue of option.product_option_value; let j = index;' [value]='productOptionValue.product_option_value_id'>{{ productOptionValue.name }}</option>
</select>
</div>
<div class='price' ngDefaultControl [(ngModel)]="price">{{ price | currency:'GBP':true:'1.2-2' }}</div>
<div class='add-to-basket-wrap'>
<button class='add-to-basket'>add to basket</button>
<button class='increment' (click)="changeQuantity('down')">-</button>
<input type='text' name='quantity-to-add' [(ngModel)]="quantity" (click)='addToBasket()' />
<button class='increment'(click)="changeQuantity('up')">+</button>
</div>
</div>
Here I loop through the options then through the values for these options to generate the select boxes. I set the [ngModel] attribute for the select to the same as the one that was saved in my controller. I was under the impression that Angular2 would detect this binding, spot the value was the same as the controller value and then automatically set that as the 'selected' default option.
Can anyone see why this isn't working?
Thanks
Looks like your code is not working because your template and your class are not binding to the same properties.
In your component template, when you write this:
<div *ngFor="let option of product.options; let i = index">
<select [ngModel]="option.name">...</select>
</div>
... you're effectively binding each <select> to a class property named product.options[i].name.
On the other hand, in your component class, when you write this:
changeOption(optionValueId, option) {
this[option.name] = selectedOptionValue[0];
}
... you're writing to a class property named after whatever string is contained in option.name, e.g. foo.
As you can see, product.options[i].name and foo don't match. Even by changing foo to another string, you won't be able to access the property you want.
A few remarks/questions that might help:
It's a bit strange to store options inside "dynamic" class properties — this[option.name] = .... Why not store them in a dedicated this.options property that you can declare, type, and log out for debugging purposes: this.options[option.name] = ....
Why did you decide to use <select [ngModel]="..." (ngModelChange)="..."> vs the more compact <select [(ngModel)]="...">?
Any reason why you're using simple quotes on your HTML attributes, e.g. <div class='price'> vs <div class="price">? This is not the usual style.

Angular 2: Is there an easy way using FormBuilder with FormArray in ng2?

Situation
I'm working on a form where I want to list some mails having a checkbox besides the subject and a "check all" checkbox in the same column as the other checkboxes.
The form looks simply like this:
[ ] Check all
------------------------------------------
[ ] This is email subject #1
[ ] This is email subject #2
[ ] ...
When I select Check all all the below checkboxes should be selected and when I click again, all mails should be unselected.
The mails are coming dynamically into the component via an #Input and the list can change at any point of time.
So far so easy, nothing special. BUT it seems not so easy when using the FormBuilder in ng2 for that. Side note: I want to use the FormBuilder to test my code less end-to-end but more with unit tests.
Current code
Template
<form [formGroup]="form">
<div><input formControlName="toggleAll" type="checkbox"></div>
<div>
<ul formArrayName="checkMailList">
<li *ngFor="let mail of mails; let i=index">
<input type="checkbox" [formControlName]="i">
<div>mail.subject</div>
</li>
</ul>
</div>
</form>
Component
#Component({ ... })
export class MailListComponent implements OnChanges {
#Input() mails: Mail[];
private get checkMailList(): FormArray { return this.form.get('checkMailList') as FormArray; }
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
checkMailList: this.fb.array([]);
});
}
ngOnChanges(changes: SimpleChanges) {
if (!changes['mails'] || !changes['mails'].currentValue) {
return;
}
// remove array first from form, as we will get
// mails again when anything updates
if (this.checkMailList.length > 0) {
this.form.removeControl('checkMailList');
this.form.addControl('checkMailList', this.fb.array([]));
}
this.mails.forEach(m => {
this.checkMailList.push(this.fb.control());
});
this.form
.valueChanges
.pluck('toggleAll')
.distinctUntilChanged()
.subscribe(selectAll => {
if (selectAll) {
this.checkMailList.setValue(this.mails.map(_ => true));
} else {
this.checkMailList.reset();
}
});
}
}
Problem
I think that there could occur a race condition/timing issue: I iterate over the mails array provided by #Input but I wire the checkMailList manually in the template to the corresponding index. I iterate over all mails whenever the #Input changes. I don't know if Angular first iterates over all mails in the template and then runs the ngOnChange method or vice versa. Can anyone can give me a profounded answer here?
Forms are the fundamental part of every WebApp. Am I doing it right? Any help would be appreciated.