Angular 2 / Ionic Content is behind Headerbar - javascript

I have the problem that I tried to extract the headerbar into a own component but if I use this new component the content of the page goes behind the headerbar.
Without extracting the header and using the same code directly in the HTML it works correctly.
My new Component:
header-bar.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'my-header-bar',
templateUrl: 'header-bar.html'
})
export class MyHeaderBar {
#Input() pageTitle : String;
constructor() {}
}
header-bar.html
<ion-header>
<ion-navbar>
<button ion-button menuToggle icon-only>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>{{pageTitle}}</ion-title>
</ion-navbar>
</ion-header>
The page I try to use it:
home.html
<my-header-bar pageTitle="Einstellungen"> </my-header-bar>
<ion-content>
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps
that are going to primarily use a Tabbed UI.
</p>
...
</ion-content>
Why does it not work with my own component?

I'm using following code and have not faced any issue till now. I tried with following;
<ion-navbar navbar>
<ion-title >{{title}}</ion-title>
</ion-navbar>
Following is ts file code:
#Component({
selector: 'navbar',
templateUrl: 'navbar.html'
})
export class CommonNavbar {
public title: string;
constructor(
private nav: NavController) {
}
setTitle(title: string){
this.title = title;
console.log(this.title);
}
}
And using this in a following way:
<ion-header>
<navbar></navbar>
</ion-header>
I had also added some styling to common navbar( like centering the title) and configured this from the component/page by using following code snippet.
import { CommonNavbar } from '../../components/shared_nav/navbar';
#ViewChild(forwardRef(() => CommonNavbar)) commonNavbar: CommonNavbar;
ngAfterViewInit() {
this.commonNavbar.setTitle(title);
this.commonNavbar.centerTitle(true);
}

I tried to do the same in an older version of Ionic, and I was facing the same issues. I asked that on the Ionic Slack channel and #mhartington (from Ionic team) said:
There is no way to create a global ion-navbar, as this is done on
purpose. The point of having a navbar defined for each component is so
that we can properly animate the titles, navbar background color (if
you change them) and animate other properties needed.
And about creating a custom directive to avoid duplicating ion-header / ion-navbar html code:
That will still creat errors with how angular2 content projection
works. We have several issues that have been open when people try this
and the best answer is to not do it.

Related

Angular: 7.2.14 - Yet Another Router Link Not Working Topic

Sorry to revive a question. Im using Angular 7 and Im trying to use Router Link.
This is my app-routing-module
const routes: Routes = [
{ path: 'locations' , component : LocationManagerComponent },
{ path: 'locations/create' , component : CreateEditLocationComponent },
{ path: 'locations/create/:id', component : CreateEditLocationComponent },
{ path: '404' , component : PageNotFoundComponent},
{ path: '**' , redirectTo: '/404'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
This is router link:
<a [routerLink] = "['/locations']" routerLinkActive="active"> test link </a>
When I click on link, nothing happens. The URL on browser changed but component is not loaded.
If I press F5, component is loaded and from that point on, routers link works.
I've tryed a lot of stackoverflow solution like writing link in any sort of variant like
<a routerLink="/locations" ...
<a [routerLink]= ['/locations'] ...
<a [routerLink]= "['/locations']" ...
With or without LinkAttive attribute. Putting
<base href="/">
in index.html etc....
Following this topic: TOPIC I've tried to include Router in my Layout component:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-layout',
templateUrl: './layout.component.html',
styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit {
constructor(
private route : ActivatedRoute
) { }
[...]
but nothing changes.
The strange part is that after an F5, all routes works, even route to component not yet loaded.
In this topic TOPIC 2 the user resolved removing css class. I've tried to put my link in a completely cleaned component HTML and it not working (but still works after a refresh).
<p>
dashboard works!
<a routerLink = '/locations' routerLinkActive="active"> test link </a>
</p>
UPDATE: This is layout.component where route tag is.
I can't figure out how to have a Sidenav without having route-outlet inside it.
<mat-sidenav-container fullscreen>
<mat-sidenav #sidenav mode="over">
<div class="profile_container">
<span> User Name </span>
</div>
<mat-nav-list>
<mat-list-item><a [routerLink]="['/locations']" routerLinkActive="active"> Locations
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<app-header (toggleSidenav)="sidenav.toggle()"></app-header>
<div style="padding: 20px 10px 10px 20px;">
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
Note: this answer is based on the previous version of your question, before you added the code of layout.component.html. So, instead of layout component, I am using the simplified dashboard component.
The below is working for me in Angular 8.1.
app.component.html
<app-dashboard></app-dashboard>
means that the DashboardComponent is contained within (is the child of) the AppComponent.
No change to the default app.component.ts
dashboard.component.html
<p>
dashboard works!
<a routerLink = '/locations' routerLinkActive="active">
Locations test link </a>
</p>
<p><a routerLink = '/locations/create' routerLinkActive="active">
Locations/create </a></p>
<p><a routerLink = '/locations/create/:id' routerLinkActive="active">
Locations/create/:id </a></p>
<p>router-outlet is below:</p>
<router-outlet></router-outlet>
All the links are working with click and with manually entering the url (eg: http://localhost:4200/locations/create/:id) in the browser and with reload (F5).
New Components
Generated using the ng generate component command:
Dashboard
LocationManager
CreateEditLocation
PageNotFound
app-routing-module.ts
The same as your file, but also added import statements for the newly generated components.
I figured what cause the problem but I can't unserstand why and I was not able to reproduce in StackBlitz.
This was my app.component.html, the root of all app:
<main>
<!-- Showing All Site Pages -->
<span *ngIf='isLogged()'>
<app-layout style="height:100%"></app-layout>
</span>
<!-- Showing Login Page -->
<div *ngIf='!isLogged()'>
<router-outlet></router-outlet>
</div>
</main>
The App-Layout code is above.
THIS NOT WORKS!
I changed it with a simply:
<main>
<app-layout style="height:100%"></app-layout>
</main>
As you see from my question, Layout has its own router-outlet.
I think the problem is the two router-outlet tag. Maybe Angular is not able to understand thats they are mutually exclusive. Maybe when I was clicking on menu, for some reason, Angular was updating the "first" router-outlet encountered and only after a refresh (F5), when the isLogged was already triggered and the app-layout was loaded directly, Angular knows which router-outlet to use.
In the new Way all pages, even Login, has to be child of AppLayout so every Layout component that's exists only if logged, has to be manually hide with *ngIf='!isLogged()'
A little price to pay to have routes works.

Ngrx Store - Should all UI changes be in store?

I'm using Angular 2 with Ngrx and Angular Material.
Are all UI changes like dropdown or dialog supposed to be in store?
For example:
<button md-button [mdMenuTriggerFor]="menu">Menu</button>
<md-menu #menu="mdMenu">
<button md-menu-item>Item 1</button>
<button md-menu-item>Item 2</button>
</md-menu>
Should I support dropdown in the store?
Next example:
<md-sidenav-container class="example-container">
<md-sidenav #sidenav class="example-sidenav">
Jolly good!
</md-sidenav>
<div class="example-sidenav-content">
<button type="button" md-button (click)="sidenav.open()">
Open sidenav
</button>
</div>
</md-sidenav-container>
Can I use open method? Maybe I should create component that wrap sidenav in component that operate only on input Input() open;?
For me, there are generally 3 questions I ask.
Are the UI changes isolated to the component and do not affect other components?
Do the UI changes need to be persisted when the view returned to after have been destroyed?
For debugging purposes do you want to track this state (possibly in order to recreate the view of the end user?)
If you answered NO to these 3 questions then you probably do NOT need to put the state of that component in the store.
By not coupling something the store, you make it more modular, easier to test and reusable.
You should use the store to keep state and the UI should be bound to that state. You can then keep Observable to that state hierarchy or subscribe to those state changes and update the UI accordingly and dynamically.
This is what I had in mind:
import { Component } from "#angular/core";
import { Store } from "#ngrx/store";
// Other relevant imports ....
#Component({
selector: 'some-comp',
templateUrl: 'some-comp.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SomeComponent implements OnDestroy {
employeeObs$:Observable<EmployeeInfo>;
constructor( private store: Store<IAppState>) {
this.employeeObs$ = this.store.select('employee')
.map((next: IEmployeeState) => next.info);
}
// ...
}
And then on html template something like:
<!-- Some other regular DOM
...
...
-->
<!-- Conditional DOM based on state -->
<div *ngIf="employeeObs$ | async as employeeInfo">
<div type="text">{{employeeInfo.name}}</div>
</div>
<!-- Some other regular DOM
...
...
-->
The conditional tag will only show if Observable has data...

Ionic 2 Tabs Maximum call stack size exceeded

This error seems to be a plague haunting Ionic 2. I've looked at several questions on this topic but none have been of help so far.
I've created this component Layout Component which wraps all my pages:
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>
{{pageName}}
</ion-title>
</ion-navbar>
</ion-header>
<ng-content>
</ng-content>
<ion-footer>
<ion-toolbar>
<ion-tabs>
<ion-tab [root]="usersPage" tabTitle="Chat" tabIcon="chat"></ion-tab>
</ion-tabs>
</ion-toolbar>
</ion-footer>
and the TS File looks like so:
export class AstootLayoutComponent {
#Input() pageName: string;
usersPage: any = UsersPage;
profilePage: any = ProfilePage;
}
Then I have a users page which consumes this component which looks like so:
<astoot-layout [pageName]="pageName">
<ion-content padding>
<page-list-base [detailPageType]="userDetailType" [baseProvider]="baseProvider" [config]="pageListConfiguration"></page-list-base>
</ion-content>
</astoot-layout>
When I attempt to start my application I receive an error:
Maximum call stack size exceeded
TypeError: Cannot read property 'getList' of undefined
at PageListBaseComponent.set [as baseProvider] (http://localhost:8100/build/main.js:115817:21)
at Wrapper_PageListBaseComponent.check_baseProvider (/AppModule/PageListBaseComponent/wrapper.ngfactory.js:38:31)
at CompiledTemplate.proxyViewClass.View_UsersPage0.detectChangesInternal (/AppModule/UsersPage/component.ngfactory.js:80:35)
at CompiledTemplate.proxyViewClass.AppView.detectChanges (http://localhost:8100/build/main.js:111909:14)
at CompiledTemplate.proxyViewClass.DebugAppView.detectChanges (http://localhost:8100/build/main.js:112104:44)
at CompiledTemplate.proxyViewClass.AppView.internalDetectChanges (http://localhost:8100/build/main.js:111894:18)
at CompiledTemplate.proxyViewClass.View_UsersPage_Host0.detectChangesInternal (/AppModule/UsersPage/host.ngfactory.js:29:19)
at CompiledTemplate.proxyViewClass.AppView.detectChanges (http://localhost:8100/build/main.js:111909:14)
at CompiledTemplate.proxyViewClass.DebugAppView.detectChanges (http://localhost:8100/build/main.js:112104:44)
at ViewRef_.detectChanges (http://localhost:8100/build/main.js:77367:20)
at NavControllerBase._viewAttachToDOM (http://localhost:8100/build/main.js:43575:40)
at NavControllerBase._transitionInit (http://localhost:8100/build/main.js:43678:18)
at NavControllerBase._postViewInit (http://localhost:8100/build/main.js:43531:14)
at NavControllerBase._viewTest (http://localhost:8100/build/main.js:43627:25)
at NavControllerBase._nextTrns (http://localhost:8100/build/main.js:43370:25)
Through some investigation the cause seems to be the fact that the AstootLayoutComponent references the Users page, where it resides in. Some how the creates a forever loop.
Why is this happening, the documentation doesn't seem to mention that you can't next this component on the page. How can I fix this?
Bounty Edit
I've created a Repository which replicates my issue
Just as a warning since the tabs controller is in a forever loop, and the api is pointed to github, so you may want to switch that before hitting there rate limit, you can change the url in the Environments.ts
I think you need to add a Tabs Component. Therefore some of the work you were doing in the astoot layout gets moved to this Tabs Component. I opened a PR: https://github.com/granthoff1107/Ionic-Sample-Max-Exceed/pull/1
I tested this and no more stack errors since you no longer have the recursive relationship between the astoot layout and the ion-tabs.
Remove the tabs component from the Astoot Layout Component:
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>
{{pageName}}
</ion-title>
</ion-navbar>
</ion-header>
<ng-content>
</ng-content>
Create a seperate Tabs Component:
<ion-tabs>
<ion-tab [root]="usersPage" tabTitle="Chat" tabIcon="chat"></ion-tab>
<ion-tab [root]="profilePage" tabTitle="Profile" tabIcon="person"></ion-tab>
</ion-tabs>
With a type script file that contains the pages from the astoot layout component.
import { ProfilePage } from './../profile/profile';
import { UsersPage } from './../users/users';
import { Component } from '#angular/core';
#Component({
templateUrl: 'tabs.html'
})
export class TabsPage {
usersPage: any = UsersPage;
profilePage: any = ProfilePage;
constructor() {
}
}
Add your new tabs page the app.module.ts and set your rootPage: any = TabsPage; this will now act as your master page and your content will be transcluded into your view.

getElementById works only the first time

I'm using Ionic 2 and trying to use getElementbyId on
<ion-content padding>
<ion-list id="list"></ion-list>
</ion-content>
so that i can add items the following way:
var listIon=document.getElementById("list");
var li = document.createElement("ion-item");
li.innerText=usern+": "+mensaje;
var p = document.createElement("p");
p.appendChild(li);
listIon.appendChild(p);
It works perfectly fine the first time, but if I exit the page and reopen it I get an error saying that
cannot read property appendChild on null
. I have already read other questions where they say that the script is not at the bottom. By taking in count that in Ionic you don't put the script in the html at all I don't know what could be causing this.
Edit
I have uploaded the files here main problem is in chat-details, chat-details is opened from chat.
What exactly are you trying to do? I think you're thinking in an old Jquery way, but Ionic works on top of Angular2, so things are done in a different way.
I assume you want to append a new item to a list, which can be done by using *ngFor and an array of data. Or if you want to show a message under certain conditions, you can accomplish that by using *ngIf. You should try to avoid referencing the DOM directly, and update the component code instead to change the data shown in the view.
I've created this demo plunker just to show you how to do these things in a Angular2 way. Please let me know what would be the expected result, so I can update the plunker accordingly.
Component code:
#Component({
selector: 'page-home',
templateUrl: 'app/home.page.html'
})
export class HomePage {
private count: number;
public users: Array<string>;
public showMessage: boolean;
public message: string;
constructor() {
this.count = 1;
this.users = [];
this.showMessage = false;
this.message = 'This is a secret message!';
}
public addUser(): void {
this.users.push(`User ${this.count++}`);
}
public toggleMessage(): void {
this.showMessage = !this.showMessage;
}
}
View code:
<ion-header>
<ion-navbar>
<ion-title>HomePage</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h2>Users</h2>
<p *ngIf="showMessage">{{ message }}</p>
<ion-list *ngFor="let user of users">
<ion-item>
<p>{{ user }}</p>
</ion-item>
</ion-list>
<button ion-button (click)="addUser()">Add a user</button>
<button ion-button (click)="toggleMessage()">Togle message</button>
</ion-content>

Loading External URL in Angular2 router-outlet

I am developing an application using Angular2.
I have a component with the following template:
<div>
<router-outlet></router-outlet>
</div>
Can someone please help me how to load an external URL 'www.example.com' in this div?
Just create a component to render inside <ng-outlet> by using the routing config.
Then you component template inside should have an <iframe> pointing to your external site.
import {Component, OnInit} from '#angular/core';
#Component({
selector: 'app-external-page',
templateUrl: './external-page.component.html',
styleUrls: ['./external-page.component.css']
})
export class ExternalPageComponent implements OnInit {
title = 'app works!';
constructor(private _stellarService: StellarService) {
}
ngOnInit(): void {
}
}
Then your component template should look like
<div>
<iframe src="yourexternalpage url"></iframe>
</div>
Having a code like the one above, only remaining step if to configure a route in your routing.
did you get answer for this ?
You can have a component as mentioned here . Import and add it to your NgModule; After that import it in the page you want and use the selector instead of <router-outlet>.

Categories

Resources