Ion-header overlays ion-content when ion-content is *ngIf?

I’ve got a pretty basic doohickey for displaying page contents.

<ion-header>
  <ion-navbar>
   <!-- stuff -->
  </ion-navbar>
</ion-header>

<ion-content padding-top no-bounce  *ngIf="data$ | async as data">
  <ion-list-header (tap)="activate('title-body')">
    TITLE &amp; CONTENT
    <button ion-button item-right (tap)="publish()">
      <!-- <ion-icon name="send"></ion-icon> -->
      publish
    </button>
  </ion-list-header>
  <div class="title-body" *ngIf="activeSection === 'title-body'">
    <item-editor [model]="item"></item-editor>
  </div>

This does two things: prevents the page from rendering content until there’s something to display, and puts all the async data into a sync-access alias. That way I don’t have a lot of |async peppered throughout the rest of the page.

The problem is that when I ngIf the ion-content, the stuff in it underlays the ion-header. If I take off the ngIf, it properly has a padding.

My inituition is that the header is looking for siblings when it is created to add padding appropriately, but that doesn’t exist yet.

apologize - got caught in some weird keyboard eventing thing and published the post before I was done typing.

anyway: does anyone have problems with getting ion-content to render scroll areas correctly when it is conditionally rendered, and relatedly do you have a solution? I’ll probably see if I can do a workaround with ng-content somewhere higher up on the DOM tree.

Yes, i’ll reply to your question simply:

  1. *ngIf=“data$ | async as data” is a very good idea, but you should not use it on the main element of html (ion-content). In general it just don’t work, this doesn’t tell the controller where to display your data in html.
  2. instead try to split your template with ion-list/ion-item and use *ngIf() on ion-item tags (you can wrap ion-lists and ion-items almost undefinitely).

For example what i do in my app, looks like this (i use segments and tabs too):

  • ion-content
    – ion-list
    — ion-item (with ngIf*)
    — ion-item (with or without ngIf*)
    – next ion-list
    — next ion-item etc…
    – ion-list end (auto generated)
  • ion-content end (auto generated)

To show you a complex example, I use something like that in my production app, with a lot of ngIf():

<ion-content>

  <ion-list>

    <ion-card>
      <ion-list>

          <ion-item>
            <ion-label>Text</ion-label>
            <ion-select okText="Ok" cancelText="Annuler" [(ngModel)]="customerFullName" required>
                  <ion-option *ngFor="let XXXX of XXXX | async" value="{{customer?.first_name}} {{customer?.last_name}} {{customer?.$key}}">{{...}}</ion-option>
            </ion-select>
          </ion-item>

      </ion-list>
    </ion-card>

</ion-list>

etc...

</ion-content>

Hope it gives you some insights :slight_smile:

Thanks.

Further basic investigation (just putting a boolean ngIf=“shouldShow” and a toggle on the nav) indicates that the scroll padding is only added at first.

I can just wrap the entire inside with an ng-container with an ng-if on it, so

<ion-header></ion-header>
<ion-content>
  <ng-container *ngIf="data$ | async as data">
    <!-- stuff here -->
  </ng-container>
</ion-content>

works okay. The problem I want to avoid is having too many |async items there, since they’re all displaying the same data anyway, there’s no particular reason to have like fourteen different listeners on the same piece of data.

as an aside, it is possible to build scrollable interfaces with sticky top navs that don’t rely on scroll padding (which also have an unsatisfying coverage of the scrollbar towards the top) by taking advantage of relative / absolute placement, instead of using fixed placement stuff. Top-padding for scroll overlays, while probably more broadly compatible, is a pretty brittle rendering solution. I did this for a different job I had, where we did an in-house interface library in angular 1, and it worked pretty well.

Then again, I’m not rushing to emit a PR on this issue either.

Well I thank you for the tip, my goal was just to tell you ngIf() don’t work on <ion-content> element, and your conclusion seems to agree with me.

As for the rest, I’m not sure I understand what you want to achieve in HTML DOM. I’m not sure, but again, if <ion-content> don’t contain your data with ion-list like i updated above, I’m pretty sure your data from any database will be lost in the front end processing of ionic.

Note: I updated my above answer with real working code, you might want to check it out to see html structure (tested on ios and android).

But I don’t have all the answers, I’d be glad to read your tests, please reply :wink:

Not that anybody need care about my opinion, but I don’t like the async pipe idiom. I think templates should not have to care about how their backing properties are populated, and all of that should be taken care of in the controller.

Yeah, it’s not my favorite either. It’s just that using the async pipe takes care of a fair bit of boilerplate - otherwise you have to remember to unsubscribe on ngOnDestroy if nothing else.