Can't bind ngFor after upgrading to Beta 8


#1

I am trying to upgrade from Beta 6 to Beta 8 but can’t get my code to work anymore.

I followed your instructions to upgrade from 6 to 7 and then your instructions for upgrading from 7 to 8. Now when I do:

ionic serve —lab

I get a blank page with the following browser error:


Error: Uncaught (in promise): Template parse errors:
Can't bind to 'ngFor' since it isn't a known native property ("
      </ion-list-header>
      -->
      <button ion-item menuClose [ERROR ->]*ngFor="let p of loggedInPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
"): ConferenceApp@19:33
Can't bind to 'ngForP' since it isn't a known native property ("
      </ion-list-header>
      -->
      <button ion-item menuClose [ERROR ->]*ngFor="let p of loggedInPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
"): ConferenceApp@19:33
Property binding ngFor not used by any directive on an embedded template ("
      </ion-list-header>
      -->
      [ERROR ->]<button ion-item menuClose *ngFor="let p of loggedInPages" (click)="openPage(p)">
        <ion-icon ["): ConferenceApp@19:6
Property binding ngForP not used by any directive on an embedded template ("
      </ion-list-header>
      -->
      [ERROR ->]<button ion-item menuClose *ngFor="let p of loggedInPages" (click)="openPage(p)">
        <ion-icon ["): ConferenceApp@19:6
Can't bind to 'ngFor' since it isn't a known native property ("
      </ion-list-header>
      -->
      <button ion-item menuClose [ERROR ->]*ngFor="let p of loggedOutPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>"): ConferenceApp@31:33
Can't bind to 'ngForP' since it isn't a known native property ("
      </ion-list-header>
      -->
      <button ion-item menuClose [ERROR ->]*ngFor="let p of loggedOutPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>"): ConferenceApp@31:33
Property binding ngFor not used by any directive on an embedded template ("
      </ion-list-header>
      -->
      [ERROR ->]<button ion-item menuClose *ngFor="let p of loggedOutPages" (click)="openPage(p)">
        <ion-icon "): ConferenceApp@31:6
Property binding ngForP not used by any directive on an embedded template ("
      </ion-list-header>
      -->
      [ERROR ->]<button ion-item menuClose *ngFor="let p of loggedOutPages" (click)="openPage(p)">
        <ion-icon "): ConferenceApp@31:6
Can't bind to 'ngFor' since it isn't a known native property ("
      </ion-list-header>
      -->
      <button ion-item menuClose [ERROR ->]*ngFor="let p of appPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
     "): ConferenceApp@42:33
Can't bind to 'ngForP' since it isn't a known native property ("
      </ion-list-header>
      -->
      <button ion-item menuClose [ERROR ->]*ngFor="let p of appPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
     "): ConferenceApp@42:33
Property binding ngFor not used by any directive on an embedded template ("
      </ion-list-header>
      -->
      [ERROR ->]<button ion-item menuClose *ngFor="let p of appPages" (click)="openPage(p)">
        <ion-icon [name]"): ConferenceApp@42:6
Property binding ngForP not used by any directive on an embedded template ("
      </ion-list-header>
      -->
      [ERROR ->]<button ion-item menuClose *ngFor="let p of appPages" (click)="openPage(p)">
        <ion-icon [name]"): ConferenceApp@42:6
Stack trace:
resolvePromise@http://localhost:8100/build/js/zone.js:538:32
resolvePromise@http://localhost:8100/build/js/zone.js:523:18
scheduleResolveOrReject/<@http://localhost:8100/build/js/zone.js:571:18
Zone</ZoneDelegate</ZoneDelegate.prototype.invokeTask@http://localhost:8100/build/js/zone.js:356:24
Zone</Zone</Zone.prototype.runTask@http://localhost:8100/build/js/zone.js:256:29
drainMicroTaskQueue@http://localhost:8100/build/js/zone.js:474:26
ZoneTask/this.invoke@http://localhost:8100/build/js/zone.js:426:22

———————

My package.json looks like this:

{
  "dependencies": {
    "@angular/common": "^2.0.0-rc.1",
    "@angular/compiler": "^2.0.0-rc.1",
    "@angular/core": "^2.0.0-rc.1",
    "@angular/http": "^2.0.0-rc.1",
    "@angular/platform-browser": "^2.0.0-rc.1",
    "@angular/platform-browser-dynamic": "^2.0.0-rc.1",
    "@angular/router": "^2.0.0-rc.1",
    "es6-promise": "3.0.2",
    "es6-shim": "^0.35.0",
    "ionic-angular": "^2.0.0-beta.8",
    "ionic-native": "^1.2.0",
    "ionicons": "3.0.0-alpha.3",
    "iphone-inline-video": "^1.4.0",
    "reflect-metadata": "^0.1.2",
    "rxjs": "^5.0.0-beta.6",
    "zone.js": "^0.6.12"
  },
  "devDependencies": {
    "babel-plugin-transform-decorators-legacy": "1.3.4",
    "babel-preset-es2015": "6.6.0",
    "babelify": "7.2.0",
    "del": "2.2.0",
    "gulp": "3.9.1",
    "gulp-watch": "4.3.5",
    "ionic-gulp-browserify-es2015": "^1.0.0",
    "ionic-gulp-fonts-copy": "^1.0.0",
    "ionic-gulp-html-copy": "^1.0.0",
    "ionic-gulp-sass-build": "^1.0.0",
    "ionic-gulp-scripts-copy": "^2.0.0"
  },
  "cordovaPlugins": [
    "cordova-plugin-device",
    "cordova-plugin-console",
    "cordova-plugin-whitelist",
    "cordova-plugin-splashscreen",
    "cordova-plugin-statusbar",
    "ionic-plugin-keyboard"
  ],
  "cordovaPlatforms": [
    "ios",
    {
      "platform": "ios",
      "version": "",
      "locator": "ios"
    },
    "android"
  ],
  "name": "VeeU",
  "description": "VeeU: Exposing digital media to a wider audience”
}

Any ideas what I need to do to fix this?

EDIT:

Here is the app.html code containing the ngFor:

<!-- left menu -->
<ion-menu [content]="content">

  <ion-toolbar>
    <ion-title>Menu</ion-title>
  </ion-toolbar>

  <ion-content class="outer-content">

    <ion-list>


    <ion-list *ngIf="loggedIn">
      <!--
      <ion-list-header>
        Account
      </ion-list-header>
      -->
      <button ion-item menuClose *ngFor="let p of loggedInPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
        &nbsp;&nbsp;{{p.title}}
      </button>
    </ion-list>

    <ion-list *ngIf="!loggedIn">
      <!--
      <ion-list-header>
        Account
      </ion-list-header>
      -->
      <button ion-item menuClose *ngFor="let p of loggedOutPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
        &nbsp;&nbsp;{{p.title}}
      </button>
    </ion-list>

      <!--
      <ion-list-header>
        Navigate
      </ion-list-header>
      -->
      <button ion-item menuClose *ngFor="let p of appPages" (click)="openPage(p)">
        <ion-icon [name]="p.icon"></ion-icon>
        &nbsp;&nbsp;{{p.title}}
      </button>
    </ion-list>
  </ion-content>

</ion-menu>


<!-- main navigation -->
<ion-nav id="nav" [root]="root" #content swipe-back-enabled="false"></ion-nav>

And here is the associated app.js code:

import {ionicBootstrap, App, Events, Platform, MenuController, Storage, SqlStorage} from 'ionic-angular';
import {File, StatusBar} from 'ionic-native';
import {ViewChild, Component, Inject} from '@angular/core';
import {ConferenceData} from './providers/conference-data';
import {MediaItemData} from './providers/item-data';
import {UserData} from './providers/user-data';
import {TabsPage} from './pages/tabs/tabs';
import {LoginPage} from './pages/login/login';
import {SignupPage} from './pages/signup/signup';
import {TutorialPage} from './pages/tutorial/tutorial';
import {PhotoPage} from './pages/photo/photo';
import {SettingsPage} from './pages/settings/settings';
import {AboutPage} from './pages/about/about';
import {VeeUPage} from './pages/veeu/veeu';
import {MyItemsPage} from './pages/my-items/my-items';
import {BookmarksPage} from './pages/bookmarks/bookmarks';
import {ProfilePage} from './pages/profile/profile';
import {API_ENDPOINT} from '../app_settings';

/* OLD IONIC BETA 6
@App({
  templateUrl: 'build/app.html',
  providers: [ConferenceData, UserData, MediaItemData],
  queries: {
    nav: new ViewChild('content')
  },
  config: {
    platforms: {
      android: {
        tabbarLayout: 'icon-hide'
      }
    }
  }
})
*/

@Component({
  templateUrl: 'build/app.html',
})

export class ConferenceApp {
  static get parameters() {
    return [[App], [Events], [ConferenceData], [UserData], [MediaItemData], [Platform], [MenuController]]
  }

  constructor(app, events, confData, userData, mediaItemData, platform, menu) {
    this.app = app;
    this.userData = userData;
    this.events = events;
    this.menu = menu;
    this.platform = platform;
    this.loggedIn = false;

    this.menu.swipeEnable(true);

    
    /*
    this.platform.ready().then(() => {
      var push = Push.init({
        android: {
          senderID: "12345679"
        },
        ios: {
          alert: "true",
          badge: true,
          sound: 'false'
        },
        windows: {}
      });
    });
    */

    // :PUSH: Onesignal BAAS
    document.addEventListener('deviceready', function () {
      //alert('Device Ready');
      // Enable to debug issues.
      // window.plugins.OneSignal.setLogLevel({logLevel: 4, visualLevel: 4});

      var notificationOpenedCallback = function(jsonData) {
        //alert('didReceiveRemoteNotificationCallBack: ' + JSON.stringify(jsonData));
      };

      window.plugins.OneSignal.init("ad1fbf91-bd63-43a7-ad02-f7739f4b4b7a",
        {googleProjectNumber: "xxxxxx"},
        notificationOpenedCallback);

      // Show an alert box if a notification comes in when the user is in your app.
      window.plugins.OneSignal.enableInAppAlertNotification(true);
    }, false);

    // Load the media item data
    //mediaItemData.loadMediaItems();

    // load the conference data
    //confData.load();

    //alert(API_ENDPOINT);

    // MENU...
    // create a list of pages that can be navigated to from the left menu
    // the left menu only works after login
    // the login page disables the left menu
    this.appPages = [
        /* Tabs version
      { title: 'Schedule', component: TabsPage, icon: 'calendar' },
      { title: 'Speakers', component: TabsPage, index: 1, icon: 'contacts' },
      { title: 'Map', component: TabsPage, index: 2, icon: 'map' },
      { title: 'About', component: TabsPage, index: 3, icon: 'information-circle' },
      */
      { title: 'About', component: AboutPage, index: 3, icon: 'information-circle' }
    ];

    this.loggedInPages = [
      { title: 'VeeU', component: VeeUPage, icon: 'eye' },
      { title: 'Saved', component: BookmarksPage, icon: 'star' },
      { title: 'Submit image', component: PhotoPage, icon: 'add-circle' },
      { title: 'My images', component: MyItemsPage, icon: 'camera' },
      { title: 'Categories', component: SettingsPage, icon: 'settings' },
      { title: 'Profile', component: ProfilePage, icon: 'person' },
      { title: 'Logout', component: TutorialPage, icon: 'log-out' }
    ];

    this.loggedOutPages = [
      { title: 'Login', component: LoginPage, icon: 'log-in' },
      { title: 'Signup', component: SignupPage, icon: 'person-add' }
    ];

    // decide which menu items should be hidden by current login status stored in local storage
    //alert('about to check hasLoggedIn');
    this.userData.hasLoggedIn().then((hasLoggedIn) => {
      this.loggedIn = (hasLoggedIn == 'true');
      //alert('logged in = '+this.loggedIn);
      
      // If logged in show VeeU page else show tutorial page
      if (this.loggedIn) {
        // Setup user ID global
        this.userData.veeuID().then((veeuID) => {
          this.root = VeeUPage;
        });
      }
      else {
        this.root = TutorialPage;
      }

    });

    this.listenToLoginEvents();
  }

  // Called by menu items to open the appropriate page
  openPage(page) {
    this.menu.swipeEnable(true);
    // find the nav component and set what the root page should be
    // reset the nav to remove previous pages and only have this page
    // we wouldn't want the back button to show in this scenario
    //let nav = this.app.getComponent('nav');

    if (page.index) {
      this.nav.setRoot(page.component, {tabIndex: page.index});
    } else {
      this.nav.setRoot(page.component);
    }

    if (page.title === 'Logout') {
      // Give the menu time to close before changing to logged out
      setTimeout(() => {
        this.userData.logout();
      }, 1000);
    }
  }

  listenToLoginEvents() {
    this.events.subscribe('user:login', () => {
      //alert("app.js:[user:login");

      //alert("Login event handler");
      this.loggedIn = true;
      //let nav = this.app.getComponent('nav');
      this.enableMenu(true);
      //this.nav.setRoot(VeeUPage);
    });

    this.events.subscribe('user:signup', () => {
      //alert("app.js:[user:signup");

      // At the moment this event is not used as signup always calls login which creates the user:login event
      this.loggedIn = true;
      //let nav = this.app.getComponent('nav');
      this.enableMenu(true);
      //this.nav.setRoot(ProfilePage);
    });

    this.events.subscribe('user:logout', () => {
      //alert("Logout event handler");
      this.loggedIn = false;
      this.enableMenu(false);
      //let nav = this.app.getComponent('nav');
      this.nav.setRoot(TutorialPage);
    });
  }

  enableMenu(loggedIn) {
    this.menu.enable(loggedIn, "loggedInMenu");
    this.menu.enable(!loggedIn, "loggedOutMenu");
  }
}

ionicBootstrap(ConferenceApp, [ConferenceData, UserData, MediaItemData], {
  platforms: {
    android: {
      tabbarLayout: 'icon-hide'
    }
  }
});

#2

Can you please paste the code it is erroring on? Thanks!


#3

Thanks, I have added the app.html and app.js code


#4

Wow, that is hard to read. ngForP jumps out at me from the error message. Might there be a typo somewhere?


#5

This is the relevant line I can’t see a typo.


#6

I have just noticed that the WWW folder is not getting built any more. The app.bundle.js is not getting updated when I do ionic serve or ionic build. Any ideas why this might be?


#7

Seems like I have to run gulp build now to do a build?


#8

Did you run npm install after the upgrade? Looking at the error messages I would say that you’re trying to use the new syntax (i.e. *ngFor="let p of loggedInPages") in an older release of Angular 2 which needs the previous syntax (i.e. *ngFor="#p of loggedInPages"). Also make sure that you don’t have ionic serve running during the upgrade (or at least restart it after that) - see the linked post for details:


#9

I did a reinstall and it works now thanks.