Multi Tabs ionic2 - tabs

i have an ionic2 app and i want to display an ion-tabs with multiple ion-tab from an array. This array i want to populate with a generic tab and pass it an array.
I have this component <multi-tab></multi-tab>:
<ion-tabs tabsPlacement="top" class="multi-tabs">
<ion-tab *ngFor="let tab of tabs" [tabTitle]="tab.title" [root]="tab.component"></ion-tab>
</ion-tabs>
And this is the ts file:
[some imports here]
export class Tab {
title: string;
component: any;
};
const tabs = [{title: 'Snacks', component: Snacks},
{title: 'Drinks', component: Drinks},
{title: 'Frozen', component: Frozen},
{title: 'Custom', component: customTab}];
#Component({
selector: 'multi-tab',
templateUrl: 'multitab.html'
})
export class multiTab {
tabs : Array<Tab>;
constructor(public modalCtrl: ModalController) {
this.tabs = tabs;
}
}
This is my custom tab component:
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items">
<h2>{{item.title}}</h2>
<p>Quantity: {{item.units}}</p>
<ion-icon name="trash" item-right color="indigo100" (click)="onDelete(item)"></ion-icon>
</ion-item>
</ion-list>
<ion-fab center bottom>
<button ion-fab mini color="pink200" (click)="onAdd()"><ion-icon name="add"></ion-icon></button>
</ion-fab>
</ion-content>
And this the ts file:
[some imports]
const CustomItems = [{
'title': 'custom',
'units': 1
}];
#Component({
templateUrl: 'customTab.html'
})
export class customTab {
items : Array<Item>;
constructor(public modalCtrl: ModalController) {
this.items = CustomItems;
}
[some methods]
I want to use in all tabs the customTab and give it an array to initialize (the items array property) But the closest I've ever been is having this error:
Can't resolve all parameters for customTab(?)
when i tried:
constructor(public aux: Array<Item>) {
this.items = aux;
}

There are 2 options, either use the property rootParams (explained in depth here for example) or create a service that is injected in both components.
Both solutions have their merits, the first one is using the mechanism introduced explicitly for that purpose because ion-tabs are custom components and you can't use inputs and outputs. The second solution is the main mechanism of sharing data in angular applications (like a user session) but can seem as overkill for this small task.
There is an example of using rootParams in this commit.

Related

How do you set the value of a select-options value now that ngModel is deprecated

Now that ngModel is deprecated in Angular, how do you set the value of the select-options on entering a page.
I'm using a reusable form:
constructor(public formBuilder: FormBuilder) {
this.createProfileForm = this.formBuilder.group({
gender: new FormControl('', [Validators.required]),
})
}
ngOnInit() {
this.createProfileForm.patchValue({
gender: 'Man'
})
console.log('form', this.createProfileForm.value);
}
<form [formGroup]="createProfileForm">
<ion-card>
<ion-select formControlName="gender" placeholder="*Gender">
<ion-select-option data-cy="gender-dropdown" *ngFor="let genOption of genderOptions"> {{genOption}}</ion-select-option>
</ion-select>
<ion-button class="submit" expand="full" (click)="addProfile()" [disabled]="!createProfileForm.valid">Submit</ion-button>
</form>
I can't use ngModel so I'm trying to patch the value but that value doesn't appear on the selection even though I can see the patch value in the console.log
{bio: '', gender: 'Man', education: '', smoking: '', drinking: '', …}
bio: ""
dateOfBirth: ""
drinking: ""
education: ""
gender: "Man"
smoking: ""
So I can customize the options I have the select options as an input on the form component:
#Input('genderOptions') genderOptions: string[];
On the page that actually uses the form I'm sending in the genderOptions in the following:
genderOptions: string[] = ['Woman', 'Man', 'Transgender Woman', 'Transgender Man', 'Non-binary'];
<app-profile-form [genderOptions]="genderOptions" (profile)="onSubmit($event)"></app-profile-form>
So when I load the profile page I need to set the value of the gender.
This is one attempt to resolve the issue to no avail:
<ion-select formControlName="gender" placeholder="*Gender" (change)="valueChanging($event)" [value]='{{Opt}}'>
<ion-select-option data-cy="gender-dropdown" *ngFor="let genOption of genderOptions"> {{genOption}}</ion-select-option>
</ion-select>
How exactly do you set the value without using ngModel?
You can set the value of a select element with the help of reactiveforms in Angular.
Below is my attempt to illustrate the following :
How to retrieve the selected value via 'Event Change' and 'Reactive Forms'
How to set the value of a dropdown using 'Reactive Forms' in Angular.
app.component.html
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<form [formGroup]="exampleFormGroup" (ngSubmit)="onSubmit(exampleFormGroup)">
<select formControlName="exampleSelect" (change)="onChange($event)">
<option *ngFor="let item of itemList" [value]="item">{{item}}
</option>
</select>
<p>{{selectedValue}}</p>
<button type="submit">Submit</button>
</form>
app.component.ts
import { Component, VERSION } from '#angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
exampleFormGroup: FormGroup;
selectedValue: any = '';
constructor(public formBuilder: FormBuilder) {
this.exampleFormGroup = this.formBuilder.group({
exampleSelect: new FormControl('', [Validators.required]),
})
}
onChange(event:any){
console.log(event.target.value);
}
itemList=["item1","item2","item3","item4"];
onSubmit(formData){
console.log('Form has been submitted!');
//get the selected value
console.info(this.exampleFormGroup.controls['exampleSelect'].value);
//set the value
this.exampleFormGroup.controls['exampleSelect'].setValue('item4');
}
}
For getting the selected value on 'Event change of a drop down' ,
please refer 'onChange()' method.
To get and set the value of a dropdown via reactive form please refer 'onSubmit()' method.
Let me know if this will help by providing your feedback, so that others will get benefitted as well.
I have created a sample project which can be accessed from here!

passing data to ionic 5 custom element tag

my ionic 5 app has a side menu and tabs. Each page can have different contents for the side menu and the tabs. I don't want to be duplicating the ion-menu in each page so I created a header component like so (I also do one for the tabs):
HEADER COMPONENT:
<ion-menu contentId="content">
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>{{pageObj.title}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-menu-toggle auto-hide="false" *ngFor="let page of pageObj.pages">
<ion-item [routerLink]="page.url" routerDirection="root" [class.active-item]="selectedPath.startWith(page.url)">
<ion-icon>{{page.icon}}</ion-icon>
<ion-label>
{{page.title}}
</ion-label>
</ion-item>
</ion-menu-toggle>
</ion-list>
</ion-content>
</ion-menu>
Now I use this header component in app.component.html like so:
<ion-app>
<ion-split-pane>
<app-header></app-header>
<ion-router-outlet id="content"></ion-router-outlet>
</ion-split-pane>
</ion-app>
And app.component.ts:
...........
.........
export class AppComponent {
pageObj: any = '';
selectedPath = '';
pages = [
{
title: 'Become a Member',
url: '/pages/membership'
},
{
title: 'Make a Posts',
url: '/pages/posts'
}
]
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private router: Router
) {
this.initializeApp();
this.pageObj.title = 'Menu';
this.pageObj.pages = this.pages;
this.router.events.subscribe((event:RouterEvent) => {
if (event && event.url) {
this.selectedPath = event.url;
}
})
}
.........
........
So the problem is requires variables (pageObg) that exist in app.component.ts. I am not sure how to pass those variables? I believe I can do something like this:
<app-header [pageObj]="pageObj"><app-header>
but I am not sure how this works! Is there a better way to achieve what I am trying to do?
You are on the right path, but that is not enough. You need to create the corresponding #Input pageObj property in the child component's class. That should be sufficient in your case.
This is the comprehensive guide on Component Interaction, please refer to the first section on "input binding" at this link: https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding

How to create material cards on button click?

I'm trying to create mat-cards when clicking on a button.
This is the information, which should be in the mat-card (this information comes from a service).
blockHash: "iejg5gpylg6l9gjxor3bnvigs0ipaonr"
blockNumber: 1
previousBlock: "00000000000000000000000000000000"
transactions: Array (1)
0 {sender: "10", recipient: null, amount: null, fee: null}
At the beginning the section, where the mat cards are at should be completely empty. When clicking on a button, that section should be filled with one mat-card; when clicking again, a second mat-card should appear and so on.
This is the block with the information (in another component), which gets send to the component, which should add up the material cards.
This is how it should look like (This is just hardcoded at the moment).
What's an elegant way to do that?
You should create a parent-component that displays multiple card-components.
https://stackblitz.com/edit/angular-zvjblo
parent-component
The parent-component holds your list of blocks and displays multiple card-components by supplying each card-component with the block data for that card. There is also a button to add a new block to the list.
template
<button (click)="addCard()">Add Card</button>
<app-block-card *ngFor="let block of blocks" [blockData]="block"></app-block-card>
code
import { Component, OnInit } from '#angular/core';
import { BlockData } from './block-data';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
blocks: BlockData[];
ngOnInit() {
this.blocks = [];
}
addCard() {
this.blocks.push({
blockHash: '9348534985720587',
blockNumber: 3,
previousBlock: "0000",
transactions: [
{
sender: 'sender',
recipient: 'recipient',
amount: 1,
fee: 200
}
]
});
}
}
card-component
The card-component receives the data of one block from the parent component and displays it.
template
<mat-card class="card">
<p>{{blockData.blockHash}}</p>
<p>{{blockData.blockNumber}}</p>
<p>{{blockData.previousBlock}}</p>
<p>{{blockData.transactions | json}}</p>
</mat-card>
code
import { Component, OnInit, Input } from '#angular/core';
import { BlockData } from '../block-data';
#Component({
selector: 'app-block-card',
templateUrl: './block-card.component.html',
styleUrls: ['./block-card.component.css']
})
export class BlockCardComponent implements OnInit {
#Input() blockData: BlockData;
constructor() { }
ngOnInit() {
}
}
You could be using a list, but I am not sure how to make it horizontal so that might require some tweaking.
But, I would say a drag and drop list would also be very fitting.
The documentation easily describes how to apply these with a horizontal view. But if the drag and drop feature is not for you, then the go with the list.
Using either you can run a *ngFor loop that can be performed upon arrays of data.
Example of such:
<div cdkDropList cdkDropListOrientation="horizontal" class="example-list"
(cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let card of cardArray" cdkDrag>
<mat-card class="example-card">
<mat-card-header>
<div mat-card-avatar class="example-header-image"></div>
<mat-card-title>Shiba Inu</mat-card-title>
<mat-card-subtitle>Dog Breed</mat-card-subtitle>
</mat-card-header>
<img mat-card-image src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="Photo of a Shiba Inu">
<mat-card-content>
<p>
text
</p>
</mat-card-content>
<mat-card-actions>
<button mat-button>LIKE</button>
<button mat-button>SHARE</button>
</mat-card-actions>
</mat-card>
</div>
</div>
In your case you will have to apply the card template inside the *ngFor div, and bind the data accordingly to your naming.
To add a new cards through a button you will simply have to add a new element to the array that you loop over, and it will appear when it has been added.

How to get input text value in ionic

I'm trying to get input text value and store it in a variable named input in ionic. But I tried and failed. Can anyone please tell me what I have faulty done?
This is my HTML
<ion-content>
<ion-list>
<ion-item>
<ion-label stacked>display</ion-label>
<ion-input type="text" text-right id="input" ></ion-input>
</ion-item>
</ion-list>
</ion-content>
and this is my home.ts in ionic
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController) {
var input=document.getElementById('input').nodeValue;
document.getElementById('seven').innerHTML=input;
}
}
Actually you seems to be using angular not angularjs, use [(ngModel)]
<ion-input type="text" [(ngModel)]="name" text-right id="input" ></ion-input>
and inside the component,
name:string;
so whenever you need the value , you can use.
console.log(this.name);
<ion-content>
<ion-list>
<ion-item>
<ion-label stacked>display</ion-label>
<ion-input type="text" text-right id="input" [(ngModel)]="inputValue"></ion-input>
</ion-item>
</ion-list>
</ion-content>
// ===
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
inputValue: string = "";
constructor(public navCtrl: NavController) {}
someFunction() {
// here you can use the 'this.inputValue' and get the value of the ion-input
}
}
we use the two way binding the value of ion-input with the class member inputValue,
about ngModel
whan you need to access on the value of the input, check the value of inputValue.
here you can see an exemple I wrote on StackBlitz
Two-way binding is a combination of both property binding and event binding as it is a continuous synchronization of data/values from presentation layer to component and from component to the presentation layer.
Since this is a two way binding we have to use both the brackets - [ ( ) ]. Also ngModel is a directive which is used to bind the data both ways.
In Ionic 5 - The way to get the value of an ion-input value when focusing:
<ion-input (ionFocus)="onFocusPlace($event)"></ion-input>
onFocusPlace(event){
this.value = event.target.value;
}

getting errors while displaying json format list in html it cannot be displayed

I am facing some problems while displaying the json formatted data in html file. It work successfully but list is not displayed. This in my code...
import { Component } from '#angular/core';
import { Platform } from 'ionic-angular';
import { Hotspot, Network } from 'ionic-native';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public platform: Platform){
this.platform = platform
}
List(){
Hotspot.scanWifi().then((networks:Array<Network>)=>{
console.log(networks);
});
}
}
This is my HTML File
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<button ion-button color="Primary" (click)="List()">ScanWifi</button>
<div *ngFor="let network of networks">
<ion-list>
<ion-item>
<h2>{{network}}</h2><br>
</ion-item>
</ion-list>
</div>
</ion-content>
You are just console logging your result, your current code:
List(){
Hotspot.scanWifi().then((networks:Array<Network>)=>{
console.log(networks);
});
You need to declare a local variable networks so that you can use it in the view as you have:
So your code should look something like this:
networks;
List(){
Hotspot.scanWifi().then((networks:Array<Network>)=>{
this.networks = networks;
});
so that you can refer to your networks in your view:
<div *ngFor="let network of networks">
EDIT: I first wrongfully "assumed" that Hotspot needed to be injected into the constructor, but by doing some research, I found out that Hotspot is a native element with methods, and can therefore be called just by Hotspot.scanWifi() as per can be seen here.
You have to set networks array in the class
export class HomePage {
networks:any;
constructor(public platform: Platform){
this.platform = platform
}
List(){
Hotspot.scanWifi().then((networks:Array<Network>)=>{
console.log(networks);
this.networks = networks;//or do forEach and push
});
}
}