From 42e8ec390689dc63ae5428f5d4f085d85716d4aa Mon Sep 17 00:00:00 2001 From: Colin Bares Date: Fri, 6 Jun 2025 18:09:49 -0500 Subject: [PATCH 01/12] created renderParameters function in docusaurus script to include row for method parameters generated in renderMethods function (#4139) --- .../index.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plugins/docusaurus-plugin-ionic-component-api/index.js b/plugins/docusaurus-plugin-ionic-component-api/index.js index d6908d4a0d3..d3b3d2d7255 100644 --- a/plugins/docusaurus-plugin-ionic-component-api/index.js +++ b/plugins/docusaurus-plugin-ionic-component-api/index.js @@ -173,6 +173,25 @@ function renderEvents({ events }) { ${events.map((event) => `| \`${event.event}\` | ${formatMultiline(event.docs)} | \`${event.bubbles}\` |`).join('\n')}`; } +/** + * Formats method parameters for the optional Parameters row of each method table + * @param {*} paramsArr Array of method parameters + * @returns formatted parameters for methods table + */ +function renderParameters(paramsArr) { + if (!paramsArr.some((param) => param.docs)) { + return ''; + } + + const documentedParams = paramsArr.filter((param) => param.docs); + const formattedParams = documentedParams + .map((param) => { + return `**${param.name}**: ${formatMultiline(param.docs)}`; + }) + .join('
'); + return `| **Parameters** | ${formattedParams} |`; +} + function renderMethods({ methods }) { if (methods.length === 0) { return 'No public methods available for this component.'; @@ -189,6 +208,7 @@ ${methods | --- | --- | | **Description** | ${formatMultiline(method.docs)} | | **Signature** | \`${method.signature.replace(/\|/g, '\uff5c')}\` | +${method.parameters.length !== 0 ? renderParameters(method.parameters) : ''} ` ) .join('\n')} From 16cca6ac8ed6d2efd89a9f05567e249ee4c2ca77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 06:21:09 -0700 Subject: [PATCH 02/12] chore(deps): update dependency ionicons to v8.0.9 (#4144) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- static/code/stackblitz/v7/angular/package.json | 2 +- static/code/stackblitz/v7/html/package.json | 2 +- static/code/stackblitz/v8/angular/package.json | 2 +- static/code/stackblitz/v8/html/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/code/stackblitz/v7/angular/package.json b/static/code/stackblitz/v7/angular/package.json index 439076f494a..295ed77a940 100644 --- a/static/code/stackblitz/v7/angular/package.json +++ b/static/code/stackblitz/v7/angular/package.json @@ -17,7 +17,7 @@ "@angular/router": "^19.0.0", "@ionic/angular": "^7.0.0", "@ionic/core": "^7.0.0", - "ionicons": "8.0.8", + "ionicons": "8.0.9", "rxjs": "^7.8.1", "tslib": "^2.5.0", "zone.js": "~0.15.0" diff --git a/static/code/stackblitz/v7/html/package.json b/static/code/stackblitz/v7/html/package.json index c002c4a5631..7b9f1727b1c 100644 --- a/static/code/stackblitz/v7/html/package.json +++ b/static/code/stackblitz/v7/html/package.json @@ -1,6 +1,6 @@ { "dependencies": { "@ionic/core": "^7.0.0", - "ionicons": "8.0.8" + "ionicons": "8.0.9" } } diff --git a/static/code/stackblitz/v8/angular/package.json b/static/code/stackblitz/v8/angular/package.json index 7047ff2b60b..d11eda773bb 100644 --- a/static/code/stackblitz/v8/angular/package.json +++ b/static/code/stackblitz/v8/angular/package.json @@ -17,7 +17,7 @@ "@angular/router": "^19.0.0", "@ionic/angular": "8.6.0", "@ionic/core": "8.6.0", - "ionicons": "8.0.8", + "ionicons": "8.0.9", "rxjs": "^7.8.1", "tslib": "^2.5.0", "zone.js": "~0.15.0" diff --git a/static/code/stackblitz/v8/html/package.json b/static/code/stackblitz/v8/html/package.json index 1459153ed8d..ad228f0ae4b 100644 --- a/static/code/stackblitz/v8/html/package.json +++ b/static/code/stackblitz/v8/html/package.json @@ -1,6 +1,6 @@ { "dependencies": { "@ionic/core": "8.6.0", - "ionicons": "8.0.8" + "ionicons": "8.0.9" } } From 9a59103d1f54f7b2336aede8c65932132211ba02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 06:24:32 -0700 Subject: [PATCH 03/12] chore(deps): update dependency @vitejs/plugin-react to v4.5.1 (#4143) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- static/code/stackblitz/v7/react/package-lock.json | 12 ++++++------ static/code/stackblitz/v8/react/package-lock.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/static/code/stackblitz/v7/react/package-lock.json b/static/code/stackblitz/v7/react/package-lock.json index 3721f5dedb4..7349808fde4 100644 --- a/static/code/stackblitz/v7/react/package-lock.json +++ b/static/code/stackblitz/v7/react/package-lock.json @@ -1090,9 +1090,9 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", - "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.1.tgz", + "integrity": "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==", "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", @@ -2390,9 +2390,9 @@ } }, "@vitejs/plugin-react": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", - "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.1.tgz", + "integrity": "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==", "requires": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", diff --git a/static/code/stackblitz/v8/react/package-lock.json b/static/code/stackblitz/v8/react/package-lock.json index c880b254b9a..2a5b080806b 100644 --- a/static/code/stackblitz/v8/react/package-lock.json +++ b/static/code/stackblitz/v8/react/package-lock.json @@ -1196,9 +1196,9 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", - "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.1.tgz", + "integrity": "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==", "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", @@ -2556,9 +2556,9 @@ } }, "@vitejs/plugin-react": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", - "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.1.tgz", + "integrity": "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==", "requires": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", From dc5f3d6ca847bdb329426f653eaebeb2f26dc960 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 06:25:22 -0700 Subject: [PATCH 04/12] chore(deps): update dependency @types/react-dom to v19.1.6 (#4142) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- static/code/stackblitz/v7/react/package-lock.json | 12 ++++++------ static/code/stackblitz/v8/react/package-lock.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/static/code/stackblitz/v7/react/package-lock.json b/static/code/stackblitz/v7/react/package-lock.json index 7349808fde4..091baacd293 100644 --- a/static/code/stackblitz/v7/react/package-lock.json +++ b/static/code/stackblitz/v7/react/package-lock.json @@ -1063,9 +1063,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", - "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "peerDependencies": { "@types/react": "^19.0.0" } @@ -2365,9 +2365,9 @@ } }, "@types/react-dom": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", - "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "requires": {} }, "@types/react-router": { diff --git a/static/code/stackblitz/v8/react/package-lock.json b/static/code/stackblitz/v8/react/package-lock.json index 2a5b080806b..f8971eb9168 100644 --- a/static/code/stackblitz/v8/react/package-lock.json +++ b/static/code/stackblitz/v8/react/package-lock.json @@ -1169,9 +1169,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", - "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "peerDependencies": { "@types/react": "^19.0.0" } @@ -2531,9 +2531,9 @@ } }, "@types/react-dom": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", - "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "requires": {} }, "@types/react-router": { From 8e6b022030821573a536db0b04a2d4eccfa513b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 06:26:22 -0700 Subject: [PATCH 05/12] chore(deps): update dependency @types/node to v22.15.30 (#4141) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- static/code/stackblitz/v7/react/package-lock.json | 12 ++++++------ static/code/stackblitz/v8/react/package-lock.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/static/code/stackblitz/v7/react/package-lock.json b/static/code/stackblitz/v7/react/package-lock.json index 091baacd293..850022cfe69 100644 --- a/static/code/stackblitz/v7/react/package-lock.json +++ b/static/code/stackblitz/v7/react/package-lock.json @@ -1047,9 +1047,9 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" }, "node_modules/@types/node": { - "version": "22.15.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", - "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "version": "22.15.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", + "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", "dependencies": { "undici-types": "~6.21.0" } @@ -2349,9 +2349,9 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" }, "@types/node": { - "version": "22.15.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", - "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "version": "22.15.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", + "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", "requires": { "undici-types": "~6.21.0" } diff --git a/static/code/stackblitz/v8/react/package-lock.json b/static/code/stackblitz/v8/react/package-lock.json index f8971eb9168..0f24714695e 100644 --- a/static/code/stackblitz/v8/react/package-lock.json +++ b/static/code/stackblitz/v8/react/package-lock.json @@ -1153,9 +1153,9 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" }, "node_modules/@types/node": { - "version": "22.15.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", - "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "version": "22.15.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", + "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", "dependencies": { "undici-types": "~6.21.0" } @@ -2515,9 +2515,9 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" }, "@types/node": { - "version": "22.15.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", - "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "version": "22.15.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", + "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", "requires": { "undici-types": "~6.21.0" } From 0c52a76dc5db8ca5d8d403b078b3557b81555bf7 Mon Sep 17 00:00:00 2001 From: "kendra.jade" Date: Mon, 9 Jun 2025 08:50:57 -0500 Subject: [PATCH 06/12] docs(infinite-scroll): remove duplicate and unused imports (#4138) --- static/usage/v7/infinite-scroll/basic/vue.md | 3 --- .../v7/infinite-scroll/custom-infinite-scroll-content/vue.md | 3 --- static/usage/v7/infinite-scroll/infinite-scroll-content/vue.md | 3 --- static/usage/v8/infinite-scroll/basic/vue.md | 3 --- .../v8/infinite-scroll/custom-infinite-scroll-content/vue.md | 3 --- static/usage/v8/infinite-scroll/infinite-scroll-content/vue.md | 3 --- 6 files changed, 18 deletions(-) diff --git a/static/usage/v7/infinite-scroll/basic/vue.md b/static/usage/v7/infinite-scroll/basic/vue.md index 61c08173971..cea52dd957c 100644 --- a/static/usage/v7/infinite-scroll/basic/vue.md +++ b/static/usage/v7/infinite-scroll/basic/vue.md @@ -23,7 +23,6 @@ IonList, IonItem, IonAvatar, - IonImg, IonLabel, InfiniteScrollCustomEvent, } from '@ionic/vue'; @@ -31,14 +30,12 @@ export default defineComponent({ components: { - IonContent, IonContent, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonItem, IonAvatar, - IonImg, IonLabel, }, setup() { diff --git a/static/usage/v7/infinite-scroll/custom-infinite-scroll-content/vue.md b/static/usage/v7/infinite-scroll/custom-infinite-scroll-content/vue.md index 85954295280..b5fd3bad6e8 100644 --- a/static/usage/v7/infinite-scroll/custom-infinite-scroll-content/vue.md +++ b/static/usage/v7/infinite-scroll/custom-infinite-scroll-content/vue.md @@ -89,7 +89,6 @@ IonList, IonItem, IonAvatar, - IonImg, IonLabel, InfiniteScrollCustomEvent, } from '@ionic/vue'; @@ -97,14 +96,12 @@ export default defineComponent({ components: { - IonContent, IonContent, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonItem, IonAvatar, - IonImg, IonLabel, }, setup() { diff --git a/static/usage/v7/infinite-scroll/infinite-scroll-content/vue.md b/static/usage/v7/infinite-scroll/infinite-scroll-content/vue.md index f00b47e1fb8..6facce2be59 100644 --- a/static/usage/v7/infinite-scroll/infinite-scroll-content/vue.md +++ b/static/usage/v7/infinite-scroll/infinite-scroll-content/vue.md @@ -26,7 +26,6 @@ IonList, IonItem, IonAvatar, - IonImg, IonLabel, InfiniteScrollCustomEvent, } from '@ionic/vue'; @@ -34,14 +33,12 @@ export default defineComponent({ components: { - IonContent, IonContent, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonItem, IonAvatar, - IonImg, IonLabel, }, setup() { diff --git a/static/usage/v8/infinite-scroll/basic/vue.md b/static/usage/v8/infinite-scroll/basic/vue.md index 61c08173971..cea52dd957c 100644 --- a/static/usage/v8/infinite-scroll/basic/vue.md +++ b/static/usage/v8/infinite-scroll/basic/vue.md @@ -23,7 +23,6 @@ IonList, IonItem, IonAvatar, - IonImg, IonLabel, InfiniteScrollCustomEvent, } from '@ionic/vue'; @@ -31,14 +30,12 @@ export default defineComponent({ components: { - IonContent, IonContent, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonItem, IonAvatar, - IonImg, IonLabel, }, setup() { diff --git a/static/usage/v8/infinite-scroll/custom-infinite-scroll-content/vue.md b/static/usage/v8/infinite-scroll/custom-infinite-scroll-content/vue.md index 85954295280..b5fd3bad6e8 100644 --- a/static/usage/v8/infinite-scroll/custom-infinite-scroll-content/vue.md +++ b/static/usage/v8/infinite-scroll/custom-infinite-scroll-content/vue.md @@ -89,7 +89,6 @@ IonList, IonItem, IonAvatar, - IonImg, IonLabel, InfiniteScrollCustomEvent, } from '@ionic/vue'; @@ -97,14 +96,12 @@ export default defineComponent({ components: { - IonContent, IonContent, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonItem, IonAvatar, - IonImg, IonLabel, }, setup() { diff --git a/static/usage/v8/infinite-scroll/infinite-scroll-content/vue.md b/static/usage/v8/infinite-scroll/infinite-scroll-content/vue.md index f00b47e1fb8..6facce2be59 100644 --- a/static/usage/v8/infinite-scroll/infinite-scroll-content/vue.md +++ b/static/usage/v8/infinite-scroll/infinite-scroll-content/vue.md @@ -26,7 +26,6 @@ IonList, IonItem, IonAvatar, - IonImg, IonLabel, InfiniteScrollCustomEvent, } from '@ionic/vue'; @@ -34,14 +33,12 @@ export default defineComponent({ components: { - IonContent, IonContent, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonItem, IonAvatar, - IonImg, IonLabel, }, setup() { From 9c0859d030f71a00ec61f885ca61f8276423b78b Mon Sep 17 00:00:00 2001 From: soundproofboot Date: Fri, 13 Jun 2025 13:14:36 -0500 Subject: [PATCH 07/12] docs(angular): add context to code blocks in your-first-app page --- docs/angular/your-first-app.md | 61 +++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/docs/angular/your-first-app.md b/docs/angular/your-first-app.md index 739a9c3d29b..99a61d9ead3 100644 --- a/docs/angular/your-first-app.md +++ b/docs/angular/your-first-app.md @@ -110,10 +110,15 @@ npm install @ionic/pwa-elements Next, import `@ionic/pwa-elements` by editing `src/main.ts`. ```tsx -import { defineCustomElements } from '@ionic/pwa-elements/loader'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app/app.module'; +import { defineCustomElements } from '@ionic/pwa-elements/loader'; // Added import // Call the element loader before the bootstrapModule/bootstrapApplication call defineCustomElements(window); + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.log(err)); ``` That’s it! Now for the fun part - let’s see the app in action. @@ -137,18 +142,22 @@ There are three tabs. Click on the Tab2 tab. It’s a blank canvas, aka the perf Open the photo-gallery app folder in your code editor of choice, then navigate to `/src/app/tab2/tab2.page.html`. We see: ```html - + - Tab 2 + + Tab 2 + - + Tab 2 + + ``` @@ -161,22 +170,58 @@ Open the photo-gallery app folder in your code editor of choice, then navigate t We put the visual aspects of our app into ``. In this case, it’s where we’ll add a button that opens the device’s camera as well as displays the image captured by the camera. Start by adding a [floating action button](https://ionicframework.com/docs/api/fab) (FAB) to the bottom of the page and set the camera image as the icon. ```html + + + + Tab 2 + + + + + + + Tab 2 + + + + + + + ``` Next, open `src/app/tabs/tabs.page.html`. Change the label to “Photos” and the icon name to “images”: ```html - - - Photos - + + + + + + Tab 1 + + + + + + + Photos + + + + + Tab 3 + + + + ``` Save all changes to see them automatically applied in the browser. That’s just the start of all the cool things we can do with Ionic. Up next, implement camera taking functionality on the web, then build it for iOS and Android. From e979ea8fe623a73572876eeba7bc54a8e136cfa8 Mon Sep 17 00:00:00 2001 From: soundproofboot Date: Fri, 13 Jun 2025 14:03:09 -0500 Subject: [PATCH 08/12] docs(angular): add context to code blocks and note to learn more about ngFor in 2-taking-photos.md --- .../angular/your-first-app/2-taking-photos.md | 130 +++++++++++++++++- 1 file changed, 123 insertions(+), 7 deletions(-) diff --git a/docs/angular/your-first-app/2-taking-photos.md b/docs/angular/your-first-app/2-taking-photos.md index 0e0cd078a70..2cb41f30321 100644 --- a/docs/angular/your-first-app/2-taking-photos.md +++ b/docs/angular/your-first-app/2-taking-photos.md @@ -44,27 +44,81 @@ public async addNewToGallery() { Notice the magic here: there's no platform-specific code (web, iOS, or Android)! The Capacitor Camera plugin abstracts that away for us, leaving just one method call - `Camera.getPhoto()` - that will open up the device's camera and allow us to take photos. +Your updated `photo.service.ts` should now look like this: + +```tsx +import { Injectable } from '@angular/core'; +import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera'; +import { Filesystem, Directory } from '@capacitor/filesystem'; +import { Preferences } from '@capacitor/preferences'; + +@Injectable({ + providedIn: 'root' +}) +export class PhotoService { + + constructor() { } + + public async addNewToGallery() { + // Take a photo + const capturedPhoto = await Camera.getPhoto({ + resultType: CameraResultType.Uri, + source: CameraSource.Camera, + quality: 100 + }); + } +} +``` + Next, open up `tab2.page.ts` and import the PhotoService class and add a method that calls the `addNewToGallery` method on the imported service: ```tsx +import { Component } from '@angular/core'; import { PhotoService } from '../services/photo.service'; -constructor(public photoService: PhotoService) { } - -addPhotoToGallery() { - this.photoService.addNewToGallery(); +@Component({ + selector: 'app-tab2', + templateUrl: 'tab2.page.html', + styleUrls: ['tab2.page.scss'], + standalone: false, +}) +export class Tab2Page { + + // update constructor to include photoService + constructor(public photoService: PhotoService) { } + + // add addNewToGallery method + addPhotoToGallery() { + this.photoService.addNewToGallery(); + } } ``` Then, open `tab2.page.html` and call the `addPhotoToGallery()` function when the FAB is tapped/clicked: ```html + + + + Tab 2 + + + + + + + Tab 2 + + + + + ``` @@ -78,7 +132,7 @@ After taking a photo, it disappears right away. We need to display it within our ## Displaying Photos -Outside of the `PhotoService` class definition (the very bottom of the file), create a new interface, `UserPhoto`, to hold our photo metadata: +Return to `photo.service.ts`. Outside of the `PhotoService` class definition (the very bottom of the file), create a new interface, `UserPhoto`, to hold our photo metadata: ```tsx export interface UserPhoto { @@ -87,12 +141,14 @@ export interface UserPhoto { } ``` -Back at the top of the file, define an array of Photos, which will contain a reference to each photo captured with the Camera. +Back at the top of the `PhotoService` class definition, define an array of Photos, which will contain a reference to each photo captured with the Camera. ```tsx export class PhotoService { public photos: UserPhoto[] = []; + constructor() { } + // other code } ``` @@ -100,34 +156,94 @@ export class PhotoService { Over in the `addNewToGallery` function, add the newly captured photo to the beginning of the Photos array. ```tsx +public async addNewToGallery() { const capturedPhoto = await Camera.getPhoto({ resultType: CameraResultType.Uri, source: CameraSource.Camera, quality: 100 }); + // add new photo to photos array this.photos.unshift({ filepath: "soon...", webviewPath: capturedPhoto.webPath! }); } ``` +`photo.service.ts` should now look like this: + +```tsx +import { Injectable } from '@angular/core'; +import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera'; +import { Filesystem, Directory } from '@capacitor/filesystem'; +import { Preferences } from '@capacitor/preferences'; + +@Injectable({ + providedIn: 'root' +}) +export class PhotoService { + public photos: UserPhoto[] = []; + constructor() { } + + public async addNewToGallery() { + const capturedPhoto = await Camera.getPhoto({ + resultType: CameraResultType.Uri, + source: CameraSource.Camera, + quality: 100 + }); + + // add new photo to photos array + this.photos.unshift({ + filepath: "soon...", + webviewPath: capturedPhoto.webPath! + }); + } +} +export interface UserPhoto { + filepath: string; + webviewPath?: string; +} +``` Next, move over to `tab2.page.html` so we can display the image on the screen. Add a [Grid component](https://ionicframework.com/docs/api/grid) so that each photo will display nicely as photos are added to the gallery, and loop through each photo in the `PhotoServices`'s Photos array, adding an Image component (``) for each. Point the `src` (source) at the photo’s path: ```html + + + + Tab 2 + + + + + + + Tab 2 + + + + + - + + + + + + ``` +:::note +Learn more about the [ngFor core directive](https://blog.angular-university.io/angular-2-ngfor/). +::: Save all files. Within the web browser, click the Camera button and take another photo. This time, the photo is displayed in the Photo Gallery! From 67d1164284334b85f2ec3e38546068a842dce796 Mon Sep 17 00:00:00 2001 From: soundproofboot Date: Fri, 13 Jun 2025 14:27:31 -0500 Subject: [PATCH 09/12] docs(angular): add context to method definitions, additional code block to 3-saving-photos.md --- .../angular/your-first-app/3-saving-photos.md | 77 ++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/angular/your-first-app/3-saving-photos.md b/docs/angular/your-first-app/3-saving-photos.md index c1e013a8bec..a162a608a55 100644 --- a/docs/angular/your-first-app/3-saving-photos.md +++ b/docs/angular/your-first-app/3-saving-photos.md @@ -27,6 +27,7 @@ public async addNewToGallery() { // Save the picture and add it to photo collection const savedImageFile = await this.savePicture(capturedPhoto); + // update argument to unshift array method this.photos.unshift(savedImageFile); } ``` @@ -55,7 +56,7 @@ private async savePicture(photo: Photo) { } ``` -`readAsBase64()` is a helper function we’ll define next. It's useful to organize via a separate method since it requires a small amount of platform-specific (web vs. mobile) logic - more on that in a bit. For now, implement the logic for running on the web: +`readAsBase64()` is a helper function we’ll define next. It's useful to organize via a separate method since it requires a small amount of platform-specific (web vs. mobile) logic - more on that in a bit. For now, we'll create two new helper functions, `readAsBase64()` and `convertBlobToBase64()`, to implement the logic for running on the web: ```tsx private async readAsBase64(photo: Photo) { @@ -76,6 +77,78 @@ private convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => { }); ``` +`photo.service.ts` should now look like this: + +```tsx +import { Injectable } from '@angular/core'; +import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera'; +import { Filesystem, Directory } from '@capacitor/filesystem'; +import { Preferences } from '@capacitor/preferences'; + +@Injectable({ + providedIn: 'root' +}) +export class PhotoService { + public photos: UserPhoto[] = []; + constructor() { } + + public async addNewToGallery() { + const capturedPhoto = await Camera.getPhoto({ + resultType: CameraResultType.Uri, + source: CameraSource.Camera, + quality: 100 + }); + + // Save the picture and add it to photo collection + const savedImageFile = await this.savePicture(capturedPhoto); + // update argument to unshift array method + this.photos.unshift(savedImageFile); + } + + private async savePicture(photo: Photo) { + // Convert photo to base64 format, required by Filesystem API to save + const base64Data = await this.readAsBase64(photo); + + // Write the file to the data directory + const fileName = Date.now() + '.jpeg'; + const savedFile = await Filesystem.writeFile({ + path: fileName, + data: base64Data, + directory: Directory.Data + }); + + // Use webPath to display the new image instead of base64 since it's + // already loaded into memory + return { + filepath: fileName, + webviewPath: photo.webPath + }; + } + + private async readAsBase64(photo: Photo) { + // Fetch the photo, read as a blob, then convert to base64 format + const response = await fetch(photo.webPath!); + const blob = await response.blob(); + + return await this.convertBlobToBase64(blob) as string; + } + + private convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = reject; + reader.onload = () => { + resolve(reader.result); + }; + reader.readAsDataURL(blob); + }); +} + +export interface UserPhoto { + filepath: string; + webviewPath?: string; +} +``` Obtaining the camera photo as base64 format on the web appears to be a bit trickier than on mobile. In reality, we’re just using built-in web APIs: [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) as a neat way to read the file into blob format, then FileReader’s [readAsDataURL()](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL) to convert the photo blob to base64. -There we go! Each time a new photo is taken, it’s now automatically saved to the filesystem. +There we go! Each time a new photo is taken, it’s now automatically saved to the filesystem. Next up, we'll load and display our saved images +when the user navigates to the Photos tab. \ No newline at end of file From ebb2a94727279e8610af2ef5f732649fc6485b0b Mon Sep 17 00:00:00 2001 From: soundproofboot Date: Mon, 16 Jun 2025 10:35:35 -0500 Subject: [PATCH 10/12] docs(angular): add context in code blocks, clarity, storage note in 4-loading-photos.md --- .../your-first-app/4-loading-photos.md | 162 ++++++++++++++++-- 1 file changed, 150 insertions(+), 12 deletions(-) diff --git a/docs/angular/your-first-app/4-loading-photos.md b/docs/angular/your-first-app/4-loading-photos.md index 6f59d3f951f..e87abec44cb 100644 --- a/docs/angular/your-first-app/4-loading-photos.md +++ b/docs/angular/your-first-app/4-loading-photos.md @@ -10,27 +10,44 @@ Fortunately, this is easy: we’ll leverage the Capacitor [Preferences API](http ## Preferences API -Begin by defining a constant variable that will act as the key for the store: +Open `photo.service.ts` and begin by defining a new property in the `PhotoService` class that will act as the key for the store: ```tsx export class PhotoService { public photos: UserPhoto[] = []; + + // add key for photo store private PHOTO_STORAGE: string = 'photos'; - // other code + constructor() {} + + // other code... } ``` Next, at the end of the `addNewToGallery` function, add a call to `Preferences.set()` to save the Photos array. By adding it here, the Photos array is stored each time a new photo is taken. This way, it doesn’t matter when the app user closes or switches to a different app - all photo data is saved. ```tsx -Preferences.set({ - key: this.PHOTO_STORAGE, - value: JSON.stringify(this.photos), -}); +public async addNewToGallery() { + const capturedPhoto = await Camera.getPhoto({ + resultType: CameraResultType.Uri, + source: CameraSource.Camera, + quality: 100, + }); + + const savedImageFile = await this.savePicture(capturedPhoto); + + this.photos.unshift(savedImageFile); + + // Add call to set() method to cache all photo data for future retrieval + Preferences.set({ + key: this.PHOTO_STORAGE, + value: JSON.stringify(this.photos), + }); +} ``` -With the photo array data saved, create a function called `loadSaved()` that can retrieve that data. We use the same key to retrieve the photos array in JSON format, then parse it into an array: +With the photo array data saved, create a new public method in the `PhotoService` class called `loadSaved()` that can retrieve the photo data. We use the same key to retrieve the photos array in JSON format, then parse it into an array: ```tsx public async loadSaved() { @@ -42,7 +59,7 @@ public async loadSaved() { } ``` -On mobile (coming up next!), we can directly set the source of an image tag - `` - to each photo file on the Filesystem, displaying them automatically. On the web, however, we must read each image from the Filesystem into base64 format, using a new `base64` property on the `Photo` object. This is because the Filesystem API uses [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) under the hood. Below is the code you need to add in the `loadSaved()` function you just added: +On mobile (coming up next!), we can directly set the source of an image tag - `` - to each photo file on the Filesystem, displaying them automatically. On the web, however, we must read each image from the Filesystem into base64 format, using a new `base64` property on the `Photo` object. This is because the Filesystem API uses [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) under the hood. Add the following code to complete the `loadSaved()` function: ```tsx // Display the photo by reading into base64 format @@ -57,13 +74,134 @@ for (let photo of this.photos) { photo.webviewPath = `data:image/jpeg;base64,${readFile.data}`; } ``` - -After, call this new method in `tab2.page.ts` so that when the user first navigates to Tab 2 (the Photo Gallery), all photos are loaded and displayed on the screen. +After these updates to the `PhotoService` class, your `photos.service.ts` file should look like this: ```tsx -async ngOnInit() { - await this.photoService.loadSaved(); +import { Injectable } from '@angular/core'; +import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera'; +import { Filesystem, Directory } from '@capacitor/filesystem'; +import { Preferences } from '@capacitor/preferences'; + +@Injectable({ + providedIn: 'root' +}) + +export class PhotoService { + public photos: UserPhoto[] = []; + private PHOTO_STORAGE = 'photos'; + + constructor() {} + + public async addNewToGallery() { + // Take a photo + const capturedPhoto = await Camera.getPhoto({ + resultType: CameraResultType.Uri, // file-based data; provides best performance + source: CameraSource.Camera, // automatically take a new photo with the camera + quality: 100, // highest quality (0 to 100) + }); + + const savedImageFile = await this.savePicture(capturedPhoto); + + // Add new photo to Photos array + this.photos.unshift(savedImageFile); + + // Cache all photo data for future retrieval + Preferences.set({ + key: this.PHOTO_STORAGE, + value: JSON.stringify(this.photos), + }); + } + + public async loadSaved() { + // Retrieve cached photo array data + const { value } = await Preferences.get({ key: this.PHOTO_STORAGE }); + this.photos = (value ? JSON.parse(value) : []) as UserPhoto[]; + + // Display the photo by reading into base64 format + for (let photo of this.photos) { + // Read each saved photo's data from the Filesystem + const readFile = await Filesystem.readFile({ + path: photo.filepath, + directory: Directory.Data, + }); + + // Web platform only: Load the photo as base64 data + photo.webviewPath = `data:image/jpeg;base64,${readFile.data}`; + } + } + + private async savePicture(photo: Photo) { + // Convert photo to base64 format, required by Filesystem API to save + const base64Data = await this.readAsBase64(photo); + + // Write the file to the data directory + const fileName = Date.now() + '.jpeg'; + const savedFile = await Filesystem.writeFile({ + path: fileName, + data: base64Data, + directory: Directory.Data + }); + + // Use webPath to display the new image instead of base64 since it's + // already loaded into memory + return { + filepath: fileName, + webviewPath: photo.webPath + }; + } + + private async readAsBase64(photo: Photo) { + // Fetch the photo, read as a blob, then convert to base64 format + const response = await fetch(photo.webPath!); + const blob = await response.blob(); + + return await this.convertBlobToBase64(blob) as string; + } + + private convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = reject; + reader.onload = () => { + resolve(reader.result); + }; + reader.readAsDataURL(blob); + }); +} + +export interface UserPhoto { + filepath: string; + webviewPath?: string; } ``` +Our `PhotoService` can now load the saved images, but we'll need to update `tab2.page.ts` to put that new code to work. We'll call `loadSaved` within the [ngOnInit](https://angular.dev/guide/components/lifecycle#ngoninit) lifecycle method so that when the user first navigates to Tab 2 (the Photo Gallery), all photos are loaded and displayed on the screen. Update `tab2.page.ts` to look like the following: + +```tsx +import { Component } from '@angular/core'; +import { PhotoService } from '../services/photo.service'; + +@Component({ + selector: 'app-tab2', + templateUrl: 'tab2.page.html', + styleUrls: ['tab2.page.scss'], + standalone: false, +}) +export class Tab2Page { + + constructor(public photoService: PhotoService) {} + + // add call to loadSaved on navigation to Photos tab + async ngOnInit() { + await this.photoService.loadSaved(); + } + + addPhotoToGallery() { + this.photoService.addNewToGallery(); + } +} +``` +:::note +If you're seeing broken image links or missing photos after following these steps, you may need to open your browser's +dev tools and clear both [localStorage](https://developer.chrome.com/docs/devtools/storage/localstorage) and [IndexedDB](https://developer.chrome.com/docs/devtools/storage/indexeddb). +::: That’s it! We’ve built a complete Photo Gallery feature in our Ionic app that works on the web. Next up, we’ll transform it into a mobile app for iOS and Android! From 2660f4c544823d061d65fee1ec6f778402649700 Mon Sep 17 00:00:00 2001 From: soundproofboot Date: Mon, 16 Jun 2025 11:13:10 -0500 Subject: [PATCH 11/12] docs(angular): add context and clarity to code blocks in 5-adding-mobile.md --- .../angular/your-first-app/5-adding-mobile.md | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/angular/your-first-app/5-adding-mobile.md b/docs/angular/your-first-app/5-adding-mobile.md index ce7f60e75c5..4de0c0d0eb4 100644 --- a/docs/angular/your-first-app/5-adding-mobile.md +++ b/docs/angular/your-first-app/5-adding-mobile.md @@ -10,27 +10,38 @@ Our photo gallery app won’t be complete until it runs on iOS, Android, and the Let’s start with making some small code changes - then our app will “just work” when we deploy it to a device. -Import the Ionic [Platform API](https://ionicframework.com/docs/angular/platform) into `photo.service.ts`, which is used to retrieve information about the current device. In this case, it’s useful for selecting which code to execute based on the platform the app is running on (web or mobile): +Import the Ionic [Platform API](https://ionicframework.com/docs/angular/platform) into `photo.service.ts`, which is used to retrieve information about the current device. In this case, it’s useful for selecting which code to execute based on the platform the app is running on (web or mobile). + +Add `Platform` to the imports at the top of the file and a new property `platform` to the `PhotoService` class. We'll also need to update the constructor to set the user's platform: ```tsx +import { Injectable } from '@angular/core'; +import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera'; +import { Filesystem, Directory } from '@capacitor/filesystem'; +import { Preferences } from '@capacitor/preferences'; + +// add Platform import import { Platform } from '@ionic/angular'; export class PhotoService { public photos: UserPhoto[] = []; private PHOTO_STORAGE: string = 'photos'; + + // add property platform to store which platform app is running on private platform: Platform; + // update constructor to set platform property constructor(platform: Platform) { this.platform = platform; } - // other code + // other code... } ``` ## Platform-specific Logic -First, we’ll update the photo saving functionality to support mobile. In the `readAsBase64()` function, check which platform the app is running on. If it’s “hybrid” (Capacitor or Cordova, two native runtimes), then read the photo file into base64 format using the Filesystem `readFile()` method. Otherwise, use the same logic as before when running the app on the web: +First, we’ll update the photo saving functionality to support mobile. In the `readAsBase64()` function, check which platform the app is running on. If it’s “hybrid” (Capacitor or Cordova, two native runtimes), then read the photo file into base64 format using the Filesystem `readFile()` method. Otherwise, use the same logic as before when running the app on the web. Update `readAsBase64()` to look like the following: ```tsx private async readAsBase64(photo: Photo) { @@ -53,8 +64,13 @@ private async readAsBase64(photo: Photo) { } ``` -Next, update the `savePicture()` method. When running on mobile, set `filepath` to the result of the `writeFile()` operation - `savedFile.uri`. When setting the `webviewPath`, use the special `Capacitor.convertFileSrc()` method ([details here](https://ionicframework.com/docs/core-concepts/webview#file-protocol)). +Next, update the `savePicture()` method. When running on mobile, set `filepath` to the result of the `writeFile()` operation - `savedFile.uri`. When setting the `webviewPath`, use the special `Capacitor.convertFileSrc()` method ([details on the File Protocol](https://ionicframework.com/docs/core-concepts/webview#file-protocol)). To use this method, we'll need to import Capacitor at the +top of `photo.service.ts`. +```tsx +import { Capacitor } from '@capacitor/core'; +``` +Then update `savePicture()` to look like the following: ```tsx // Save picture to file on device private async savePicture(photo: Photo) { From 08cf534ad46d42eb478cd65016a9605cb6544332 Mon Sep 17 00:00:00 2001 From: soundproofboot Date: Mon, 16 Jun 2025 12:37:34 -0500 Subject: [PATCH 12/12] docs(angular): ran linter to format changes to angular walk through --- docs/angular/your-first-app.md | 15 +++----- .../angular/your-first-app/2-taking-photos.md | 35 ++++++++----------- .../angular/your-first-app/3-saving-photos.md | 30 ++++++++-------- .../your-first-app/4-loading-photos.md | 27 +++++++------- .../angular/your-first-app/5-adding-mobile.md | 2 ++ 5 files changed, 52 insertions(+), 57 deletions(-) diff --git a/docs/angular/your-first-app.md b/docs/angular/your-first-app.md index 99a61d9ead3..0f14b8a0e2e 100644 --- a/docs/angular/your-first-app.md +++ b/docs/angular/your-first-app.md @@ -117,8 +117,9 @@ import { defineCustomElements } from '@ionic/pwa-elements/loader'; // Added impo // Call the element loader before the bootstrapModule/bootstrapApplication call defineCustomElements(window); -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.log(err)); ``` That’s it! Now for the fun part - let’s see the app in action. @@ -144,9 +145,7 @@ Open the photo-gallery app folder in your code editor of choice, then navigate t ```html - - Tab 2 - + Tab 2 @@ -172,9 +171,7 @@ We put the visual aspects of our app into ``. In this case, it’s ```html - - Tab 2 - + Tab 2 @@ -201,7 +198,6 @@ Next, open `src/app/tabs/tabs.page.html`. Change the label to “Photos” and t ```html - @@ -220,7 +216,6 @@ Next, open `src/app/tabs/tabs.page.html`. Change the label to “Photos” and t Tab 3 - ``` diff --git a/docs/angular/your-first-app/2-taking-photos.md b/docs/angular/your-first-app/2-taking-photos.md index 2cb41f30321..3389362c2f5 100644 --- a/docs/angular/your-first-app/2-taking-photos.md +++ b/docs/angular/your-first-app/2-taking-photos.md @@ -53,18 +53,17 @@ import { Filesystem, Directory } from '@capacitor/filesystem'; import { Preferences } from '@capacitor/preferences'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PhotoService { - - constructor() { } + constructor() {} public async addNewToGallery() { // Take a photo const capturedPhoto = await Camera.getPhoto({ resultType: CameraResultType.Uri, source: CameraSource.Camera, - quality: 100 + quality: 100, }); } } @@ -83,9 +82,8 @@ import { PhotoService } from '../services/photo.service'; standalone: false, }) export class Tab2Page { - // update constructor to include photoService - constructor(public photoService: PhotoService) { } + constructor(public photoService: PhotoService) {} // add addNewToGallery method addPhotoToGallery() { @@ -99,9 +97,7 @@ Then, open `tab2.page.html` and call the `addPhotoToGallery()` function when the ```html - - Tab 2 - + Tab 2 @@ -118,7 +114,6 @@ Then, open `tab2.page.html` and call the `addPhotoToGallery()` function when the - ``` @@ -147,7 +142,7 @@ Back at the top of the `PhotoService` class definition, define an array of Photo export class PhotoService { public photos: UserPhoto[] = []; - constructor() { } + constructor() {} // other code } @@ -170,6 +165,7 @@ public async addNewToGallery() { }); } ``` + `photo.service.ts` should now look like this: ```tsx @@ -179,23 +175,23 @@ import { Filesystem, Directory } from '@capacitor/filesystem'; import { Preferences } from '@capacitor/preferences'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PhotoService { public photos: UserPhoto[] = []; - constructor() { } + constructor() {} public async addNewToGallery() { const capturedPhoto = await Camera.getPhoto({ resultType: CameraResultType.Uri, source: CameraSource.Camera, - quality: 100 + quality: 100, }); // add new photo to photos array this.photos.unshift({ - filepath: "soon...", - webviewPath: capturedPhoto.webPath! + filepath: 'soon...', + webviewPath: capturedPhoto.webPath!, }); } } @@ -205,14 +201,13 @@ export interface UserPhoto { webviewPath?: string; } ``` + Next, move over to `tab2.page.html` so we can display the image on the screen. Add a [Grid component](https://ionicframework.com/docs/api/grid) so that each photo will display nicely as photos are added to the gallery, and loop through each photo in the `PhotoServices`'s Photos array, adding an Image component (``) for each. Point the `src` (source) at the photo’s path: ```html - - Tab 2 - + Tab 2 @@ -238,9 +233,9 @@ Next, move over to `tab2.page.html` so we can display the image on the screen. A - ``` + :::note Learn more about the [ngFor core directive](https://blog.angular-university.io/angular-2-ngfor/). ::: diff --git a/docs/angular/your-first-app/3-saving-photos.md b/docs/angular/your-first-app/3-saving-photos.md index a162a608a55..4eef54f42d4 100644 --- a/docs/angular/your-first-app/3-saving-photos.md +++ b/docs/angular/your-first-app/3-saving-photos.md @@ -86,17 +86,17 @@ import { Filesystem, Directory } from '@capacitor/filesystem'; import { Preferences } from '@capacitor/preferences'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PhotoService { public photos: UserPhoto[] = []; - constructor() { } + constructor() {} public async addNewToGallery() { const capturedPhoto = await Camera.getPhoto({ resultType: CameraResultType.Uri, source: CameraSource.Camera, - quality: 100 + quality: 100, }); // Save the picture and add it to photo collection @@ -114,14 +114,14 @@ export class PhotoService { const savedFile = await Filesystem.writeFile({ path: fileName, data: base64Data, - directory: Directory.Data + directory: Directory.Data, }); // Use webPath to display the new image instead of base64 since it's // already loaded into memory return { filepath: fileName, - webviewPath: photo.webPath + webviewPath: photo.webPath, }; } @@ -130,17 +130,18 @@ export class PhotoService { const response = await fetch(photo.webPath!); const blob = await response.blob(); - return await this.convertBlobToBase64(blob) as string; + return (await this.convertBlobToBase64(blob)) as string; } - private convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onerror = reject; - reader.onload = () => { + private convertBlobToBase64 = (blob: Blob) => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = reject; + reader.onload = () => { resolve(reader.result); - }; - reader.readAsDataURL(blob); - }); + }; + reader.readAsDataURL(blob); + }); } export interface UserPhoto { @@ -148,7 +149,8 @@ export interface UserPhoto { webviewPath?: string; } ``` + Obtaining the camera photo as base64 format on the web appears to be a bit trickier than on mobile. In reality, we’re just using built-in web APIs: [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) as a neat way to read the file into blob format, then FileReader’s [readAsDataURL()](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL) to convert the photo blob to base64. There we go! Each time a new photo is taken, it’s now automatically saved to the filesystem. Next up, we'll load and display our saved images -when the user navigates to the Photos tab. \ No newline at end of file +when the user navigates to the Photos tab. diff --git a/docs/angular/your-first-app/4-loading-photos.md b/docs/angular/your-first-app/4-loading-photos.md index e87abec44cb..15ba3338fcd 100644 --- a/docs/angular/your-first-app/4-loading-photos.md +++ b/docs/angular/your-first-app/4-loading-photos.md @@ -74,6 +74,7 @@ for (let photo of this.photos) { photo.webviewPath = `data:image/jpeg;base64,${readFile.data}`; } ``` + After these updates to the `PhotoService` class, your `photos.service.ts` file should look like this: ```tsx @@ -83,9 +84,8 @@ import { Filesystem, Directory } from '@capacitor/filesystem'; import { Preferences } from '@capacitor/preferences'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) - export class PhotoService { public photos: UserPhoto[] = []; private PHOTO_STORAGE = 'photos'; @@ -139,14 +139,14 @@ export class PhotoService { const savedFile = await Filesystem.writeFile({ path: fileName, data: base64Data, - directory: Directory.Data + directory: Directory.Data, }); // Use webPath to display the new image instead of base64 since it's // already loaded into memory return { filepath: fileName, - webviewPath: photo.webPath + webviewPath: photo.webPath, }; } @@ -155,17 +155,18 @@ export class PhotoService { const response = await fetch(photo.webPath!); const blob = await response.blob(); - return await this.convertBlobToBase64(blob) as string; + return (await this.convertBlobToBase64(blob)) as string; } - private convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onerror = reject; - reader.onload = () => { + private convertBlobToBase64 = (blob: Blob) => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = reject; + reader.onload = () => { resolve(reader.result); - }; - reader.readAsDataURL(blob); - }); + }; + reader.readAsDataURL(blob); + }); } export interface UserPhoto { @@ -187,7 +188,6 @@ import { PhotoService } from '../services/photo.service'; standalone: false, }) export class Tab2Page { - constructor(public photoService: PhotoService) {} // add call to loadSaved on navigation to Photos tab @@ -200,6 +200,7 @@ export class Tab2Page { } } ``` + :::note If you're seeing broken image links or missing photos after following these steps, you may need to open your browser's dev tools and clear both [localStorage](https://developer.chrome.com/docs/devtools/storage/localstorage) and [IndexedDB](https://developer.chrome.com/docs/devtools/storage/indexeddb). diff --git a/docs/angular/your-first-app/5-adding-mobile.md b/docs/angular/your-first-app/5-adding-mobile.md index 4de0c0d0eb4..e1d4b508b62 100644 --- a/docs/angular/your-first-app/5-adding-mobile.md +++ b/docs/angular/your-first-app/5-adding-mobile.md @@ -70,7 +70,9 @@ top of `photo.service.ts`. ```tsx import { Capacitor } from '@capacitor/core'; ``` + Then update `savePicture()` to look like the following: + ```tsx // Save picture to file on device private async savePicture(photo: Photo) {