How to add sub-menu in ion sidemenu? I have done following code for side menu
app.html
<ion-menu [content]="content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
app.compact.ts
import { Component ,ViewChild} from '#angular/core';
import { Platform ,Nav} from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { Dashboard } from '../pages/dashboard/dashboard';
import { LineBoy } from '../pages/line-boy/line-boy';
import { Customer } from '../pages/customer/customer';
import { DeliveryLine } from '../pages/delivery-line/delivery-line';
import { Newspaper } from '../pages/newspaper/newspaper';
import { Scheme } from '../pages/scheme/scheme';
import { Expenses } from '../pages/expenses/expenses';
import { Report } from '../pages/report/report';
import { Billing } from '../pages/billing/billing';
import { Settings }from '../pages/settings/settings';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage:any = Dashboard;
#ViewChild(Nav) nav: Nav;
pages: Array<{title: string, component: any}>;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
});
this.pages = [
{ title: 'Dashboard', component: Dashboard },
{ title: 'LineBoy', component: LineBoy },
{ title: 'DeliveryLine', component: DeliveryLine },
{ title: 'Customer', component: Customer },
{ title: 'Newspaper', component: Newspaper },
{ title: 'Scheme', component: Scheme },
{ title: 'Expenses', component: Expenses },
{ title: 'Report', component: Report },
{ title: 'Billing', component: Billing },
{ title: 'Settings', component: Settings },
];
}
openPage(page) {
this.nav.setRoot(page.component);
}
}
https://market.ionic.io/plugins/ionic-multilevel-sidemenu
Related
I have implemented a side-menu in Ionic2.I am getting "Invalid link" when i click on the Event or Map button on the side-menu and sometimes i get the error as "Invalid Views to insert". I tried removing quotes from the calendar and gmap but it does not work out.
Please guide.
This is my TS file:
import { Component } from '#angular/core';
import { Platform , IonicPage } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { CalendarPage } from '../pages/calendar/calendar'
import { GmapPage } from '../pages/gmap/gmap'
import firebase from 'firebase';
import { LoginPage } from '../pages/login/login';
import { ViewChild } from '#angular/core';
import { NavController } from 'ionic-angular/navigation/nav-controller';
import { AuthProvider } from '../providers/auth/auth';
#IonicPage()
#Component({
templateUrl: 'app.html'
})
export class MyApp {
calendar: CalendarPage;
gmap: GmapPage;
#ViewChild('nav') nav: NavController;
rootPage: any = CalendarPage;
pages = []
constructor(platform: Platform, statusBar: StatusBar, splashScreen:SplashScreen,
public authProvider: AuthProvider) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (!user) {
this.rootPage = 'LoginPage';
unsubscribe();
} else {
this.rootPage = 'EventPage';
unsubscribe();
}
});
});
}
onLoad(page : string) {
this.nav.setRoot(page);
}
onLogout(){
this.authProvider.logoutUser();
}
}
This is my HTML code :
<ion-menu [content] = "nav">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<button ion-item (click) = "onLoad('calendar')">Events</button>
<button ion-item (click) = "onLoad('gmap')">Maps</button>
<button ion-item (click) = "onLogout()">Logout</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav [root]="rootPage" #nav></ion-nav>
How to hide native android keyboard when clicking on text box using IONIC 2? I have installed IONIC keyboard plugin from https://ionicframework.com/docs/native/keyboard/ link and uses this.keyboard.close();
But still keyboard is opening. Help me how to close the keyboard. I am basically showing DOB from the datepicker plugin in a TEXTBOXenter image description here.
This is the ts file(datepicker1.ts)
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { DatePicker } from '#ionic-native/date-picker';
import { Keyboard } from '#ionic-native/keyboard';
#IonicPage()
#Component({
selector: 'page-datepicker1',
templateUrl: 'datepicker1.html',
})
export class Datepicker1Page {
public today:any;
constructor(public navCtrl: NavController, public navParams: NavParams,private datePicker: DatePicker,private keyboard: Keyboard) {
}
ionViewDidLoad() {
console.log('ionViewDidLoad Datepicker1Page');
}
openDatepicker()
{
this.keyboard.close();
this.datePicker.show({
date: new Date(),
mode: 'date',
androidTheme: this.datePicker.ANDROID_THEMES.THEME_DEVICE_DEFAULT_LIGHT
}).then(
date => {
this.today=date.getDate()+'/'+date.getMonth()+'/'+date.getFullYear()},
err => console.log('Error occurred while getting date: ', err)
);
}
}
And this is the datepicker1.html page
<ion-header>
<ion-navbar>
<ion-title>datepicker page</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-item>
<ion-label>DOB</ion-label>
<ion-input type="text" name="DOB" (click)="openDatepicker()" [(ngModel)]="today" ng-readonly></ion-input>
</ion-item>
</ion-content>
You have missed to declare the today variable in the class and you missed to add disabled="true" in ion-input tag. Everything is working fine and I have tested it.
TS File
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { Keyboard } from '#ionic-native/keyboard';
import { DatePicker } from '#ionic-native/date-picker';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController, public keyboard : Keyboard, public datePicker : DatePicker) {
}
today : any;
openDatepicker(){
this.keyboard.close();
this.datePicker.show({
date: new Date(),
mode: 'date',
androidTheme: this.datePicker.ANDROID_THEMES.THEME_DEVICE_DEFAULT_LIGHT
}).then(
date => {
this.today=date.getDate()+'/'+date.getMonth()+'/'+date.getFullYear()},
err => console.log('Error occurred while getting date: ', err)
);
}
}
HTML File
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-item>
<ion-label>DOB</ion-label>
<ion-input disabled="true" type="text" name="DOB" (click)="openDatepicker()" [(ngModel)]="today" ng-readonly></ion-input>
</ion-item>
</ion-content>
1) import { DatePicker } from '#ionic-native/date-picker/ngx'; in app.module.ts and keyboard.page.ts
2) public keyboard : Keyboard, in your constructor inject
3) https://ionicframework.com/docs/native/keyboard Take reference from this Official site
openDatepickerStart(){
setTimeout(() => {
this.keyboard.hide();
}, 100);
this.datePicker.show({
date: new Date(),
mode: 'date',
androidTheme: this.datePicker.ANDROID_THEMES.THEME_DEVICE_DEFAULT_LIGHT
}).then(
date => {
this.SelectDateModelDetails.get('StartTime').patchValue(date.getDate()+'/'+date.getMonth()+'/'+date.getFullYear())},
err => console.log('Error occurred while getting date: ', err)
);
}
I am trying to achieve a log out functionality. I am using FB authentication. My problem here is after I do a logout successfully(This happens from the Side menu ) I return to the Login Screen. But now if I tap the device's back button it is taking me to the homepage(. Any idea how to prevent this ?
This is my app.component.ts
import { Component, ViewChild } from '#angular/core';
import { Platform, Nav, AlertController } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { LoginPage } from '../pages/login/login';
import { NativeStorage } from '#ionic-native/native-storage';
import { TabsPage } from '../pages/tabs/tabs';
import { Facebook } from 'ionic-native';
#Component({
templateUrl: 'app.html',
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage: any = LoginPage;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, private nativeStorage: NativeStorage, private alertCtrl: AlertController) {
platform.ready().then(() => platform.ready().then(() => {
this.nativeStorage.getItem("userId").then((data) => {
console.log(data.userExists);
this.rootPage = TabsPage;
this.nav.push(TabsPage);
}, (error) => {
console.log("No data in storage");
this.nav.push(LoginPage);
})
statusBar.styleDefault();
splashScreen.hide();
})
)
}
presentConfirm() {
let alert = this.alertCtrl.create({
title: "Confirm Logout",
message: "Do you really really want to quit this awesome app?",
buttons: [{
text: 'Nah! Just Kidding!', role: 'cancel', handler: () => {
}
},
{
text: "Ok",
handler: () => {
this.nativeStorage.clear();
this.nav.push(LoginPage);
this.rootPage = LoginPage;
Facebook.logout().then((response) => {
}, (error) => {
})
}
}]
});
alert.present();
}
}
This is my login page. Login.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { Facebook } from 'ionic-native';
import { NativeStorage } from '#ionic-native/native-storage';
import { TabsPage } from '../tabs/tabs'
#Component({
selector: 'login-home',
templateUrl: 'login.html'
})
export class LoginPage {
constructor(public navCtrl: NavController, private nativeStorage: NativeStorage) {
}
FBLogin() {
//this.navCtrl.push(TabsPage); - To be used on browser environment
Facebook.login(['email']).then((response) => {
Facebook.getLoginStatus().then((response) => {
if (response.status == 'connected') {
this.navCtrl.push(TabsPage); // - to be used on device
console.log("Setting Storage")
this.nativeStorage.setItem("userId", { userExists: true });
Facebook.api('/' + response.authResponse.userID + '?fields=id,name,gender', []).then((response) => {
}, (error) => {
alert(error)
})
}
else
alert("Not Logged in");
})
}, (error) => {
console.log((error));
})
}
}
This is my Menu
<ion-menu swipeEnabled="false" side="right" [content]="content">
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<p> </p>
<button menuClose padding-top ion-item> Test 1 </button>
<button menuClose ion-item> Test 2 </button>
<button menuClose ion-item (click)="presentConfirm()"><ion-icon name="power"></ion-icon> Logout </button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav id="nav" [root]="rootPage" #content swipe-back-enabled="false"></ion-nav>
Your issue is you are doing:
this.nav.push(LoginPage);
in your logout handler.
This means LoginPage is added on top of your old navigation stack. You need to set a new stack and set LoginPage as root.
Do :
this.nav.setRoot(LoginPage);
I am in the process of upgrading from Ionic 2 beta to Ionic 2 rc3.
I have my app.component.ts file, that worked fine, when it was just displaying a root page. But as soon as I have tried to add menu items from my old working Ionic 2 beta version, I get the error below.
If anyone can advise how I can resolve this, I would appreciate the help.
Compile Error in CLI
[13:14:56] template error, "E:\Development\IDE\ionic-apps\WhatsAppClone\src\app\build\app.html": Error: ENOENT: no such
file or directory, open 'E:\Development\IDE\ionic-apps\WhatsAppClone\src\app\build\app.html'
Runtime Error in browser console
Unhandled Promise rejection: Failed to load build/app.html ; Zone: meteor-rxjs-zone ; Task: Promise.then ; Value: Failed to load build/app.html undefined polyfills.js:3:7730
Error: Uncaught (in promise): Failed to load build/app.html
Stack trace:
s#http://localhost:8100/build/polyfills.js:3:8568
s#http://localhost:8100/build/polyfills.js:3:8391
h/<#http://localhost:8100/build/polyfills.js:3:8902
sg</d</t.prototype.invokeTask#http://localhost:8100/build/polyfills.js:3:14040
sg</v</e.prototype.runTask#http://localhost:8100/build/polyfills.js:3:11392
i#http://localhost:8100/build/polyfills.js:3:8021
t/this.invoke#http://localhost:8100/build/polyfills.js:3:15204
app.component.ts
import { Component, ViewChild } from '#angular/core';
import { Storage } from "#ionic/storage";
import { Platform, Events, AlertController, Nav } from 'ionic-angular';
import { StatusBar, Push, Splashscreen } from 'ionic-native';
import { SearchJobsPage } from "../pages/searchjobs/searchjobs";
import { LoginPage } from '../pages/login/login';
import { LogoutPage } from '../pages/logout/logout';
import { PersonModel } from '../pages/model/personModel';
import { ChatsPage } from '../pages/chats/chats';
import { PersonPage } from '../pages/person/person';
import { SearchFavouriteJobsPage } from '../pages/searchfavouritejobs/searchfavouritejobs';
import { SearchPostingsPage } from '../pages/searchpostings/searchpostings';
import { SearchFavouritePostingsPage } from '../pages/searchfavouritepostings/searchfavouritepostings';
import { UtilityService } from '../pages/utils/utilityService';
import { NotificationService } from '../pages/service/notificationService';
import { JobService } from '../pages/service/jobService';
import { JobModel } from '../pages/model/jobModel';
import { MapLocationsPage } from '../pages/maplocations/maplocations';
import { MapRangePage } from '../pages/maprange/maprange';
//import { METEOR_PROVIDERS } from 'angular2-meteor';
// import * as Check from 'meteor/check';
// import * as EJSON from 'meteor/ejson';
//declare let Meteor;
#Component({
templateUrl: 'build/app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage: any;
public storage: Storage = null;
public pages: Array<{ title: string, component: any }>;
public pages_person: Array<{ title: string, component: any }>;
public pages_person_admin: Array<{ title: string, component: any }>;
public menuTitle: string = 'Menu';
public personModel: PersonModel = null;
public utilityService: UtilityService = null;
public notificationService: NotificationService = null;
public personModelLoggedIn: PersonModel;
public jobService: JobService = null;
public events: Events = null;
public ios: boolean = false;
constructor(private platform: Platform, utilityService: UtilityService, notificationService: NotificationService, jobService: JobService, events: Events, private alertCtrl: AlertController, storage: Storage) {
this.storage = storage;
this.utilityService = utilityService;
this.jobService = jobService;
this.notificationService = notificationService;
this.events = events;
this.initializeApp();
if (this.platform.is('ios')) {
this.ios = true;
}
// this.rootPage = Meteor.user() ? TabsPage : LoginComponent;
this.rootPage = SearchJobsPage;
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.initializeApp();
StatusBar.styleDefault();
Splashscreen.hide();
});
// used for an example of ngFor and navigation
this.pages = [
{ title: 'Market', component: SearchJobsPage },
{ title: 'Postings', component: SearchPostingsPage },
{ title: 'Login', component: LoginPage }
];
this.pages_person = [
{ title: 'Market', component: SearchJobsPage },
{ title: 'Market Favourites', component: SearchFavouriteJobsPage },
{ title: 'Postings', component: SearchPostingsPage },
{ title: 'Favourite Postings', component: SearchFavouritePostingsPage },
{ title: 'Messages', component: ChatsPage },
{ title: 'Profile', component: PersonPage },
{ title: 'Logout', component: LogoutPage }
];
this.pages_person_admin = [
{ title: 'Market', component: SearchJobsPage },
{ title: 'Market Favourites', component: SearchFavouriteJobsPage },
{ title: 'Postings', component: SearchPostingsPage },
{ title: 'Favourite Postings', component: SearchFavouritePostingsPage },
{ title: 'Messages', component: ChatsPage },
{ title: 'Profile', component: PersonPage },
{ title: 'Logout', component: LogoutPage },
{ title: 'Map Locations', component: MapLocationsPage },
{ title: 'Map Range', component: MapRangePage }
];
}
initializeApp() {
StatusBar.styleDefault();
this.checkLogin();
this.utilityService.startUpChecks();
if (window['cordova']) {
this.utilityService.setLocalStrorage('this.chats.observe', 'false');
this.utilityService.setLocalStrorage('this.messages.observe', 'false');
this.utilityService.setLocalStrorage('this.messages.subscribe', 'false');
this.utilityService.setLocalStrorage('push:notifications.subscribe', 'false');
}
this.subscribeEvents();
}
// openPage(page) {
// // Reset the content nav to have just this page
// // we wouldn't want the back button to show in this scenario
// this.nav.setRoot(page.component);
// }
public subscribeEvents(): void {
this.events.subscribe('push:notifications', (data) => {
this.checkLogin();
});
}
public pushNotifications(): void {
let observedPromise: Promise<string> = this.utilityService.getLocalStrorage('push:notifications.subscribe');
observedPromise.then((observed: string) => {
if (!observed || observed != 'true') {
this.utilityService.setLocalStrorage('push:notifications.subscribe', 'true');
try {
if (window['cordova']) {
if (this.personModelLoggedIn) {
let promiseJobsForPerson: Promise<JobModel[]> = this.jobService.getJobsByPerson(this.personModelLoggedIn.id);
promiseJobsForPerson.then((data) => {
let jobModelsForPerson: JobModel[] = data;
let topics: string[] = [];
topics.push('P' + this.personModelLoggedIn.id);
for (let i = 0; i < jobModelsForPerson.length; i++) {
let jobModel: JobModel = jobModelsForPerson[i];
topics.push('J' + jobModel.id);
}
//topics.push('J65'); // deleteme
//topics.push('P9'); // deleteme
let push = Push.init({
android: {
senderID: "893141127008",
topics: topics
},
ios: {
alert: "true",
badge: false,
sound: "true",
topics: topics
},
windows: {}
});
push.on('registration', (data1) => {
this.events.subscribe('messages:notify', (data) => {
let promise: Promise<string> = this.notificationService.push('null', data[0].topic, data[0].message, data[0].title);
promise.then((data2) => {
// console.log('app.ts messages2:notify', data2);
});
});
});
push.on('notification', (data) => {
this.events.publish('messages:update');
if (this.nav.getActive().name != 'ChatsPage' && this.nav.getActive().name != 'MessagesPage') {
//if user using app and push notification comes
if (data.additionalData.foreground) {
//if application open, show popup
let confirmAlert = this.alertCtrl.create({
title: data.title,
message: data.message,
buttons: [{
text: 'Ignore',
role: 'cancel'
}, {
text: 'View',
handler: () => {
this.rootPage = ChatsPage;
}
}]
});
confirmAlert.present(confirmAlert);
} else {
this.rootPage = ChatsPage;
}
}
});
push.on('error', (e) => {
alert('Error: ' + e.message);
console.log(e);
});
});
}
}
} catch (e) {
alert('Push Notification: ' + e.message());
console.log('Push Notification: ' + e.message());
}
}
});
}
public checkLogin(): void {
let promise: Promise<string> = this.utilityService.getLoggedInPerson();
promise.then((data) => {
this.personModelLoggedIn = JSON.parse(data);
if (this.personModelLoggedIn) {
this.utilityService.setUpMenuItems();
this.pushNotifications();
}
});
}
}
app.html
<ion-menu [content]="content" id="unauthenticated">
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-menu [content]="content" id="authenticated-person">
<ion-toolbar [class]="ios ? 'menu-toolbar' : ''">
<ion-title [class]="ios ? 'menu-title' : ''">
<div class="item-avatar-img" id="menu-item-avatar-img-person"></div>
<div class="item-avatar-name" id="menu-item-avatar-name-person"></div>
</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages_person" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-menu [content]="content" id="authenticated-person-admin">
<ion-toolbar [class]="ios ? 'menu-toolbar' : ''">
<ion-title [class]="ios ? 'menu-title' : ''">
<div class="item-avatar-img" id="menu-item-avatar-img-person-admin"></div>
<div class="item-avatar-name" id="menu-item-avatar-name-person-admin"></div>
</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages_person_admin" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<!-- Disable swipe-to-go-back because it's poor UX to combine STGB with side menus -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
Directory
You have set your templateUrl as build/app.html.
You shouldnt check it in build folder.
Try templateUrl: 'app.html' in app.component.ts
I have spent a while getting the hang of modules in Angular2 and really like them but I am a little uncertain as to best approach for testing both my modules and the components within. (I also realise that my app.component can and probably should be broken out more but for now it is helpful while learning the testing framework to be a little more complex)
For example this is my app.module:
import { createStore, compose, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import { AUTH_PROVIDERS } from 'angular2-jwt';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { ComponentsModule } from './components';
import { MaterialModule} from './material';
import { RouterModule } from '#angular/router';
import { AppComponent } from './app.component';
import { ViewsModule } from './+views';
import { rootReducer } from './dataStore';
import { CurrentUserModel } from './models/current-user'
const appStore = createStore(rootReducer, applyMiddleware(ReduxThunk));
const APP_DECLARATIONS = [
AppComponent
];
const APP_PROVIDERS = [
{ provide: 'AppStore', useValue: appStore },
CurrentUserModel
];
#NgModule({
imports:[
FormsModule,
BrowserModule,
RouterModule,// here as well as in our Views Module because of router-outlet
ViewsModule,
MaterialModule, // here as well as in our Views & componet Module because used in App componet
ComponentsModule
],
declarations: APP_DECLARATIONS,
bootstrap:[AppComponent],
providers: APP_PROVIDERS,
})
export class AppModule {
}
and this is what my app.component looks like:
import { Component, ViewChild, AfterViewInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app',
styleUrls:['app.component.scss'],
template: `
<md-toolbar>
<!-- <i class="material-icons demo-toolbar-icon">menu</i> -->
<span class="toolbar-brand">Franks</span>
<span *ngIf="searchActive" role="search" class="fill-remaining-space">
<span class="search-input-container flex flex-1">
<i class="material-icons search-link">search</i>
<input class="search-input" placeholder="Search" type="text" id="searchInput" #searchInput (keyup.esc)="exitSearch($event)"/>
</span>
</span>
<i *ngIf="searchActive" class="material-icons right selectable" (click)="exitSearch($event)">close</i>
<span *ngIf="!searchActive" class="fill-remaining-space">
</span>
<span *ngIf="!searchActive" role="navmenu">
<span class="hlink" routerLink="/" routerLinkActive="active">home</span>
<span class="hlink" routerLink="/profile" routerLinkActive="active">Profile</span>
<span class="hlink" routerLink="/login" routerLinkActive="active">Login</span>
<span class="hlink" routerLink="/signup" routerLinkActive="active">Sign Up</span>
<i class="material-icons search-link" (click)="activeSearch($event)">search</i>
</span>
</md-toolbar>
<div class="container">
<router-outlet></router-outlet>
</div>
`,
})
export class AppComponent {
#ViewChild('searchInput') searchInputRef;
ngAfterViewChecked() {
if(this.searchActive && this.searchInputRef){
console.log(this.searchInputRef);
this.searchInputRef.nativeElement.focus();
}
}
searchActive: boolean;
constructor(public router: Router) {
this.searchActive = false;
}
activeSearch(event):void {
this.searchActive = true;
}
exitSearch(event) : void {
this.searchActive = false;
}
}
So I know I can potentially mock out all the Components with for example the MaterialComponents (this is just a wrapper module for the material components) within my tests but this seems a little tedious. Is that my only options and if so does it make sense to make creating a mock of components when creating the components part of the process.
for informational purposes this is what my material module looks like and my views and components modules are similar:
import { NgModule } from '#angular/core';
// Material
import { MdCardModule } from '#angular2-material/card';
import { MdButtonModule } from '#angular2-material/button';
import { MdInputModule } from '#angular2-material/input';
import { MdToolbarModule } from '#angular2-material/toolbar';
import { MdListModule } from '#angular2-material/list';
import { MdIconModule, MdIconRegistry } from '#angular2-material/icon';
const MATERIAL_UI_MODULES = [
MdCardModule,
MdButtonModule,
MdInputModule,
MdToolbarModule,
MdIconModule,
MdListModule
]
const MATERIAL_UI_REGISTRIES = [
MdIconRegistry
]
#NgModule({
imports:[
...MATERIAL_UI_MODULES,
],
providers: MATERIAL_UI_REGISTRIES,
exports:[
...MATERIAL_UI_MODULES,
]
})
export class MaterialModule {
}
So I eventually figured out how to do this and this is my current solution:
/* tslint:disable:no-unused-variable */
import { TestBed, inject, async } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { By } from '#angular/platform-browser';
import { Component,ViewChild, AfterViewChecked } from '#angular/core';
import { Router } from '#angular/router';
import { Location, CommonModule } from '#angular/common';
import { MaterialModule} from './material';
#Component({
template: '<div></div>'
})
class DummyComponent {
}
import { AppComponent } from './app.component';
describe('component: TestComponent', function () {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CommonModule,
RouterTestingModule.withRoutes([
{ path: 'profile', component: DummyComponent },
{ path: 'login', component: DummyComponent },
{ path: 'signup', component: DummyComponent },
{ path: '', component: DummyComponent }
]),
MaterialModule
],
declarations: [ AppComponent, DummyComponent ]
});
});
it('should create the app', async(() => {
let fixture = TestBed.createComponent(AppComponent);
let app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it('should be navigate to correct url for each option in navmenu',
async(inject([Router, Location], (router: Router, location: Location) => {
let fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
fixture.debugElement.query(By.css('span.hlink[routerLink="/profile"]')).nativeElement.click();
fixture.whenStable().then(() => {
expect(location.path()).toEqual('/profile');
expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/profile"]')).classes['active']).toBeTruthy();
expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)
fixture.debugElement.query(By.css('span.hlink[routerLink="/login"]')).nativeElement.click();
return fixture.whenStable();
}).then(() => {
expect(location.path()).toEqual('/login');
expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/login"]')).classes['active']).toBeTruthy();
expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)
fixture.debugElement.query(By.css('span.hlink[routerLink="/signup"]')).nativeElement.click();
return fixture.whenStable();
}).then(() => {
expect(location.path()).toEqual('/signup');
expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/signup"]')).classes['active']).toBeTruthy();
expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)
fixture.debugElement.query(By.css('span.hlink[routerLink="/"]')).nativeElement.click();
return fixture.whenStable();
}).then(() => {
expect(location.path()).toEqual('/');
expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/"]')).classes['active']).toBeTruthy();
expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)
});
})));
});