Quantcast
Channel: Ionic Framework - Ionic Forum
Viewing all 49381 articles
Browse latest View live

Manually trigger click on ion-button using vitest

$
0
0

I’m trying to unit test this component.

<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <ion-input
        label="Email"
        data-testid="email-input"
        name="email"
        label-placement="floating"
        fill="solid"
        v-model="form.email"
        type="email"
        required
      />
    </div>

    <div>
      <label for="username">Username</label>
      <input
        id="username"
        name="username"
        v-model="form.username"
        type="text"
        required
      />
    </div>

    <div>
      <label for="password">Password</label>
      <input
        id="password"
        name="password"
        v-model="form.password"
        type="password"
        required
      />
    </div>

    <ion-button type="submit" :disabled="isSubmitting">
      {{ isSubmitting ? "Saving..." : "Submit" }}
    </ion-button>
  </form>
</template>

<script setup lang="ts">
import { IonInput, IonButton } from "@ionic/vue";
import { reactive, ref, watch } from "vue";

interface UserFormData {
  email: string;
  username: string;
  password: string;
}

const props = defineProps<{
  user?: Partial<UserFormData>;
  saving: boolean;
}>();

const emit = defineEmits<{
  (e: "submit", data: UserFormData): void;
}>();

const defaultUser: UserFormData = {
  email: "",
  username: "",
  password: "",
};

const initialState = (): UserFormData => ({
  ...defaultUser,
  ...props.user,
});

const form = reactive<UserFormData>(initialState());
const isSubmitting = ref(false);

function handleSubmit() {
  isSubmitting.value = true;
  emit("submit", { ...form });
}

function resetForm() {
  Object.assign(form, initialState());
}

watch(
  () => props.saving,
  (newVal, oldVal) => {
    if (oldVal && !newVal) {
      isSubmitting.value = false;
    }
  }
);

watch(
  () => props.user,
  () => {
    Object.assign(form, initialState());
  }
);

defineExpose({ resetForm });
</script>

For some reason, the click event is not triggering on the ion-button:

import { mount } from "@vue/test-utils";
import { describe, expect, it } from "vitest";
import UserForm from "@/components/UserForm.vue";

describe("UserForm.vue", () => {
  it("submits the form", async () => {
    const wrapper = mount(UserForm, { props: { saving: false }, attachTo: document.body });
    await wrapper.findComponent("[data-testid=email-input]").setValue("email@example.com");
    await wrapper.find("input[name=username]").setValue("username");
    await wrapper.find("input[name=password]").setValue("passwordf");
    const submitButton = wrapper.findComponent("ion-button");
    await submitButton.trigger("click");

    expect(wrapper.emitted()).toHaveProperty("submit");
    expect(submitButton.attributes("disabled")).toBeDefined();
    wrapper.unmount();
  });
});

expected { input: [ [ …(1) ], [ …(1) ] ], …(2) } to have property "submit"

Using the native <button> the test passes, but with <ion-button> it fails.

How do I properly manually trigger the click event?

3 posts - 2 participants

Read full topic


Ionic build --prod randomly generates erroneous builds

$
0
0

Ionic build --prod randomly generates erroneos builds

it does not fail when compiling, but it results in app not working properly

I attach an screenshot of a messed menu, with repeated words

other times it gets stuck on dashboard when the compilation comes messed

a mate told me he believes the compilation is messing with the file permissions and that’s why that’s happening

packages.json >>

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "lint": "ng lint"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^18.2.11",
    "@angular/cdk": "^18.2.13",
    "@angular/common": "^18.2.11",
    "@angular/compiler": "^18.2.11",
    "@angular/core": "^18.2.11",
    "@angular/forms": "^18.2.11",
    "@angular/material": "^18.2.13",
    "@angular/platform-browser": "^18.2.11",
    "@angular/platform-browser-dynamic": "^18.2.11",
    "@angular/router": "^18.2.11",
    "@angular/service-worker": "^18.2.11",
    "@capacitor/android": "7.2.0",
    "@capacitor/app": "7.0.1",
    "@capacitor/browser": "7.0.1",
    "@capacitor/camera": "7.0.1",
    "@capacitor/core": "7.2.0",
    "@capacitor/filesystem": "7.0.1",
    "@capacitor/haptics": "7.0.1",
    "@capacitor/ios": "7.2.0",
    "@capacitor/keyboard": "^7.0.1",
    "@capacitor/status-bar": "7.0.1",
    "@devexpress/analytics-core": "^24.2.7",
    "@fortawesome/angular-fontawesome": "^0.14.1",
    "@fortawesome/pro-duotone-svg-icons": "^6.7.2",
    "@fortawesome/pro-light-svg-icons": "^6.7.2",
    "@fortawesome/pro-solid-svg-icons": "^6.7.2",
    "@ionic-native/deeplinks": "^5.36.0",
    "@ionic/angular-server": "^8.5.8",
    "@ionic/core": "^8.5.8",
    "@ionic/pwa-elements": "^3.3.0",
    "@ionic/storage-angular": "^4.0.0",
    "@ng-bootstrap/ng-bootstrap": "^18.0.0",
    "@ngneat/input-mask": "^6.1.0",
    "@ngx-translate/core": "^16.0.4",
    "@ngx-translate/http-loader": "^16.0.1",
    "@popperjs/core": "^2.11.8",
    "@pqina/angular-pintura": "^9.0.4",
    "@pqina/filepond-plugin-image-editor": "^9.x",
    "@pqina/pintura": "^8.92.16",
    "@sweetalert2/ngx-sweetalert2": "^13.0.0",
    "@viablelogic/ngx-signature-pad": "^12.0.2",
    "angularx-qrcode": "^19.0.0",
    "bootstrap": "^5.3.6",
    "bootstrap-icons": "^1.13.1",
    "chart.js": "4.4.9",
    "cordova-plugin-deeplinks": "^1.1.1",
    "devexpress-dashboard": "24.2.6",
    "devexpress-dashboard-angular": "24.2.6",
    "devextreme": "24.2.6",
    "devextreme-angular": "24.2.6",
    "ionicons": "^8.0.8",
    "ng-inline-svg-2": "^15.0.1",
    "ng2-pdf-viewer": "^10.4.0",
    "ngx-webcam": "^0.4.1",
    "rxjs": "~7.8.2",
    "sweetalert2": "^11.22.0",
    "tslib": "^2.8.1",
    "zone.js": "^0.15.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^18.2.11",
    "@angular-eslint/builder": "^18.4.0",
    "@angular-eslint/eslint-plugin": "^18.4.0",
    "@angular-eslint/eslint-plugin-template": "^18.4.0",
    "@angular-eslint/schematics": "^18.4.0",
    "@angular-eslint/template-parser": "^18.4.0",
    "@angular/cli": "^18.2.11",
    "@angular/compiler-cli": "^18.2.11",
    "@angular/language-service": "^18.2.11",
    "@angular/localize": "^18.2.11",
    "@capacitor/assets": "^3.0.5",
    "@capacitor/cli": "7.2.0",
    "@ionic/angular": "^8.5.8",
    "@ionic/angular-toolkit": "12.2.0",
    "@types/bootstrap": "^5.2.10",
    "@types/node": "^22.10.1",
    "@typescript-eslint/eslint-plugin": "^8.16.0",
    "@typescript-eslint/parser": "^8.16.0",
    "eslint": "^9.16.0",
    "eslint-plugin-import": "^2.31.0",
    "eslint-plugin-jsdoc": "^50.6.9",
    "eslint-plugin-prefer-arrow": "^1.2.3",
    "mini-css-extract-plugin": "^2.9.2",
    "sass": "^1.81.0",
    "sass-loader": "^16.0.5",
    "style-loader": "^4.0.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.5.4",
    "webpack": "^5.96.1",
    "webpack-cli": "^5.1.4"
  },
  "browserslist": [
    "Chrome >=61",
    "ChromeAndroid >=61",
    "Firefox >=63",
    "Firefox ESR",
    "Edge >=79",
    "Safari >=13",
    "iOS >=13"
  ]

angular.json >>>

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "app": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "www",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": ["src/polyfills.ts", "zone.js"],
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              {
                "glob": "**/*",
                "input": "src/assets",
                "output": "assets"
              },
              {
                "glob": "**/*.svg",
                "input": "node_modules/ionicons/dist/ionicons/svg",
                "output": "./svg"
              },
              {
                "glob": "**/*",
                "input": "src/assets/img",
                "output": "/img"
              },
              "src/favicon.ico"
            ],
            "styles": [
              "src/global.scss",
              "src/theme/variables.scss",
              "./node_modules/@pqina/pintura/pintura.css"  
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "4mb",
                  "maximumError": "10mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "4kb",
                  "maximumError": "8kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "outputHashing": "all",
              "allowedCommonJsDependencies": [
                "localforage",
                "ace-builds",
                "inputmask",
                "jquery",
                "pdfjs-dist",
                "sweetalert2"
              ]
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true,
              "allowedCommonJsDependencies": [
                "localforage",
                "ace-builds",
                "inputmask",
                "jquery",
                "pdfjs-dist",
                "sweetalert2"
              ]
            },
            "ci": {
              "progress": false
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "buildTarget": "app:build:production"
            },
            "development": {
              "buildTarget": "app:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "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", "zone.js", "zone.js/testing"],
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "inlineStyleLanguage": "scss",
            "assets": [
              {
                "glob": "**/*",
                "input": "src/assets",
                "output": "assets"
              },
              {
                "glob": "**/*.svg",
                "input": "node_modules/ionicons/dist/ionicons/svg",
                "output": "./svg"
              },
              {
                "glob": "**/*",
                "input": "src/assets/img",
                "output": "/img"
              },
              "src/favicon.ico"
            ],
            "styles": [
              "src/global.scss",
              "src/theme/variables.scss",
              "src/styles.css"
            ],
            "scripts": []
          },
          "configurations": {
            "ci": {
              "progress": false,
              "watch": false
            }
          }
        },
        "lint": {
          "builder": "@angular-eslint/builder:lint",
          "options": {
            "lintFilePatterns": [
              "src/**/*.ts",
              "src/**/*.html"
            ]
          }
        }
      }
    }
  },
  "cli": {
    "schematicCollections": [
      "@ionic/angular-toolkit"
    ],
    "analytics": false,
    "packageManager": "npm"
  },
  "schematics": {
    "@ionic/angular-toolkit:component": {
      "styleext": "scss"
    },
    "@ionic/angular-toolkit:page": {
      "styleext": "scss"
    }
  }
}

Screenshot with messed menu (one of those randomly messed compilations) >>

1 post - 1 participant

Read full topic

Azure AD Redirect URI Issue for iOS with Ionic: AADSTS50011 Error

$
0
0

Hi,

I have an SSO app registered in Azure AD. The ionic-angular app works correctly on Android and desktop web platforms. However, I encounter an error when using the iOS app:

AADSTS50011: The redirect URI ionic://localhost/ specified in the request does not match the redirect URIs configured for the application.

This issue is specific to iOS. The redirect URI used is ionic://localhost/ (or capacitor://localhost when using Capacitor), as described in the Ionic documentation.

Unfortunately, Azure AD does not allow registering ionic://localhost/ as a valid redirect URI, since it only accepts URIs that begin with https:// or http://localhost. Redirect URI (reply URL) best practices and limitations - Microsoft identity platform | Microsoft Learn

Is there any supported way to change ionic://localhost or capacitor://localhost to use http://localhost on iOS?

Thank you!

2 posts - 2 participants

Read full topic

Capacitor plugin filesystem / mediastore access

$
0
0

congratulation to me lunch my very first github / capacitor plugin

A Capacitor plugin that provides comprehensive access to Android MediaStore API for media file access and metadata retrieval. This plugin is specifically designed to overcome the limitations of Capacitor’s filesystem API, particularly for accessing SD card storage and retrieving rich media metadata.

website - Capacitor MediaStore Plugin - Easy Media Access for Android
github - GitHub - odion-cloud/capacitor-mediastore: A Capacitor plugin that provides comprehensive access to Android MediaStore API for media file access and metadata retrieval. This plugin is specifically designed to overcome the limitations of Capacitor's filesystem API, particularly for accessing SD card storage and retrieving rich media metadata.
npm - @odion-cloud/capacitor-mediastore - npm

1 post - 1 participant

Read full topic

Connect Sqlite and create a table

$
0
0

Continuing the discussion from In my angular ionic framework to connect sqlite but jeep-sqlite not present in dom error showing:

my sqlite service

import { Injectable } from ‘@angular/core’;
import { Capacitor } from ‘@capacitor/core’;
import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection } from ‘@capacitor-community/sqlite’;

@Injectable({
providedIn: ‘root’
})
export class SqliteService {
private sqlite: SQLiteConnection;
private db: SQLiteDBConnection | null = null;

constructor() {
this.sqlite = new SQLiteConnection(CapacitorSQLite);
}

async init() {
try {
console.log(‘Checking CapacitorSQLite…’);
console.log(‘CapacitorSQLite:’, CapacitorSQLite);
console.log(‘Checking IndexedDB support:’, !!window.indexedDB);

if (Capacitor.getPlatform() === 'web') {
  console.log('Running on web, initializing web store...');
  try {
    console.log("tryblock")
   await this.sqlite.initWebStore();
    console.log('Web store initialized successfully');
  } catch (webInitErr) {
    console.error('Error during initWebStore:', webInitErr);
  }
}

console.log(‘Before creating connection…’);

this.db = await this.sqlite.createConnection('testdb', false, 'no-encryption', 1, false);
const isOpen = await this.db.isDBOpen();

console.log(‘DB is open?’, isOpen);

await this.db.open();
console.log(' creating connection...');

console.log('DB connection opened');

await this.db.execute(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY,
    name TEXT
  )
`);
console.log('Table created or exists');

} catch (err) {
console.error(‘SQLite init error:’, err);
}
}

async addUser(name: string) {
if (!this.db) {
console.error(‘DB not initialized’);
return;
}
const stmt = INSERT INTO users (name) VALUES (?);
const result = await this.db.run(stmt, [name]);
console.log(‘Insert Result:’, result);
}

async getUsers() {
if (!this.db) return ;
const result = await this.db.query(‘SELECT * FROM users’);
return result.values ?? ;
}
}
appcomponent.ts

import { Component } from ‘@angular/core’;
import { Platform } from ‘@ionic/angular’;
import { SqliteService } from ‘./services/sqlite.service’;

@Component({
selector: ‘app-root’,
templateUrl: ‘app.component.html’,
styleUrls: [‘app.component.scss’],
standalone: false,
})
export class AppComponent {
constructor(private platform: Platform, private sqliteService: SqliteService) {
this.platform.ready().then(async () => {
console.log(‘Platform is ready!’);
await this.sqliteService.init();
console.log(‘SQLite service initialized!’);
await this.addUser();
});
}
async addUser() {
await this.sqliteService.addUser(‘John’);
const users = await this.sqliteService.getUsers();
console.log(users);
}
}
main.ts

import { platformBrowserDynamic } from ‘@angular/platform-browser-dynamic’;
import { AppModule } from ‘./app/app.module’;

platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
connect sqlite time jeep-sqlite not present in Dom message showing table not created

2 posts - 2 participants

Read full topic

Prakash Hinduja Switzerland (Swiss) Building an App with Ionic

$
0
0

Hi everyone,

I’m Prakash Hinduja from geneva, switzerland (swiss), and I’ve been working as a consultant and advisor for mobile app development. I’m getting started with Ionic Framework and looking to build my first app. I wanted to reach out to the community to get some suggestions and advice.

Regards
Prakash Hinduja Geneva, Switzerland (Swiss)

2 posts - 2 participants

Read full topic

Shared elements transition with angular

$
0
0

Hi there, i currently stuck on a shared elements transition between a page and a modal. I want to get something like the instagram animation:
a list/grid of images, click and the image is getting bigger with the modal.

Has anybody done this with angular? I just find react solutions.

1 post - 1 participant

Read full topic

Using it since 1 hour, already problems: Icons not showing

$
0
0

I usually am against frameworks in general, except a strict few, angular has been forced upon me, let alone ionic. These “I do everything” framework are usually horrendous, and I did the error to let this one Ionic slips once more in my developement routine by suggestion of coworkers.
Another mistake that will cost me hours of hair pulling. How this forum is poorly build should have been a big enough red flag, but I’m stubborn and blind and tend to follow advices only to end up in despair.

Meh… let’s begin.

I literally generated a new ionic sidebar app, then a component with ionic generate page command.

The angular template basically loops over an array of “pages” to build a side menu:

  @for (p of appPages; track p; let i = $index) {
            <ion-menu-toggle auto-hide="false">
              <ion-item routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false" routerLinkActive="selected">
                <ion-icon aria-hidden="true" slot="start" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon>
                <ion-label>{{ p.title }}</ion-label>
              </ion-item>
            </ion-menu-toggle>
          }

defined in the main component:

  public appPages = [
    { title: 'Chart Test', url: '/charttest', icon: 'bar-chart' },
    { title: 'Inbox', url: '/folder/inbox', icon: 'mail' },
    { title: 'Outbox', url: '/folder/outbox', icon: 'paper-plane' },
    { title: 'Favorites', url: '/folder/favorites', icon: 'heart' },

on which I added the “Chart test” route/menu.
I have then visited https://ionic.io/ionicons to see icon names (note that templates att -outline or -sharp to it) and found “bar-chart” to be the one I need.

SURPRISE SURPRISE!!!
Not a single damn icon shows!!!

On the forementioned site we have

<ion-icon name="bar-chart-sharp"></ion-icon>
<ion-icon name="bar-chart-outline"></ion-icon>
<ion-icon name="bar-chart"></ion-icon>

I also copied all the same ionic imports from the working pages (where icons show) on the new chart test component

Yes, I tried 3 different browser, even in incognito mode. Even on different machines.

Jesus… why I’m even writing this… this thing breaks at the most basic stuff… :smiling_face_with_tear: I don’t want to imagine what happens when I build a multi platform release

1 post - 1 participant

Read full topic


Can I cancel or reverse a wrong recharge on Jio? Jio

$
0
0

Yes, you can request a cancellation or refund for a wrong recharge on Jio. Here are the steps you can follow:

Contact Customer Care: Call Jio customer service at 199 or 091-7811-6758 use the MyJio app to chat with support.

Provide Details: Explain the situation and provide details like your mobile number, recharge amount, and the date of the transaction.

Follow Up: If your request is not resolved immediately, follow up with customer care.

Check Policies: Jio has specific policies regarding refunds, so check their official website for any relevant terms.

Refunds are typically processed based on their policy, so results may vary.

1 post - 1 participant

Read full topic

How to Create a Quiz Game App Using Ionic Framework – Step-by-Step Guide

$
0
0

If you’re planning to build a cross-platform quiz game app, the Ionic Framework is one of the best choices due to its flexibility, performance, and native-like UI capabilities. Below is a step-by-step tutorial to help you get started.

Step 1: Install Prerequisites

Before you begin, ensure the following tools are installed on your system:

bash

CopyEdit

npm install -g @ionic/cli
  • Code Editor (e.g., VS Code)

Step 2: Create a New Ionic Project

Run the following command to generate a new blank Ionic app:

bash

CopyEdit

ionic start quizGameApp blank --type=angular

Use Angular for better structure and scalability in your quiz logic.

Change to your project folder:

bash

CopyEdit

cd quizGameApp

Step 3: Design the Quiz UI

Create essential pages:

  • Home Page – Start quiz
  • Quiz Page – Display questions/options
  • Result Page – Show score/result

Use this command to generate pages:

bash

CopyEdit

ionic generate page pages/home
ionic generate page pages/quiz
ionic generate page pages/result

Design the UI using Ionic components like:

html

CopyEdit

<ion-card>
  <ion-card-header>
    <ion-card-title>{{ question.text }}</ion-card-title>
  </ion-card-header>
  <ion-card-content>
    <ion-button expand="block" *ngFor="let option of question.options" (click)="selectOption(option)">
      {{ option }}
    </ion-button>
  </ion-card-content>
</ion-card>

Step 4: Add Quiz Logic (TypeScript)

Create a quiz.service.ts to store and fetch quiz data:

typescript

CopyEdit

@Injectable({
  providedIn: 'root'
})
export class QuizService {
  private questions = [
    {
      text: 'What is the capital of France?',
      options: ['Paris', 'London', 'Berlin', 'Rome'],
      answer: 'Paris'
    },
    // Add more questions...
  ];

  getQuestions() {
    return this.questions;
  }
}

In the quiz.page.ts, implement the logic to:

  • Load questions
  • Handle answer selection
  • Calculate score

Step 5: Store Score and Results

Use Angular’s services to share the quiz score between pages:

typescript

CopyEdit

export class ResultService {
  private score: number = 0;

  setScore(value: number) {
    this.score = value;
  }

  getScore(): number {
    return this.score;
  }
}

Step 6: Add Navigation

Use Ionic Router for navigation between pages:

typescript

CopyEdit

this.router.navigate(['/quiz']);

typescript

CopyEdit

this.router.navigate(['/result']);

Update app-routing.module.ts to include your new pages.


Step 7: Add Backend (Optional)

To store user scores, leaderboard, or custom questions:

  • Use Firebase or Node.js + MongoDB
  • Integrate using REST API or Firebase SDK

Step 8: Test Your App

Use the Ionic DevApp or browser to test:

bash

CopyEdit

ionic serve

To test on real devices:

bash

CopyEdit

ionic capacitor add android
ionic capacitor run android

Step 9: Build and Deploy

To create a build for Android/iOS:

bash

CopyEdit

ionic build
ionic capacitor copy android
ionic capacitor open android

Use Android Studio/Xcode to finalize deployment.


Final Thoughts

Creating a quiz game app using the Ionic Framework allows you to deliver a native-like experience across platforms with one codebase. If you want expert help in building your app, you can consult top-tier developers.

1 post - 1 participant

Read full topic

Auto-fill credential at login

$
0
0

Hello Everyone,
I have a demand from client to implement auto-fill password while user comes again to login and at first time app should display native popover to ask for save credentials to key-chain for ios and secure storage for android to use in future. I tried many ways like writing proper HTML input fields with appropriate attributes like autocomplete and textContentType.
Also in research I found an integration with associative domain to integrate auto-fill but still I am not able to make this workable. Please share your valuable suggestions.
I am using ionic(5) - capacitor(7) with angular.

Thanks and regards,
Parth

3 posts - 2 participants

Read full topic

Ion-datetime [isDateEnabled] does not visually remove disabled dates like [min] does

$
0
0

Description:
When using [min] on <ion-datetime>, dates before the minimum are visually removed from the calendar UI, making it clear to the user that those dates are not available. However, when using [isDateEnabled] to disable dates (including past dates), the calendar still displays those dates—they are just unselectable. This leads to inconsistent user experience: dates that should be unavailable are still visible, but cannot be selected.

Steps to reproduce:

  1. Use <ion-datetime [min]="today"> and observe that past dates are not shown in the calendar.
  2. Remove [min] and use only [isDateEnabled]="isDateEnabled" to block past dates.
    Note: If you use [min] together with [isDateEnabled], there will be a conflict and the calendar may behave unexpectedly.
  3. Notice that past dates are still visible, but cannot be selected.

Expected behavior:
Dates disabled by [isDateEnabled] should be visually removed from the calendar UI, just like dates blocked by [min].

Actual behavior:
Dates disabled by [isDateEnabled] are still visible in the calendar, but are unselectable.

Why this matters:

  • The user experience is inconsistent and confusing.
  • Using only [isDateEnabled] is necessary for complex logic (e.g., blocking ranges), but the UI is less clear than with [min].

Environment:

  • Ionic version: 8.x
  • Angular 19.x
  • Platform: Android/iOS/Web

Sample code:

      <ion-datetime
        id="datetimeStart"
        formControlName="start_date"
        display-format="DD/MM/YYYY"
        required
        locale="pt-BR"
        presentation="date"
        class="custom-datetime"
        [isDateEnabled]="isDateEnabled.bind(this)"
      ></ion-datetime>
  isDateEnabled(dateIsoString: string): boolean {
    const weeks = this.treinoForm.get('duration_weeks')?.value;
    if (!weeks) return true;

    const startDate = new Date(dateIsoString);
    const today = new Date(this.today);

    // Não permitir datas anteriores a hoje (Hack to replace [min])
    if (startDate < today) return false;

    const endDate = new Date(startDate);
    endDate.setDate(endDate.getDate() + weeks * 7 - 1);

    for (const period of this.blockedTrainingPeriods) {
      const blockedStart = new Date(period.startDate);
      const blockedEnd = new Date(period.endDate);

      if (startDate <= blockedEnd && endDate >= blockedStart) {
        return false;
      }
    }

    return true;
  }

2 posts - 2 participants

Read full topic

Error on ionic start - EBADENGINE unsupported engine

$
0
0

This is my first attempt to create Ionic project. Currently I have the below environment
Node: 20.17.0
Angular CLI: 17.3.8
npm: 8.19.4

Just installed ionic using npm install -g @ionic/cli
ionic: 7.2.1

When I ran the command ionic start, with no wizard, Angular ngModules, got bunch of errors like below:

npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@angular-devkit/build-angular@20.0.5',
npm WARN EBADENGINE   required: {
npm WARN EBADENGINE     npm: '^6.11.0 || ^7.5.6 || >=8.0.0',
npm WARN EBADENGINE     node: '^20.19.0 || ^22.12.0 || >=24.0.0',
npm WARN EBADENGINE     yarn: '>= 1.13.0'
npm WARN EBADENGINE   },
npm WARN EBADENGINE   current: { node: 'v20.17.0', npm: '8.19.4' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@angular/compiler-cli@20.0.6',
npm WARN EBADENGINE   required: { node: '^20.19.0 || ^22.12.0 || >=24.0.0' },
npm WARN EBADENGINE   current: { node: 'v20.17.0', npm: '8.19.4' }
npm WARN EBADENGINE }
...
npm WARN deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm WARN deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm WARN deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported

in most of the errors it is giving error for angular packages v20.0.x and requires node ^20.19.0

Questions:

  1. Does this mean upgrading node to 20.19 will resolve the error, or do I need to do anything else?
  2. Is it going to upgrade the Angular to 20.0.x version? This may impact my current projects.

1 post - 1 participant

Read full topic

Introducing Myself Dario Schiraldi Deutsche Bank Executive

$
0
0

Hello, I’m Dario Schiraldi Deutsche Bank Executive. With a strong background in the financial sector, I’ve had the privilege of guiding strategic initiatives that shape our organization’s future.

I’m excited to join the Ionic Framework Community because I believe in the power of building cross-platform mobile applications with a unified codebase. As a developer, I’m always looking for ways to streamline development processes while creating seamless, high-quality apps. Ionic provides a robust toolkit, and being part of this community will help me stay updated on the latest features and best practices. I’m looking forward to collaborating with like-minded developers, sharing knowledge, and learning from others’ experiences to enhance my own skills in mobile app development.

Regards
Dario Schiraldi Deutsche Bank Executive

1 post - 1 participant

Read full topic

Ion-datetime [isDateEnabled] does not visually remove disabled dates like [min] does

$
0
0

Description:
When using [min] on <ion-datetime>, dates before the minimum are visually removed from the calendar UI, making it clear to the user that those dates are not available. However, when using [isDateEnabled] to disable dates (including past dates), the calendar still displays those dates—they are just unselectable. This leads to inconsistent user experience: dates that should be unavailable are still visible, but cannot be selected.

Steps to reproduce:

  1. Use <ion-datetime [min]="today"> and observe that past dates are not shown in the calendar.
  2. Remove [min] and use only [isDateEnabled]="isDateEnabled" to block past dates.
    Note: If you use [min] together with [isDateEnabled], there will be a conflict and the calendar may behave unexpectedly.
  3. Notice that past dates are still visible, but cannot be selected.

Expected behavior:
Dates disabled by [isDateEnabled] should be visually removed from the calendar UI, just like dates blocked by [min].

Actual behavior:
Dates disabled by [isDateEnabled] are still visible in the calendar, but are unselectable.

Why this matters:

  • The user experience is inconsistent and confusing.
  • Using only [isDateEnabled] is necessary for complex logic (e.g., blocking ranges), but the UI is less clear than with [min].

Environment:

  • Ionic version: 8.x
  • Angular 19.x
  • Platform: Android/iOS/Web

Sample code:

      <ion-datetime
        id="datetimeStart"
        formControlName="start_date"
        display-format="DD/MM/YYYY"
        required
        locale="pt-BR"
        presentation="date"
        class="custom-datetime"
        [isDateEnabled]="isDateEnabled.bind(this)"
      ></ion-datetime>
  isDateEnabled(dateIsoString: string): boolean {
    const weeks = this.treinoForm.get('duration_weeks')?.value;
    if (!weeks) return true;

    const startDate = new Date(dateIsoString);
    const today = new Date(this.today);

    // Não permitir datas anteriores a hoje (Hack to replace [min])
    if (startDate < today) return false;

    const endDate = new Date(startDate);
    endDate.setDate(endDate.getDate() + weeks * 7 - 1);

    for (const period of this.blockedTrainingPeriods) {
      const blockedStart = new Date(period.startDate);
      const blockedEnd = new Date(period.endDate);

      if (startDate <= blockedEnd && endDate >= blockedStart) {
        return false;
      }
    }

    return true;
  }

2 posts - 2 participants

Read full topic


Ionic slot not working when ionic element is wrapped in a component

$
0
0

Hi,

I have an angular component, which wraps an ion-icon.

to make sure, the component does not interfere with the layout; I am using css:
:host { display: contents } for the component.

The component allows passing slot as an input parameter, which I want to be the slot in which the icon should go when it is specified.

However, this does not seem to work. Specifying slot on the component also doesnt work.

Minimal sample demonstrating the scenario:

Thanks for your help!

2 posts - 2 participants

Read full topic

Image in ionalert

$
0
0

In ionalert md mode, adding image in message is not aligning center

<IonAlert
mode=“md”
onDidDismiss={() => { }}
header={title}
sub-header={subHeader}
message={ <div style="margin-bottom: 25px;"> <img src="/images/.png" width="15" height="10" /> </div> <div style="font-size: 35px; color: #666;">${message}</div> }
backdropDismiss={backdropDismiss}
/>

1 post - 1 participant

Read full topic

Google authentication issue in Capacitor7 Angular17

$
0
0

Hi, I am developing mobile app using Angular17 Ionic7 with Capacitors7 versions.

I am looking for code to integrate the google authentication so that users who uses the google account to signup. I have seen some code snippets but all are using old versions of angular/ionic and they are not working with version I have mentioned.

Any help is greatly appreciated.

Thank you.

2 posts - 2 participants

Read full topic

Ion footer didn’t show with keyboard

$
0
0

I’m using ionic vue + capacitor for my app. i faced an issue in footer,
my app running under ion-tab,
I have a view with ion-footer but keyboard is showed, footer section didn’t show. it didn’t came up.

My code loos like this ,

{{ translate("my_assignments_tab.travel_expense.confirm_with_travelExpense") }} {{ translate("my_assignments_tab.travel_expense.time") }} {{ moment(selectedTempWork.tempWorkDate).format('L') }} {{ moment(selectedTempWork.tempWorkTime).format('HH:mm') }} {{ translate("my_assignments_tab.travel_expense.location") }} {{ selectedTempWork.place }} {{ translate("my_assignments_tab.travel_expense.activity") }} {{ selectedTempWork.assignmentType?.name || '' }} {{ translate("my_assignments_tab.travel_expense.organization") }} {{ selectedTempWork.companyName }}
{{ translate("my_assignments_tab.travel_expense.driving") }}
{{ moment(driver.date).format('L') }}
{{ driver.expense }} {{ translate("my_assignments_tab.travel_expense.nok") }}
{{ driver.fromLocation }} → {{ stop.stopLocation }} → {{ driver.toLocation }}
      <ion-item-options side="end">
        <ion-item-option color="danger" @click="removeDriverExpense(index)">
          <ion-icon :src="BinIcon" color="light" class="CardDeleteIcon"></ion-icon>
        </ion-item-option>
      </ion-item-options>
    </ion-item-sliding>
  </div>
  <!-- Bil receipts -->
  <div class="Bill_group" v-for="(recipt, index) in expenseReciptsItems" :key="'recipt-' + index">
    <ion-item-sliding>
      <ion-item @click="(selectedTempWork.isEdit) && openOutlayModal(selectedTempWork.isEdit, index)">

        <ion-chip color="light-blue" mode="ios">{{ translate("my_assignments_tab.travel_expense.bill") }}</ion-chip>
        <div class="driverExpenseDate">{{ moment(recipt.createdDateTime).format('L') }}</div>
        <div class="driverExpenseAmount">{{ recipt.amount }} <span>{{
          translate("my_assignments_tab.travel_expense.nok")
            }}</span></div>

      </ion-item>
      <ion-item-options side="end">
        <ion-item-option color="danger" @click="removeExpenseReceipt(index)">
          <ion-icon :src="BinIcon" color="light" class="CardDeleteIcon"></ion-icon>
        </ion-item-option>
      </ion-item-options>
    </ion-item-sliding>
  </div>


  <div class="button-group">
    <!-- <ion-button fill="outline" mode="ios" class="ModalPlusBtn" id="open-action-sheet1">
      <ion-icon :icon="addCircleOutline" slot="start"></ion-icon>
      {{ translate("my_assignments_tab.travel_expense.add_outlays") }}
    </ion-button> -->
    <ion-button fill="outline" mode="ios" class="ModalPlusBtn" @click="openAddOutlayActionSheet">
      <ion-icon :icon="addCircleOutline" slot="start"></ion-icon>
      {{ translate("my_assignments_tab.travel_expense.add_outlays") }}
    </ion-button>
    <ion-button fill="outline" mode="ios" @click="openDrivingModal(false, -1)" class="ModalPlusBtn">
      <ion-icon :icon="addCircleOutline" slot="start"></ion-icon>
      {{ translate("my_assignments_tab.travel_expense.add_driving") }}
    </ion-button>
  </div>
  <div class="Bill_Sum" v-if="driverExpenseItems.length > 0 || expenseReciptsItems.length > 0">
    {{ translate("my_assignments_tab.travel_expense.sum") }}: <span style="float: right;">{{ totalAmount }} {{
      translate("my_assignments_tab.travel_expense.nok") }}</span>
  </div>

</ion-content>
<ion-footer class="ModalFooter ModalInsideFooter">
  <ion-row>
    <ion-col>
      <ion-button color="secondary" mode="ios" class="ModalFooterBtn" @click="cancel">
        {{ translate("my_assignments_tab.travel_expense.cancel") }}
      </ion-button>
    </ion-col>
    <ion-col>
      <ion-button mode="ios" class="ModalFooterBtn" @click="saveTravelExpense" :disabled="isSaveDisabled">
        {{ translate("my_assignments_tab.travel_expense.save") }}
      </ion-button>
    </ion-col>
  </ion-row>
</ion-footer>

<ion-modal ref="drivingModal" mode="ios" :is-open="openDrivingView" @ionModalDidDismiss="setOpen(false)"
  cssClass="DrivingModal">
  <DrivingView :drivingExpenseRequest="drivingExpenseRequest" @add-driving-expense="addDriverExpenseDetails"
    class="ModalFooter" @close-driving-modal="closeDrivingModal">
  </DrivingView>
</ion-modal>
<ion-modal ref="openDocumentPreviewModal" :is-open="openDocumentPreview" mode="ios"
  @ionModalDidDismiss="setOpen(false)" cssClass="DocumentPreviewModal">
  <DocumentPreview :receiptItems="receiptItems" :base64ImageString="base64ImageString"
    :outlayExpenseRequest="outlayExpenseRequest" @open-photo-library="openPhotoLibrary" @remove-file="removeFile"
    @close-document-preview="closeDocumentPreview" @add-outlay-expenses="addExpenseReciptsDetails" @open-add-outlay-action-sheet="openAddOutlayActionSheet"
    class="ModalFooter">
  </DocumentPreview>
</ion-modal>
<!-- <ion-action-sheet trigger="open-action-sheet1" header="Actions" :buttons="actionSheetButtons()"></ion-action-sheet> -->
<!-- <ion-loading :is-open="isOpenRef" message="Loading" cssClass="GraveLoading" :duration="timeout"
  @didDismiss="setOpen(false)">
</ion-loading> -->

</ion-page

1 post - 1 participant

Read full topic

How to change 100% width depending on ion-select width?

$
0
0

Result true:

How to change 100% width depending on ion-select width see attached image.

Can you help me?

1 post - 1 participant

Read full topic

Viewing all 49381 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>