Accordion List within ionic 2 - ionic-framework

I've create a custom components named Accordion within iconic 2 and working in browser perfectly but on device not working.
I've split my code up into components, where
Home.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import {DataCards} from '../../components/data-cards/data-cards';
import {Data} from '../../components/data/data';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public dataList: Data[];
constructor(public navCtrl: NavController) {
this.dataList = [
new Data('title 1', 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. ','ios-remove-circle-outline', true),
new Data('title 2', 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. ','ios-add-circle-outline', false),
new Data('title 3', 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. ','ios-add-circle-outline', false)
];
}
}
and the corresponding HTML
<ion-content padding>
<data-cards [data]="dataList"></data-cards>
</ion-content>
contain my custom component data-cards. data-cards has an input parameter data, through which the list of data is passed.
data.ts
import { Component } from '#angular/core';
#Component({
selector: 'data',
templateUrl: 'data.html'
})
export class Data {
constructor(public title: string, public details: string, public icon: string, public showDetails: boolean) {}
}
data-cards.ts
import { Component } from '#angular/core';
import { Data } from '../data/data';
#Component({
selector: 'data-cards',
inputs: ['data'],
templateUrl: 'data-cards.html'
})
export class DataCards {
public data: Data[];
constructor() {}
toggleDetails(data: Data) {
if (data.showDetails) {
data.showDetails = false;
data.icon = 'ios-add-circle-outline';
} else {
data.showDetails = true;
data.icon = 'ios-remove-circle-outline';
}
}
}
app.module.ts
import { NgModule } from '#angular/core';
import { IonicApp, IonicModule } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { Data } from '../components/data/data';
import { DataCards } from '../components/data-cards/data-cards';
#NgModule({
declarations: [
MyApp,
HomePage,
Data,
DataCards
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage,
Data,
DataCards
],
providers: []
})
export class AppModule {}
When run on iOS ( ionic run ios ) i've got an error like below :
[08:44:54] Error: Error at /Users/imac/Documents/ionic2Accordion/.tmp/components/data/data.ngfactory.ts:29:71
[08:44:54] Property 'string' does not exist on type 'typeof "/path/ionic2Accordion/.tmp/components/data/data"'.
[08:44:54] Error at /path/ionic2Accordion/.tmp/components/data/data.ngfactory.ts:29:111
[08:44:54] Property 'string' does not exist on type 'typeof "/path/ionic2Accordion/.tmp/components/data/data"'.
[08:44:54] Error at /path/ionic2Accordion/.tmp/components/data/data.ngfactory.ts:29:151
[08:44:54] Property 'string' does not exist on type 'typeof "/path/ionic2Accordion/.tmp/components/data/data"'.
[08:44:54] Error at /path/ionic2Accordion/.tmp/components/data/data.ngfactory.ts:29:191
[08:44:54] Property 'boolean' does not exist on type 'typeof "/path/ionic2Accordion/.tmp/components/data/data"'.
[08:44:54] ngc failed
[08:44:54] ionic-app-script task: "build"
[08:44:54] Error: Error
so my question : how i can resolve this problem any suggestion ?

In data-card.ts change
public data: Data[];
o be
Input() data: Data[];
since you will be assigning it from the component creation in the home.html? You'll also need to import the Input module via
import { Component, Input } from '#angular/core';

Related

Add Components dynamically in DOM ionic+angular

I am following How to Dynamically Create a Component in Angular to add components dynamically inside another component. I am receiving a weired error of undefined variable.
My Component file (MessComponent)
<template #messContainer>
<p>
mess works!
</p>
</template>
ts file
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-mess',
templateUrl: './mess.component.html',
styleUrls: ['./mess.component.scss'],
})
export class MessComponent implements OnInit {
constructor() { }
ngOnInit() {}
}
Parent Component (hosting dynamic component)
module ts file
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { CommonModule } from '#angular/common';
import { IonicModule } from '#ionic/angular';
import { FormsModule } from '#angular/forms';
import { HomePage } from './home.page';
import { HomePageRoutingModule } from './home-routing.module';
import { MessComponent } from './../mess/mess.component';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
HomePageRoutingModule
],
declarations: [HomePage, MessComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
entryComponents: [MessComponent]
})
export class HomePageModule {}
ts file
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentRef, ComponentFactory, OnInit } from "#angular/core";
import { MessComponent } from "./../mess/mess.component";
#Component({
selector: "app-home",
templateUrl: "home.page.html",
styleUrls: ["home.page.scss"],
})
export class HomePage implements OnInit {
componentRef: any;
#ViewChild('messContainer', { read: ViewContainerRef, static: true }) entry: ViewContainerRef;
createComponent() {
this.entry.clear();
const factory = this.resolver.resolveComponentFactory(MessComponent);
this.componentRef = this.entry.createComponent(factory);
}
destroyComponent() {
this.componentRef.destroy();
}
constructor(private resolver: ComponentFactoryResolver) {}
ngOnInit(): void {
this.createComponent();
}
}
and the error I am receiving
Uncaught (in promise): TypeError: this.entry is undefined
I understand this is claiming regarding the variable entry, but don't understand why it is not identifying that variable. To conclude, why I cannot add the component?
Solved it. Actually I was passing wrong param to the #ViewChild(''). I was passing the template name (container) of the child while I should have passed the container name in the parent component. So created a div in the parent component with #messContainer and corrected the #ViewChild
Important!:
now #messContainer is in the parent component and everything works as expected.
#ViewChild('messContainer', { read: ViewContainerRef, static: true }) entry: ViewContainerRef;

How to lazy load modals in ionic4

I need some help with lazy loading of modals in ionic 4. I googled a lot but can't find an exact solution.
I have several modals on a page. And I want to lazy load them. Following is the example of two modals on a page
In one of my modal, I need AndroidPermissions, so I have to import it in the module file of the page because importing in the module file of the modal is not working.
Why this is happening? Can ionic modals not be lazy-loaded?
Thank you in advance
home.module.ts
import { AddressPage } from '../pages/address/address.page'; // modal 1
import { AddAddressPage } from '../pages/add-address/add-address.page' // modal 2
import { AndroidPermissions } from '#ionic-native/android-permissions/ngx';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild([
{
path: '',
component: HomePage
}
])
],
declarations: [HomePage, AddressPage, AddAddressPage],
entryComponents :[AddressPage , AddAddressPage],
providers :[AndroidPermissions]
})
export class HomePageModule {}
To lazy loading of modals follow following steps
Add modal page's module in the import of your page
Remove all routing of modal as we don't need it
Remove modal's entry from app.routing.module
Add modal page in entryComponents of modal's module
In my case, I had two modals. The second modal is opened inside the first modal.
So I have to add modale1module in the import of the page and modal2module in the import of modal1module
base page.module
import { AddressModalPageModule } from '../address-modal/address-modal.module';
const routes: Routes = [
{
path: '',
component: CartsPage
}
];
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild(routes),
ReactiveFormsModule,
AddressModalPageModule
],
declarations: [CartsPage ],
})
export class CartsPageModule {}
modal1.module
import { AddressModalPage } from './address-modal.page';
import { AddAddressModalPageModule } from '../add-address-modal/add-address-modal.module';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
AddAddressModalPageModule
],
declarations: [AddressModalPage],
entryComponents:[AddressModalPage]
})
export class AddressModalPageModule {}
modal2.module
import { AddAddressModalPage } from './add-address-modal.page';
import { AndroidPermissions } from '#ionic-native/android-permissions/ngx';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
ReactiveFormsModule
],
declarations: [AddAddressModalPage],
entryComponents:[AddAddressModalPage],
providers :[
AndroidPermissions, ]
})
export class AddAddressModalPageModule {}
Ionic 4 supports lazy loading for modals, but as the documentation says with nuance:
it's important to note that the modal will not be loaded when it is opened, but rather when the module that imports the modal's module is loaded
To lazy load a modal you need to:
import your modal page module into the module of a component from which
the modal page will be opened
ensure you added the modal page into entry components list of the modal page module
You should be able to access your singleton provider inside your modal, by just importing it into the modal's page (Angular 8)
for example your modal's module ts looks like this:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
// import the component for your modal's content:
import { MyModalComponent } from '../my-modal/my-modal.component'
#NgModule({
// add it to entry components and to the declarations:
entryComponents: [MyModalComponent],
declarations: [MyModalComponent],
imports: [
CommonModule
]
})
export class LazyLoadedModalModule { }
Then importing it into the module of the page that will call the modal would look like this:
...
// import lazy loaded module:
import { LazyLoadedModalModule } from '../lazy-loaded-modal/lazy-loaded-modal.module';
#NgModule({
imports: [
IonicModule,
CommonModule,
// add it to the imports:
LazyLoadedModalModule,
RouterModule.forChild([{ path: '', component: Tab1Page }])
],
declarations: [Tab1Page]
})
export class Tab1PageModule {}
now in the page where you need to create the modal you need to import the component and use modal controller:
import { Component } from '#angular/core';
import { ModalController } from '#ionic/angular';
import { MyModalComponent } from '../my-modal/my-modal.component'
#Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss']
})
export class Tab1Page {
constructor(private modalCtrl: ModalController) {}
async openModal() {
const modal = await this.modalCtrl.create({
component: MyModalComponent
});
await modal.present();
}
}

Testing Error in Karma: Can't bind to 'cdkObserveContentDisabled' since it isn't a known property of 'label'

Setting up basic testing with parent component using a child FORM component. Getting the following error,
Failed: Template parse errors:
Can't bind to 'cdkObserveContentDisabled' since it isn't a known property of 'label'. ("m-field-label-wrapper">][cdkObserveContentDisabled]="appearance != 'outline'" [id]="_labelId" [attr.for]="_control.id" [attr."): ng:///DynamicTestModule/MatFormField.html#0:930
Error: Template parse errors:
Can't bind to 'cdkObserveContentDisabled' since it isn't a known property of 'label'
component.spec.ts is
import { CreatepageComponent } from './createpage.component';
import { NavbarComponent } from '../common/navbar/navbar.component';
import { TitleComponent } from '../common/title/title.component';
import { MobileTitleComponent } from '../common/mobile-title/mobile-title.component';
import { FormComponent } from '../common/form/form.component';
import { FooterComponent } from '../common/footer/footer.component';
import { MapComponent } from '../common/map/map.component';
import { SvgComponent } from '../common/svg/svg.component';
import { SvgDefinitionsComponent } from '../common/svg/svg-definitions/svg-definitions.component';
import { LinkComponent } from '../common/link/link.component';
import { DropdownMenuComponent } from '../common/dropdown-menu/dropdown-menu.component';
import { RouterTestingModule } from '#angular/router/testing';
import { RouterModule } from '#angular/router';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { ValidationErrorsComponent } from '../common/form/validation-errors/validation-errors.component';
import {
MatError,
MatFormFieldModule,
MatInputModule,
MatFormField,
MatLabel,
} from '#angular/material';
import { ButtonComponent } from '../common/button/button.component';
describe('CreatepageComponent', () => {
let component: CreatepageComponent;
let fixture: ComponentFixture<CreatepageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
CreatepageComponent,
NavbarComponent,
TitleComponent,
MobileTitleComponent,
FormComponent,
FooterComponent,
SvgComponent,
SvgDefinitionsComponent,
LinkComponent,
DropdownMenuComponent,
ValidationErrorsComponent,
MatError,
MatFormField,
ButtonComponent,
MapComponent
],
providers: [
RouterModule,
ReactiveFormsModule,
FormsModule,
MatFormFieldModule,
MatInputModule,
MatLabel
],
imports: [FormsModule, ReactiveFormsModule, RouterTestingModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CreatepageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Expected: Karma passes all tests
Actual: Karma responds with errors in any component with the form as a child.
Just ran into this myself. It looks like you need to import ObserversModule from '#angular/cdk/observers' in your spec.ts file, and include it in the 'imports' section under the configureTestingModule function.
import {ObserversModule} from '#angular/cdk/observers'; <== HERE
---------------------------
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [],
imports: [ObserversModule] <== HERE
},)
.compileComponents();
}));
Seen here:
https://material.angular.io/cdk/observers/api

Interface, Angular 5, Reactive Form

I have created an interface for login and getting and error when I am trying to compile it
error :Type 'FormGroup' is not assignable to type 'IAuth'. Property 'email' is missing in type 'FormGroup'
auth.ts
export interface IAuth{
email : String;
password : String;
}
login.component.ts
import { HomePage } from './../home/home';
import { FormBuilder, FormGroup, Validators, FormControl } from '#angular/forms';
import { Component } from '#angular/core';
import { IAuth } from '../../models/auth';
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
userAuth : IAuth;
constructor ( private formBuilder : FormBuilder)
{
this.userAuth = formBuilder.group({
email : new FormControl('',Validators.required),
password : new FormControl('',Validators.required)
});
}
}

Angular 2 RC5 - Redirecting the route with Observable

I'm not managing to make the protected component HeaderComponent be redirected to the LoginComponent component if the value is false the function canActivate.
The HeaderComponent component and their children are protected, only accessing the URL http://localhost:3000/#/header/index the screen goes blank, and I wanted it to be directed to http://localhost:3000/#/auth that is the login screen
Anyone know how to help me?
auth.guard.ts
import {Injectable} from '#angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from '#angular/router';
import {Observable, BehaviorSubject} from 'rxjs/Rx';
import 'rxjs/operator/take';
import {LoginService} from './login/login.service';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private loginService: LoginService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | boolean {
this.loginService.logado()
.subscribe(
data => {
return data //true
},
error => {
this.loginService.redirectUrl = state.url;
this.router.navigate(['/auth']);
return error //false
}
)
return this.loginService.logado().take(1);
}
}
routes.component.ts
import {Routes, RouterModule} from '#angular/router';
import {LoginComponent} from './login/login.component';
import {HeaderComponent} from './header/header.component';
import {AuthGuard} from './auth.guard';
import {UserComponent} from './user/user.component';
import {IndexComponent} from './index/index.component';
import {UserPasswordComponent} from './user/user.password.component';
export const appRoutes: Routes = [
{path: 'auth', component: LoginComponent},
{path: 'user', component: UserPasswordComponent},
{path: 'header', component: HeaderComponent, canActivate: [AuthGuard],
children: [
{path: 'index', component: IndexComponent},
{path: 'user', component: UserComponent}
]
},
{path: '', redirectTo: 'auth', pathMatch: 'full'},
];
export const routing = RouterModule.forRoot(appRoutes);
It works after the repair auth.guard.ts file, using as reference Angular2 - Extending router and Observable
auth.guard.ts
import {Injectable} from '#angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from '#angular/router';
import {Observable} from 'rxjs/Rx';
import {LoginService} from './login/login.service';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private loginService: LoginService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | boolean {
return this.loginService.logado()
.map(
data => {
if (data == false) {
this.router.navigate(['/auth']);
return data;
};
if (data == true) {
return data;
}
},
error => {
this.router.navigate(['/auth']);
return error
}
)
}
}