ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
51.08k stars 13.51k forks source link

bug: Angular standalone - ToastController does not present toast in production build for iOs #28626

Closed PatrikHorvatic closed 11 months ago

PatrikHorvatic commented 11 months ago

Prerequisites

Ionic Framework Version

v7.x

Current Behavior

Hello.

When building the app for production, ToastController never displays toast in interface or throws any kind of error. I have created 2 videos, one displays production build and other development build.

Application is built in latest version of XCode. I am using Angular standalone components and newest Angular 17 version with Vite build system.

I have created videos for both build options, they are attached here. In login page after tapping the login button, spinner is displayed and with deliberatly false login data it dissapears and Toast should appear from top of the screen with text: "Invalid login data".


iOs development build. This build has expected behaviour and works properly.

https://github.com/ionic-team/ionic-framework/assets/80419964/d4e306ef-98a3-4d52-9e57-f7b9523450df


iOs production build This build does not have expected behaviour as toast does not appear fro mtop of the screen as seen in previous video.

https://github.com/ionic-team/ionic-framework/assets/80419964/1b6272ac-34cd-477a-be1b-3a8d5e182d9d

Expected Behavior

It is expected to display toast message in both production and development build.

Steps to Reproduce

Prepared code for the page is not equal to ones in the videos above. I have written minimal code required for bug reproduction. I have added basic button, method for event listener and basic setup of Angular NgModule based app.

1. Create InfoService InfoService contains all methods neccessery.

import { Injectable } from '@angular/core';
import { ModalController, ModalOptions, ToastController } from '@ionic/angular';

/**Servis za prikazivanje obavijesti korisnika. */
@Injectable({
  providedIn: 'root'
})
export class InfoService {

  constructor(private toastCtrl: ToastController,
    private modalCtrl: ModalController) {
  }

  /**Prikazuje Toast poruku s vrha ekrana. */
  public async presentToast(mess: string, pozicija: 'top' | 'bottom' | 'middle' = 'top', duration = 2500) {
    try {
      const toast = await this.toastCtrl.create({
        message: mess,
        duration: duration,
        position: pozicija,
        mode: 'ios',
        buttons: [{
          text: 'Ok',
          role: 'cancel'
        }
        ],
        id: 'TOST'
      });
      toast.present();
    } catch (error) {
      console.log(error);
    }

  }

  public async prepareModal(options: ModalOptions) {
    return this.modalCtrl.create(options);
  }

  public async dismissModal() {
    this.modalCtrl.dismiss();
  }

}

2. Import standalone components into AppModule

@NgModule({
  declarations: [AppComponent],
  imports: [],
  providers: [
    HttpClientModule,
    provideIonicAngular({
      mode: 'ios',
      rippleEffect: false,
      sanitizerEnabled: true,
      hardwareBackButton: false,
      statusTap: true
    }),
    InfoService],
  bootstrap: [AppComponent]
})
export class AppModule { }

3. Create a page with NgModule

import { NgModule } from '@angular/core';
import { IonButton, IonContent } from '@ionic/angular/standalone';
import { LoginPage } from './login.page';

@NgModule({
  imports: [IonContent, IonButton],
  declarations: [LoginPage],
  exports: [LoginPage]
})
export class LoginPageModule { }

4. Add basic code to page TS code.

@Component({
  selector: 'app-login',
  templateUrl: './login.page.html',
  styleUrls: ['./login.page.scss']
})
export class LoginPage implements OnInit {
  constructor(private router: Router,
    private info: InfoService) {
  }

  public LogIn(){
    setTimeout(async () => {
       await this.info.presentToast('Invalid login data');
    }, 500);
  }

}

5. In that page add basic button with click listener.

<ion-content>
    <ion-button (click)="LogIn()" expand="block" fill="solid" class="button-common"
      style="width: 100%; margin: 0px; color: white !important">
      Log in
    </ion-button>
</ion-content>

6. Toast should appear after 500ms delay.

It is all working properly on web.

7. Build the application with following command: ionic capacitor build ios After successfull build, run the app on real device. Tapping on button should present toasts message.

8. Build the application with following command: ionic capacitor build ios --prod After successfull build, run the app on real device. Tapping on button should NOT present toasts message.

Code Reproduction URL

No response

Ionic Info

Ionic:

Ionic CLI : 7.1.1 (C:\Users\patrik.horvatic\AppData\Roaming\npm\node_modules\@ionic\cli) Ionic Framework : @ionic/angular 7.5.7 @angular-devkit/build-angular : 17.0.5 @angular-devkit/schematics : 16.2.9 @angular/cli : 17.0.5 @ionic/angular-toolkit : 10.0.0

Capacitor:

Capacitor CLI : 5.5.1 @capacitor/android : 5.5.1 @capacitor/core : 5.5.1 @capacitor/ios : 5.5.1

Utility:

cordova-res : 0.15.4 native-run : 1.7.3

System:

NodeJS : v20.9.0 (C:\Program Files\nodejs\node.exe) npm : 10.2.4 OS : Windows 10

I am also using macOs with same version of NodeJS and npm

Additional Information

Full angular.json file

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "app": {
      "schematics": {
        "@schematics/angular:component": {
          "skipTests": true
        },
        "@schematics/angular:directive": {
          "skipTests": true
        },
        "@schematics/angular:pipe": {
          "skipTests": true
        },
        "@schematics/angular:service": {
          "skipTests": true
        }
      },
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "outputPath": "www",
            "index": "src/index.html",
            "polyfills": [
              "src/polyfills.ts"
            ],
            "tsConfig": "tsconfig.app.json",
            "webWorkerTsConfig": "tsconfig.worker.json",
            "assets": [
              {
                "glob": "**/*",
                "input": "src/assets",
                "output": "assets"
              },
              {
                "glob": "**/*",
                "input": "./node_modules/@kolkov/angular-editor/assets/",
                "output": "./assets/fonts/"
              },
              "src/manifest.webmanifest"
            ],
            "styles": [
              "src/theme/ad-features.scss",
              "src/theme/forms-ad-small-cards-and-filters.scss",
              "src/theme/variables.scss",
              "src/theme/navigation.scss",
              "src/theme/verify-pages.scss",
              "src/theme/fonts-colors.scss",
              "src/theme/animations.scss",
              "src/theme/responsive.scss",
              "src/theme/side-menu-and-registration-pages.scss",
              "node_modules/keen-slider/keen-slider.min.css",
              "node_modules/@videogular/ngx-videogular/fonts/videogular.css",
              "src/global.scss"
            ],
            "scripts": [
              "node_modules/hls.js/dist/hls.min.js"
            ],
            "aot": false,
            "extractLicenses": false,
            "sourceMap": true,
            "optimization": false,
            "preserveSymlinks": true,
            "namedChunks": false,
            "outputHashing": "none",
            "browser": "src/main.ts"
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": {
                "fonts": true,
                "scripts": true,
                "styles": {
                  "minify": true,
                  "inlineCritical": false
                }
              },
              "webWorkerTsConfig": "tsconfig.worker.json",
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "statsJson": true,
              "aot": true,
              "progress": true,
              "extractLicenses": false,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "4mb",
                  "maximumError": "18mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "1mb",
                  "maximumError": "15mb"
                }
              ]
            },
            "ci": {
              "progress": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "buildTarget": "app:build"
          },
          "configurations": {
            "production": {
              "buildTarget": "app:build:production"
            },
            "ci": {}
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "buildTarget": "app:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "styles": [],
            "scripts": [],
            "assets": [
              {
                "glob": "favicon.ico",
                "input": "src/",
                "output": "/"
              },
              {
                "glob": "**/*",
                "input": "src/assets",
                "output": "/assets"
              },
              "src/manifest.webmanifest"
            ]
          },
          "configurations": {
            "ci": {
              "progress": false,
              "watch": false
            }
          }
        },
        "lint": {
          "builder": "@angular-eslint/builder:lint",
          "options": {
            "lintFilePatterns": [
              "src/**/*.ts",
              "src/**/*.html"
            ]
          }
        },
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "app:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "app:serve:production"
            },
            "ci": {
              "devServerTarget": "app:serve:ci"
            }
          }
        }
      }
    }
  },
  "cli": {
    "schematicCollections": [
      "@ionic/angular-toolkit"
    ],
    "packageManager": "npm",
    "analytics": false
  },
  "schematics": {
    "@schematics/angular:component": {
      "skipTests": true
    },
    "@schematics/angular:page": {
      "skipTests": true
    },
    "@schematics/angular:pipe": {
      "skipTests": true
    },
    "@schematics/angular:service": {
      "skipTests": true
    },
    "@schematics/angular:directive": {
      "skipTests": true
    },
    "@ionic/angular-toolkit:component": {
      "styleext": "scss",
      "skipTests": true
    },
    "@ionic/angular-toolkit:page": {
      "styleext": "scss",
      "skipTests": true
    },
    "@ionic/angular-toolkit:pipe": {
      "skipTests": true
    },
    "@ionic/angular-toolkit:service": {
      "skipTests": true
    },
    "@ionic/angular-toolkit:directive": {
      "skipTests": true
    }
  }
}
liamdebeasi commented 11 months ago

Thanks for the issue. I am going to close this as this is not a bug in Ionic Framework. The problem here is you are importing from both @ionic/angular and @ionic/angular/standalone. When using Angular standalone components you should not import from @ionic/angular as noted in https://ionicframework.com/docs/angular/build-options#usage-with-standalone-based-applications.

Updating your toast import to import from @ionic/angular/standalone should fix the issue.

ionitron-bot[bot] commented 10 months ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.