I have a nested form created for an ionic project, in which I have formGroups that contain formArray, and each formArray has one or more formGroups, itself.
The process of saving data works great. I can have as many formArrays with as many formGroups as I like.
My problem is when I am trying to populate the form with a saved data. I cannot add correctly the data inside the formArrays.
This is my edit script:
import { Component } from '#angular/core';
import { FormBuilder, FormControl, FormArray, FormGroup, Validators } from '#angular/forms';
import { NavController, NavParams } from 'ionic-angular';
import { Todos } from '../../providers/todos';
import { HomePage } from '../home/home';
import { Patient } from '../../interfaces/patient.interface';
#Component({
selector: 'page-edit',
templateUrl: 'edit.html'
})
export class EditPage {
patient: any;
patientDate: any;
editTodoForm: FormGroup;
submitted: boolean;
events: any[] = [];
constructor(public navCtrl: NavController, public todoService: Todos, public navParams: NavParams, public formBuilder: FormBuilder) {
this.patient = this.navParams.data;
this.patientDate = new Date(this.patient.date).toISOString();
this.editTodoForm = formBuilder.group({
_id: [this.patient._id],
_rev: [this.patient._rev],
firstName: [this.patient.firstName, Validators.compose([Validators.pattern('[a-zA-Z ]*'), Validators.required])],
date: [this.patientDate],
botoxes: this.formBuilder.array([]),
acids: this.formBuilder.array([])
});
this.subcribeToFormChanges();
this.addBotox();
this.addAcid();
}
ionViewDidLoad() {
console.log('ionViewDidLoad EditPage');
this.editTodoForm.setValue(this.patient);
}
initBotox() {
return this.formBuilder.group({
botoxDate: [''],
botoxTypes: this.formBuilder.array([
this.formBuilder.group({
botoxType: [''],
botoxZone: [''],
botoxUnit: ['']
})
])
});
}
addBotox() {
const control = <FormArray>this.editTodoForm.controls['botoxes'];
const botoxCtrl = this.initBotox();
if(this.patient.botoxes) {
this.patient.botoxes.forEach(botox => {
control.push(botoxCtrl);
})
} else {
control.push(botoxCtrl);
}
}
removeBotox(i: number) {
const control = <FormArray>this.editTodoForm.controls['botoxes'];
control.removeAt(i);
}
initAcid() {
return this.formBuilder.group({
acidDate: [''],
acidTypes: this.formBuilder.array([
this.formBuilder.group({
acidType: [''],
acidZone: [''],
acidUnit: ['']
})
])
});
}
addAcid() {
const control = <FormArray>this.editTodoForm.controls['acids'];
const acidCtrl = this.initAcid();
control.push(acidCtrl);
}
removeAcid(i: number) {
const control = <FormArray>this.editTodoForm.controls['acids'];
control.removeAt(i);
}
subcribeToFormChanges() {
const myFormStatusChanges$ = this.editTodoForm.statusChanges;
const myFormValueChanges$ = this.editTodoForm.valueChanges;
myFormStatusChanges$.subscribe(x => this.events.push({ event: 'STATUS_CHANGED', object: x }));
myFormValueChanges$.subscribe(x => this.events.push({ event: 'VALUE_CHANGED', object: x }));
}
updateTodo(model: Patient, isValid: boolean) {
this.submitted = true;
this.todoService.updateTodo(this.editTodoForm.value);
this.navCtrl.setRoot(HomePage);
}
}
This is the edit html:
<ion-header no-border>
<ion-navbar color="primary">
<ion-title>Editeaza pacient</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<form [formGroup]="editTodoForm" novalidate>
<div [hidden]="editTodoForm.controls.firstName.valid || (editTodoForm.controls.firstName.pristine && !submitted)" class="error-notification">
Pacientul trebuie sa aiba cel putin un nume si un prenume
</div>
<ion-card>
<ion-card-header>
Date personale
</ion-card-header>
<ion-list padding>
<ion-item>
<ion-label stacked>Prenume pacient</ion-label>
<ion-input type="text" formControlName="firstName"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Dată activitate</ion-label>
<ion-datetime displayFormat="DD MMMM YYYY" pickerFormat="DD MMMM YYYY" formControlName="date"
monthNames="ianuaie, februarie, martie, aprilie, mai, iunie, iulie, august, septembrie, octombrie, noiembrie, decembrie"></ion-datetime>
</ion-item>
</ion-list>
</ion-card>
<ion-card>
<ion-card-header>
Tratamente botox
</ion-card-header>
<ion-list padding>
<ion-card formArrayName="botoxes">
<div *ngFor="let botox of editTodoForm.controls.botoxes.controls; let i=index">
<p class="card-heading">
<span>Tratament cu botox {{i + 1}}</span>
<button ion-button icon-only *ngIf="editTodoForm.controls.botoxes.controls.length > 1" (click)="removeBotox(i)" class="right-button remove-button">
<ion-icon name="trash"></ion-icon>
</button>
</p>
<div [formGroupName]="i">
<botoxInputs [group]="editTodoForm.controls.botoxes.controls[i]"></botoxInputs>
</div>
</div>
</ion-card>
<button (click)="addBotox()" ion-button icon-left>
<ion-icon name="add"></ion-icon>
Adauga tratament cu botox
</button>
</ion-list>
</ion-card>
<ion-card>
<ion-card-header>
Tratamente acid hialuronic
</ion-card-header>
<ion-list padding>
<ion-card formArrayName="acids">
<div *ngFor="let acid of editTodoForm.controls.acids.controls; let i=index">
<p class="card-heading">
<span>Tratament cu acid hialuronic {{i + 1}}</span>
<button ion-button icon-only *ngIf="editTodoForm.controls.acids.controls.length > 1" (click)="removeAcid(i)" class="right-button remove-button">
<ion-icon name="trash"></ion-icon>
</button>
</p>
<div [formGroupName]="i">
<acidInputs [group]="editTodoForm.controls.acids.controls[i]"></acidInputs>
</div>
</div>
</ion-card>
<button (click)="addAcid()" ion-button icon-left>
<ion-icon name="add"></ion-icon>
Adauga tratament cu acid hialuronic
</button>
</ion-list>
</ion-card>
<div padding>
<button ion-button color="primary" block type="submit" (click)="createPatient(editTodoForm, editTodoForm.valid)">Salveaza date pacient</button>
</div>
</form>
</ion-content>
And this is an example of a saved JSON I am trying to add back to the form:
{
"firstName": "Ionescu Ion",
"date": "2017-02-01T00:00:00.000Z",
"botoxes": [{
"botoxDate": "2017-02-01",
"botoxTypes": [{
"botoxType": "Xeomin 100UI",
"botoxZone": ["Frunte", "Crow feet", "Sprânceană"],
"botoxUnit": "111"
}, {
"botoxType": "Azzalure 50UI",
"botoxZone": ["Glabelar", "Intersprincenos", "Frunte"],
"botoxUnit": "222"
}]
}],
"acids": [{
"acidDate": "2017-02-01",
"acidTypes": [{
"acidType": "Juvederm Volift",
"acidZone": ["Periocular", "Tâmple"],
"acidUnit": "0.5 ml"
}]
}],
"_id": "0A418E81-CFD0-545B-B8DB-A326CECFC5F1",
"_rev": "3-f2914e24db5fac42930dba548f418cbd"
}
My problem is that I can get only on e botoxType displayed of the two botoxTypes.
I would really appreciate any help
For anyone having the same issues as I had. This is how I changed my edit script to set initial values properly on formArrays:
import { Component } from '#angular/core';
import { FormBuilder, FormArray, FormGroup, Validators } from '#angular/forms';
import { NavController, NavParams } from 'ionic-angular';
import { Todos } from '../../providers/todos';
import { HomePage } from '../home/home';
import { Patient } from '../../interfaces/patient.interface';
#Component({
selector: 'page-edit',
templateUrl: 'edit.html'
})
export class EditPage {
patient: any;
editTodoForm: FormGroup;
submitted: boolean;
events: any[] = [];
constructor(public navCtrl: NavController, public todoService: Todos, public navParams: NavParams, public formBuilder: FormBuilder) {
this.patient = this.navParams.data;
this.editTodoForm = formBuilder.group({
_id: [this.patient._id],
_rev: [this.patient._rev],
firstName: ['', Validators.compose([Validators.pattern('[a-zA-Z ]*'), Validators.required])],
date: [''],
botoxes: this.formBuilder.array([]),
acids: this.formBuilder.array([])
});
this.subcribeToFormChanges();
if(this.patient.botoxes.length > 0) {
this.patient.botoxes.forEach(botox => {
let btys = botox.botoxTypes.length;
this.addBotox(btys);
});
} else {
this.addBotox(1);
}
if(this.patient.acids.length > 0) {
this.patient.acids.forEach(acid => {
let atys = acid.acidTypes.length;
this.addAcid(atys);
});
} else {
this.addAcid(1);
}
}
ionViewDidLoad() {
console.log('ionViewDidLoad EditPage');
const value: Patient = this.navParams.data;
(<FormGroup>this.editTodoForm).patchValue(value, { onlySelf: true });
}
initBotox(number) {
return this.formBuilder.group({
botoxDate: [''],
botoxTypes: this.addBotoxTypes(number)
});
}
initBotoxTypes() {
return this.formBuilder.group({
botoxType: [''],
botoxZone: [''],
botoxUnit: ['']
});
}
addBotoxTypes(number) {
let bts = new FormArray([]);
for(let i = 0; i < number; i++) {
bts.push(this.initBotoxTypes())
}
return bts;
}
addBotox(number) {
const control = <FormArray>this.editTodoForm.controls['botoxes'];
const botoxCtrl = this.initBotox(number);
control.push(botoxCtrl);
}
removeBotox(i: number) {
const control = <FormArray>this.editTodoForm.controls['botoxes'];
control.removeAt(i);
}
initAcid(number) {
return this.formBuilder.group({
acidDate: [''],
acidTypes: this.addAcidTypes(number)
});
}
initAcidTypes() {
return this.formBuilder.group({
acidType: [''],
acidZone: [''],
acidUnit: ['']
});
}
addAcidTypes(number) {
let acs = new FormArray([]);
for(let i = 0; i < number; i++) {
acs.push(this.initAcidTypes())
}
return acs;
}
addAcid(number) {
const control = <FormArray>this.editTodoForm.controls['acids'];
const acidCtrl = this.initAcid(number);
control.push(acidCtrl);
}
removeAcid(i: number) {
const control = <FormArray>this.editTodoForm.controls['acids'];
control.removeAt(i);
}
subcribeToFormChanges() {
const myFormStatusChanges$ = this.editTodoForm.statusChanges;
const myFormValueChanges$ = this.editTodoForm.valueChanges;
myFormStatusChanges$.subscribe(x => this.events.push({ event: 'STATUS_CHANGED', object: x }));
myFormValueChanges$.subscribe(x => this.events.push({ event: 'VALUE_CHANGED', object: x }));
}
updateTodo(model: Patient, isValid: boolean) {
this.submitted = true;
this.todoService.updateTodo(this.editTodoForm.value);
this.navCtrl.setRoot(HomePage);
}
}
Related
I'm developing an ionic 3 application in which I need to show the camera interface inside the app screen and camera-preview seems to be the right and only solution to go with at this moment. However, when I trigger the startCamera function, I always get the error ' Object(...) is not a function at CameraPreview.startCamera'. Any help would be much appreciated.
Here are the steps I used for the installation:
From CLI:
ionic cordova plugin add https://github.com/cordova-plugin-camera-preview/cordova-plugin-camera-preview.git
npm install #ionic-native/camera-preview
2.Add to the provider list in module.ts file
import { CameraPreview } from '#ionic-native/camera-preview/ngx';
.....
providers: [
CameraPreview, ...
]
Below is the page where I would use the plugin:
import { Component, NgZone } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { CameraPreview, CameraPreviewPictureOptions, CameraPreviewOptions, CameraPreviewDimensions } from '#ionic-native/camera-preview/ngx';
#Component({
selector: 'page-submitdata2',
templateUrl: 'submitdata2.html',
})
export class Submitdata2Page {
public getWidth: number;
public getHeight: number;
public calcWidth: number;
cameraPreviewOpts: CameraPreviewOptions =
{
x: 40,
y: 100,
width: 220,
height: 220,
toBack: false,
previewDrag: true,
tapPhoto: true,
camera: this.cameraPreview.CAMERA_DIRECTION.BACK
}
constructor(
public cameraPreview: CameraPreview, private zone: NgZone,
public navCtrl: NavController, public navParams: NavParams) {
this.zone.run(() => {
this.getWidth = window.innerWidth;
this.getHeight = window.innerHeight;
});
console.log('width', this.getWidth);
this.calcWidth = this.getWidth - 80;
console.log('calc width', this.calcWidth);
}
ionViewDidLoad() {
console.log('ionViewDidLoad Submitdata2Page');
}
startCamera() {
debugger
this.cameraPreview.startCamera(this.cameraPreviewOpts).then(
(res) => {
console.log(res)
},
(err) => {
console.log(err)
});
}
stopCamera() {
this.cameraPreview.stopCamera();
}
takePicture() {
this.cameraPreview.takePicture()
.then((imgData) => {
(<HTMLInputElement>document.getElementById('previewPicture')).src = 'data:image/jpeg;base64,' + imgData;
});
}
SwitchCamera() {
this.cameraPreview.switchCamera();
}
showCamera() {
this.cameraPreview.show();
}
hideCamera() {
this.cameraPreview.hide();
}
}
The HTML:
<ion-header>
<ion-navbar>
<ion-title>Preview Page</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h5>This is camera Preview Application..</h5>
<div class="displaybottom">
<button ion-button (click)="startCamera()"> Start Camera</button>
<button ion-button (click)="stopCamera()"> Stop Camera</button>
<button ion-button (click)="takePicture()"> Take Picture Camera</button>
<button ion-button (click)="SwitchCamera()"> Switch Camera</button>
<button ion-button (click)="showCamera()"> Show Camera</button>
<button ion-button (click)="hideCamera()"> Hide Camera</button>
</div>
<div class="pictures">
<p><img id="previewPicture" width="200" /></p>
<!--<p><img id="originalPicture" width="200"/></p>-->
</div>
</ion-content>
My Development Enviroment:
I am making ionic project in my project i want to add double tab likes button .My proble is when tap on image then show likes icon for all post .I want to show only likes post icon .How can resolve this please help me below are my code which is used for likes icon..
tab1.page.html
<ion-header>
<ion-toolbar color="secondary">
<ion-title style="border: 1px solid #ccc" (click)="scrollToTop()">Pictagram</ion-title>
</ion-toolbar>
</ion-header>
<ion-content cache-view="false">
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content
pullingIcon="arrow-dropdown"
pullingText="Pull to refresh"
refreshingSpinner="circles"
refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
<hr no-margin no-padding>
<div (swipe)="swipePage($event)">
<!-- Cards -->
<ion-card no-padding padding-bottom no-margin class="card" *ngFor="let getImage of getImages3">
<ion-row>
<ion-col col-10>
<ion-item>
<ion-avatar item-left>
<img *ngIf="getImage.sex == 'male'" src="http://localhost:8000/files/{{getImage.image || 'avatar2.png'}}">
<img *ngIf="getImage.sex == 'female'" src="http://localhost:8000/files/{{getImage.image || 'Female-Avatar.jpg'}}">
</ion-avatar>
<b style="width: 222px;display: -webkit-box;padding-left: 11px;">{{getImage.name}}</b>
</ion-item>
</ion-col>
<ion-col col-2>
</ion-col>
</ion-row>
<div>
<img src="http://localhost:8000/files/{{getImage.image || 'avatar2.png'}}" (click)="tapPhotoLike(getImage.id)">
</div>
<p no-margin no-padding>
<button clear ion-button icon-only class="like-btn">
<ion-icon no-padding [name]="like_btn.icon_name" color="{{ like_btn.color }}" class="icon-space"></ion-icon>
</button>
</p>
<ion-card-content class="padding">
<p class="like-content">
<ion-icon class="color" name="heart" ></ion-icon> {{getcounts}} likes
</p>
<p><b>{{getImage.title}}</b>{{getImage.info}}</p>
<ion-note style="font-size: 12px">
{{getImage.created_at | timeAgo}}
</ion-note>
</ion-card-content>
</ion-card>
</div>
</ion-content>
In tab1.page.ts file i am manage the icon please help me how to show icon only for liked post. how to set id please help me...
tab1.page.ts
import { Component, OnInit } from '#angular/core';
import { UserService } from '../user.service';
import { User } from '../user';
import { first } from 'rxjs/operators';
import { Storage } from '#ionic/storage';
import { ToastController } from '#ionic/angular';
import { LoadingController } from '#ionic/angular';
#Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss']
})
export class Tab1Page implements OnInit {
getImages: User[] = [];
getImages2: User[] = [];
getImages3;
getcounts;
countLikes
counts
unlikes
public like_btn = {
color: 'danger',
icon_name: 'heart-empty'
};
public tap: number = 0;
constructor(private userService: UserService,
public toastController: ToastController,
private storage: Storage,
public loadingController: LoadingController) {
}
doRefresh(event) {
this.userPost();
setTimeout(() => {
event.target.complete();
}, 2000);
}
ngOnInit() {
this.userPost();
this.getCountOfLikes();
}
async userPost() {
const loading = await this.loadingController.create({
message: 'Please wait...',
spinner: 'crescent',
duration: 2000
});
await loading.present();
this.userService.getUserPost().pipe(first()).subscribe(getImages => {
this.getImages3 = getImages;
//console.log(this.getImages3);
loading.dismiss();
});
}
likeButton() {
const detail_id = this.userService.getCurrentIdpostId();
if (this.like_btn.icon_name === 'heart-empty') {
this.like_btn.icon_name = 'heart';
this.like_btn.color = 'danger';
this.storage.get('userId').then((val) => {
if (val) {
this.userService.userPostLikes(val, detail_id).pipe(first()).subscribe(
async data => {
//console.log(data);
if (data['status'] === 'you have already liked this post') {
const toast = await this.toastController.create({
message: 'you have already liked this post',
duration: 2000
});
toast.present();
}
this.getCountOfLikes();
}
);
}
});
}
else {
this.like_btn.icon_name = 'heart-empty';
this.like_btn.color = 'danger';
this.unlikePost();
this.getCountOfLikes();
}
}
tapPhotoLike($detail_id) {
this.userService.setCurrentIdpostId($detail_id);
this.tap++;
setTimeout(() => {
if (this.tap == 1) {
this.tap = 0;
//alert('Single Tap');
} if (this.tap > 1) {
this.tap = 0;
this.likeButton();
//alert('Double Tap');
}
}, 250);
}
ionViewWillEnter() {
this.userPost();
this.getCountOfLikes();
}
getCountOfLikes() {
this.userService.getCountId().pipe(first()).subscribe(likes => {
this.counts = likes;
for (var k in this.counts) {
var i = this.counts[k].detail_id;
}
this.userService.getUserPostlikes(i).pipe(first()).subscribe(getlikes => {
this.getcounts = getlikes;
});
});
}
unlikePost() {
let detail_id = this.userService.getCurrentIdpostId();
this.storage.get('userId').then((val) => {
if (val) {
this.userService.unLikes(val, detail_id).subscribe(async dislikes => {
this.unlikes = dislikes;
})
}
});
}
}
This is an general example of how you can fix your shared button problem.
We are going to make an array which will toggle values. The array will be the size of the data array. Inside we will place all values to false.
If a button is clicked, the index of the data array will be passed and this will change the value of the toggle array to true.
HTML
Looping over the data array with index i
<ion-item *ngFor="let image of images; index as i">
<ion-button (click)="toggleIcon(i)">
<ion-icon name="heart-empty" *ngIf="!iconToggle[i]"></ion-icon>
<ion-icon name="heart" *ngIf="iconToggle[i]"></ion-icon>
</ion-button>
</ion-item>
TS
If we get the data from the subscribe, we set the toggle array to all false values.
If the button is clicked, the value of the same index as the data array changes to true and the other icon can be showed in html.
images = []
iconToggle = [];
this.userService
.getUserPost()
.subscribe(res => {
this.images = res;
this.setIconToggles();
});
setIconToggles() {
let x = 0;
this.images.forEach(() => {
this.iconToggle[x] = false;
x++;
});
}
toggleIcon(num: number) {
if (this.orderToggle[num]) {
this.orderToggle[num] = false;
} else {
this.orderToggle[num] = true;
}
}
Using Ionic4 for the first time and struggling with ngSubmit not triggering the respective method in the login page. Although its always successfully hitting the LoginPage constructor and AuthService constructor. All the respective modules have been imported and there are no console errors too. What am i doing wrong ?
login.page.html:
<ion-header>
<ion-toolbar>
<ion-title>
Login
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form (ngSubmit)="login()" [formGroup]="loginForm">
<ion-grid>
<ion-row>
<ion-col>
<ion-item>
<ion-input type="text" placeholder="Email" formControlName="email" style="width:50%;"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col col-3>
<ion-item>
<ion-input type="password" placeholder="Password" formControlName="password" style="width:50%;"></ion-input>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<div padding-horizontal>
<div class="form-error">{{loginError}}</div>
</div>
<ion-grid>
<ion-row>
<ion-col>
<ion-button type="submit" [disabled]="!loginForm.valid" color="primary">Log in</ion-button>
Forgot password?
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-button icon-left block clear (click)="loginWithGoogle()" color="secondary">
<ion-icon name="logo-google"></ion-icon>
Log in with Google
</ion-button>
<ion-button icon-left block clear (click)="signup()" color="primary">
<ion-icon name="person-add"></ion-icon>
Sign up
</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</form>
</ion-content>
login.page.ts:
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../common/services/auth.service';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
loginForm: FormGroup;
loginError: string;
constructor(private auth:AuthService,
private router:Router,
private fb: FormBuilder)
{
this.loginForm = this.fb.group({
email: ['', Validators.compose([Validators.required, Validators.email])],
password: ['', Validators.compose([Validators.required, Validators.minLength(6)])]
});
}
ngOnInit() {}
login() {
alert('login');
let data = this.loginForm.value;
if (!data.email) {
return;
}
let credentials = {
email: data.email,
password: data.password
};
this.auth.signInWithEmail(credentials)
.then(
() => this.router.navigate(['/home']),
error => this.loginError = error.message
);
}
}
login.module.ts:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule,ReactiveFormsModule } from '#angular/forms';
import { Routes, RouterModule } from '#angular/router';
import { IonicModule } from '#ionic/angular';
import { LoginPage } from './login.page';
const routes: Routes = [
{
path: '',
component: LoginPage
}
];
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
IonicModule,
RouterModule.forChild(routes),
],
entryComponents: [LoginPage],
declarations: [LoginPage]
})
export class LoginPageModule {}
auth.service.ts:
import { Injectable } from '#angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import AuthProvider = firebase.auth.AuthProvider;
#Injectable({
providedIn: 'root'
})
export class AuthService {
private user: firebase.User;
constructor(public afAuth: AngularFireAuth) {
afAuth.authState.subscribe(user => {
this.user = user;
});
}
signInWithEmail(credentials) {
console.log('Sign in with email');
return this.afAuth.auth.signInWithEmailAndPassword(credentials.email,
credentials.password);
}
}
ngSubmit is reloading the complete page and hence its hitting only login constructor but not the respective method. Removed (ngSubmit) on <form>, type="submit" on <ion-button>, added (click)="login()" event to <ion-button>. It started working.
Short summary of what i actually want to do.
If i click on the heroes table. I need to pass the heroes data from the row clicked to a Modal.
I read that there are two ways to get Data into a Modal. (NGMODAL)
With #Input and #Output or through a shared Service.
But it doesn't matter which way I am using. The modal opens (and fetches data) always before the data is passed.
I've simply got a table in which all my heroes are displayed.
On a Click on the delete button in a table row I wan't to open the modal and pass the data from my table row. (I want to pass the whole hero, but shouldn't be a difference to just passing the name of the hero).
After that i want to show.
Do you really want to delete hero with name...?
[Cancel], [Yes, Delete] . Onclick on the Yes, Delete Button I want to delete the hero.
At the moment I can't even display the heroes name I want to delete
I hope anyone can tell my what I can do and how.
Thank you.
Here is my code:
Shows my Heroes: hero.component.html
<div class="container">
<h2>Heroes</h2>
<input type="text" placeholder="Suchbegriff eingeben" class="form-control has-float-label" [(ngModel)]="filter" >
<br>
<div *ngIf="heroes">
<table class="heroes table table-hover table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let hero of heroes | filter:filter:'name':'name'">
<td>{{hero.name}}</td>
// I could use (click)="pusHero(hero)" and open the modal afterwars. Than i would get the right data through input. But when i click to open the modal first there is no data, or the data from the row clicked before
<td>
<ngbd-modal-component (click)="selectRow(hero) [(delhero)]="delhero""></ngbd-modal-component> </td>
</tbody>
</table>
</div>
</div>
heroes.component.ts
import { Component, OnInit, ViewEncapsulation , Input , EventEmitter} from '#angular/core';
import { Hero } from '../../model/hero';
import { HeroService } from '../hero.service';
import { CommonModule } from '#angular/common';
import { Observable } from 'rxjs/Observable';
import { Response } from '#angular/http/src/static_response';
// For use of map
import 'rxjs/Rx';
// Für Pipe und Suche in Liste
import { Pipe, PipeTransform } from '#angular/core';
import { FilterPipe } from '../../pipes/filter.pipe';
// Component Decorator imported from Component
#Component({
// Unique Selector
selector: 'app-hero',
// URL of Template
templateUrl: './hero.component.html',
// URL of stylesheet
styleUrls: ['./hero.component.css']
// encapsulation: ViewEncapsulation.None
})
export class HeroesComponent implements OnInit {
heroes: Observable<Hero[]>;
hero: Hero;
filter = '';
// Is Input for Child Component. In this case modal
delhero: Hero;
constructor(
private heroService: HeroService,
// Modal Service
// private modalService: NgbModal,
// private ngbdModalComponent: NgbdModalComponent,
// private ngbdModalContent: NgbdModalContent,
) {
// this.searchableList = ['name','age']
}
selectRow(hero): void {
console.log("Select the tables data");
console.log(hero);
this.heroService.selectRow(hero);
}
}
Hero Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { HttpResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { catchError, map, tap } from 'rxjs/operators';
import { Hero } from '../model/hero';
import { MessageService } from '../message.service';
import { Response } from '#angular/http/src/static_response';
// For use of map
import 'rxjs/Rx';
import { headersToString } from 'selenium-webdriver/http';
const httpOptions = {
headers: new HttpHeaders(
{
'Content-Type': 'application/json'
}
)
};
#Injectable()
export class HeroService {
private hero: Hero;
constructor(private http: HttpClient, private messageService: MessageService) { }
selectRow(input){
this.hero = input;
}
getRow(){
console.log(this.hero);
return this.hero;
}
}
My Modal
modal.component.html
<button class="btn btn-danger btn-xs more" (click)="open()"> <span class="fa fa-trash"></span></button>
modal.component.ts
import { Component, Input, EventEmitter, SimpleChange, Output, OnInit } from '#angular/core';
import { NgbModal, NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
import { NgbdModalContent } from './modal.content.component';
import { Hero } from '../../model/hero';
import { HeroService } from '../../hero/hero.service';
// Modal Component
#Component({
selector: 'ngbd-modal-component',
templateUrl: './modal.component.html'
})
export class NgbdModalComponent {
// #Input() delhero: Hero;
// hero: Hero;
// #Output() getDeleteHero = new EventEmitter();
name = '';
constructor(private modalService: NgbModal,
private heroService: HeroService) {}
ngOnInit(){
// this.open();
// console.log(this.delhero);
}
open() {
var name = this.heroService.getRow();
// this.getDeleteHero.emit();
console.log("Modal Component hero");
// console.log(this.delhero);
this.name = this.heroService.getRow().name;
const modalRef = this.modalService.open(NgbdModalContent);
modalRef.componentInstance.name = name;
}
delete(){
console.log("Delete Hero through Hero Service");
}
}
modal.content.component.html
<div class="modal-header ">
<h4 class="modal-title">Delete Hero?</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Delete Hero with Name: {{name}}?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="activeModal.close('Close click')">No, do not delete</button>
<button type="button" class="btn btn-primary">Ja, löschen</button>
</div>
modal.content.component.ts
import { Component, Input, EventEmitter } from '#angular/core';
import { NgbModal, NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
import { HeroService } from '../../hero/hero.service';
import { Hero } from '../../model/hero';
// Modal Content
#Component({
selector: 'ngbd-modal-content',
templateUrl: './modal.content.component.html'
// styleUrls: ['./modal.content.component.css']
})
export class NgbdModalContent {
// #Input() Hero;
name = "";
constructor(
public activeModal: NgbActiveModal,
privateheroService: HeroService
) {}
ngOnInit() {
//Called after the constructor, initializing input properties, and the first call to ngOnChanges.
//Add 'implements OnInit' to the class.
console.log("NG On Init ModalsContent");
this.name = this.heroService.getRow().name;
console.log(this.name);
}
}
Not sure if this helps but I found this after having the same problem.
I ended up with working code. Sorry it's not your code but I was having trouble following it. I had to set a variable and pass it in via the click code:
HTML:
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">Option Entry</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label class="control-form-label" for="optDescription">Description:</label>
<input type="text" class="form-control" name="description" ngModel id="optDescription" [(ngModel)]="editOption">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="c('Close click')">Update</button>
</div>
</ng-template>
<div *ngIf='optionList && optionList.length'>
<div *ngFor='let opt of optionList'>
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0" class="accordianheading">
<ngb-panel>
<ng-template ngbPanelTitle>
<span>{{opt.description}}</span>
<button class='btn btn-sm btn-outline-primary btnline pull-right' (click)='$event.preventDefault(); $event.stopPropagation(); editItem(content, opt)'>Edit</button>
</ng-template>
<ng-template ngbPanelContent>
<js-option-lists [OptionListID]=opt.id></js-option-lists>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
</div>
and:
.ts file
import { Component, OnInit, Input } from '#angular/core';
import { IOptionListHeader } from '../dtos/option-list-header';
import { OptionListService } from '../services/felixApi/option-list.service';
import { NgbModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
import { NgModel, NgForm } from '#angular/forms';
#Component({
selector: 'js-option-lists',
templateUrl: './option-lists.component.html',
styleUrls: ['./option-lists.component.css']
})
export class OptionListsComponent implements OnInit {
#Input() OptionListID: number;
errorMessage: any;
optionList: IOptionListHeader[] = [];
updatedOption: any;
closeResult: string;
editOption = '';
constructor(private _optionListService: OptionListService, private modalService: NgbModal) { }
// for now read in all options but will need to read by level
ngOnInit() {
this.getChildren(this.OptionListID);
}
getChildren (id) {
this._optionListService.getOptionListChildren(id)
.subscribe(
optionList => {
this.optionList = optionList;
},
error => this.errorMessage = <any>error);
}
// Patch change
patchOrderNo(updatedOption: any) {
this._optionListService.updateOption(updatedOption).subscribe(
res => {
this.getChildren(this.OptionListID);
},
err => {
console.log('error code ' + err.status);
}
);
}
editItem(content, opt: IOptionListHeader) {
this.editOption = opt.description;
this.modalService.open(content).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
this.updatedOption = { id: opt.id, description: this.editOption };
this.patchOrderNo(this.updatedOption);
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
console.log('cancelled');
});
}
private getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return 'by pressing ESC';
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return 'by clicking on a backdrop';
} else {
return `with: ${reason}`;
}
}
}
I have a Login page. It was working fine. I changed the login form to use FormBuilder.
There is no error during build process or any reported in the developer tool. But the application shows blank screen when running with ionic serve.
Below is the code changed in login.html.
<form [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)" >
<ion-row>
<ion-col>
<ion-list inset>
<ion-item>
<ion-input type="text" placeholder="Username" formControlName="username" ></ion-input>
</ion-item>
<p *ngIf="username.hasError('required') && username.touched" danger>Username is required</p>
<ion-item>
<ion-input type="password" placeholder="Password" formControlName="password"></ion-input>
</ion-item>
<p *ngIf="password.hasError('required') && password.touched" danger>Password is required</p>
</ion-list>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="signup-col">
<button ion-button class="submit-btn" full type="submit" [disabled]="!loginForm.valid">Login</button>
<button ion-button class="register-btn" block clear (click)="createAccount()">Create New Account</button>
</ion-col>
</ion-row>
</form>
Below is the code changed in login.ts.
import { Component } from '#angular/core';
import { NavController, AlertController, LoadingController, Loading } from 'ionic-angular';
import { FormBuilder, FormGroup, FormControl, Validators } from '#angular/forms';
import { AuthService } from '../../providers/auth-service';
import { RegisterPage } from '../register/register';
import { HomePage } from '../home/home';
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class LoginPage {
loading: Loading;
registerCredentials = {username: '', password: ''};
loginForm: FormGroup;
username: FormControl;
password: FormControl;
constructor(private nav: NavController, private auth: AuthService,
private alertCtrl: AlertController, private loadingCtrl: LoadingController,
public formBuilder: FormBuilder) {
this.loginForm = formBuilder.group({
username: ['', Validators.required],
password: ['', Validators.required],
});
this.username = this.loginForm['username'];
this.password = this.loginForm['password'];
}