diff --git a/crowdin.yml b/crowdin.yml index c228fc16da1..83f8c20152a 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -9,7 +9,8 @@ files: translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name% ignore: - /docs/api/**/* - - /docs/cli/commands/**/* + - source: /guide/**/* + translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name% - source: /native/**/* translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name% ignore: diff --git a/docs/developing/config.md b/docs/developing/config.md deleted file mode 100644 index 10ad73cec8d..00000000000 --- a/docs/developing/config.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Config ---- - -Ionic Config provides a way to change the properties of components globally across an app. It can set the app mode, tab button layout, animations, and more. - -## Global Config - -The available config keys can be found in the [`IonicConfig`](#ionicconfig) interface. - -The following example disables ripple effects and default the mode to Material Design: - -import GlobalExample from '@site/docs/developing/config/global/index.md'; - - - -## Per-Component Config - -Ionic Config is not reactive. Updating the config's value after the component has rendered will result in the previous value. It is recommended to use a component's properties instead of updating the config, when you require reactive values. - -import PerComponentExample from '@site/docs/developing/config/per-component/index.md'; - - - - -## Per-Platform Config - -Ionic Config can also be set on a per-platform basis. For example, this allows you to disable animations if the app is being run in a browser on a potentially slower device. Developers can take advantage of the Platform utilities to accomplish this. - -In the following example, we are disabling all animations in our Ionic app only if the app is running in a mobile web browser. - -import PerPlatformExample from '@site/docs/developing/config/per-platform/index.md'; - - - - -### Fallbacks - -The next example allows you to set an entirely different configuration based upon the platform, falling back to a default config if no platforms match: - -import PerPlatformFallbackExample from '@site/docs/developing/config/per-platform-overrides/index.md'; - - - -### Overrides - -This final example allows you to accumulate a config object based upon different platform requirements. - -import PerPlatformOverridesExample from '@site/docs/developing/config/per-platform-fallback/index.md'; - - - -## Reading the Config (Angular) - -Ionic Angular provides a `Config` provider for accessing the Ionic Config. - -### get - -| | | -| --------------- | -------------------------------------------------------------------------------- | -| **Description** | Returns a config value as an `any`. Returns `null` if the config is not defined. | -| **Signature** | `get(key: string, fallback?: any) => any` | - -#### Examples - -```ts -import { Config } from '@ionic/angular'; - -@Component(...) -class AppComponent { - constructor(config: Config) { - const mode = config.get('mode'); - } -} -``` - -### getBoolean - -| | | -| --------------- | ------------------------------------------------------------------------------------ | -| **Description** | Returns a config value as a `boolean`. Returns `false` if the config is not defined. | -| **Signature** | `getBoolean(key: string, fallback?: boolean) => boolean` | - -#### Examples - -```ts -import { Config } from '@ionic/angular'; - -@Component(...) -class AppComponent { - constructor(config: Config) { - const swipeBackEnabled = config.getBoolean('swipeBackEnabled'); - } -} -``` - -### getNumber - -| | | -| --------------- | ------------------------------------------------------------------------------- | -| **Description** | Returns a config value as a `number`. Returns `0` if the config is not defined. | -| **Signature** | `getNumber(key: string, fallback?: number) => number` | - -## Interfaces - -### IonicConfig - -Below are the config options that Ionic uses. - -| Config | Type | Description | -| ------------------------ | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| `actionSheetEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-action-sheet`, overriding the default "animation". | -| `actionSheetLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-action-sheet`, overriding the default "animation". | -| `alertEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-alert`, overriding the default "animation". | -| `alertLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-alert`, overriding the default "animation". | -| `animated` | `boolean` | If `true`, Ionic will enable all animations and transitions across the app. | -| `backButtonDefaultHref` | `string` | Overrides the default value for the `defaultHref` property in all `` components. | -| `backButtonIcon` | `string` | Overrides the default icon in all `` components. | -| `backButtonText` | `string` | Overrides the default text in all `` components. | -| `innerHTMLTemplatesEnabled` | `boolean` | Relevant Components: `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, `ion-toast`. If `true`, content passed to the relevant components will be parsed as HTML instead of plaintext. Defaults to `false`. | -| `hardwareBackButton` | `boolean` | If `true`, Ionic will respond to the hardware back button in an Android device. | -| `infiniteLoadingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | -| `loadingEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-loading`, overriding the default "animation". | -| `loadingLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-loading`, overriding the default "animation". | -| `loadingSpinner` | `SpinnerTypes` | Overrides the default spinner for all `ion-loading` overlays. | -| `menuIcon` | `string` | Overrides the default icon in all `` components. | -| `menuType` | `string` | Overrides the default menu type for all `` components. | -| `modalEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-modal`, overriding the default "animation". | -| `modalLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-modal`, overriding the default "animation". | -| `mode` | `Mode` | The mode determines which platform styles to use for the whole application. | -| `navAnimation` | `AnimationBuilder` | Overrides the default "animation" of all `ion-nav` and `ion-router-outlet` across the whole application. | -| `pickerEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-picker`, overriding the default "animation". | -| `pickerLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-picker`, overriding the default "animation". | -| `platform` | [`PlatformConfig`](/docs/angular/platform#customizing-platform-detection-methods) | Overrides the default platform detection methods. | -| `popoverEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-popover`, overriding the default "animation". | -| `popoverLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-popover`, overriding the default "animation". | -| `refreshingIcon` | `string` | Overrides the default icon in all `` components. | -| `refreshingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | -| `sanitizerEnabled` | `boolean` | If `true`, Ionic will enable a basic DOM sanitizer on component properties that accept custom HTML. | -| `spinner` | `SpinnerTypes` | Overrides the default spinner in all `` components. | -| `statusTap` | `boolean` | If `true`, clicking or tapping the status bar will cause the content to scroll to the top. | -| `swipeBackEnabled` | `boolean` | If `true`, Ionic will enable the "swipe-to-go-back" gesture across the application. | -| `tabButtonLayout` | `TabButtonLayout` | Overrides the default "layout" of all `ion-bar-button` across the whole application. | -| `toastDuration` | `number` | Overrides the default `duration` for all `ion-toast` components. | -| `toastEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-toast`, overriding the default "animation". | -| `toastLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-toast`, overriding the default "animation". | -| `toggleOnOffLabels` | `boolean` | Overrides the default `enableOnOffLabels` in all `ion-toggle` components. | diff --git a/docusaurus.config.js b/docusaurus.config.js index 41fb8e9bae9..bdf0bb93da4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -74,8 +74,7 @@ module.exports = { }, items: [ { - type: 'doc', - docId: 'index', + to: 'guide', label: 'Guide', position: 'left', }, @@ -278,6 +277,21 @@ module.exports = { }, }, ], + [ + '@docusaurus/plugin-content-docs', + { + id: 'guide', + path: 'guide', + routeBasePath: 'guide', + sidebarPath: require.resolve('./sidebars-guide.js'), + editUrl: ({ versionDocsDirPath, docPath, locale }) => { + if (locale != 'en') { + return 'https://crowdin.com/project/ionic-docs'; + } + return `https://github.com/ionic-team/ionic-docs/edit/main/${versionDocsDirPath}/${docPath}`; + }, + } + ], [ '@docusaurus/plugin-content-docs', { diff --git a/docs/angular/lifecycle.md b/guide/angular/lifecycle.md similarity index 100% rename from docs/angular/lifecycle.md rename to guide/angular/lifecycle.md diff --git a/docs/angular/navigation.md b/guide/angular/navigation.md similarity index 100% rename from docs/angular/navigation.md rename to guide/angular/navigation.md diff --git a/docs/angular/overview.md b/guide/angular/overview.md similarity index 100% rename from docs/angular/overview.md rename to guide/angular/overview.md diff --git a/docs/angular/performance.md b/guide/angular/performance.md similarity index 100% rename from docs/angular/performance.md rename to guide/angular/performance.md diff --git a/docs/angular/platform.md b/guide/angular/platform.md similarity index 100% rename from docs/angular/platform.md rename to guide/angular/platform.md diff --git a/docs/angular/pwa.md b/guide/angular/pwa.md similarity index 100% rename from docs/angular/pwa.md rename to guide/angular/pwa.md diff --git a/docs/angular/slides.md b/guide/angular/slides.md similarity index 100% rename from docs/angular/slides.md rename to guide/angular/slides.md diff --git a/docs/angular/storage.md b/guide/angular/storage.md similarity index 100% rename from docs/angular/storage.md rename to guide/angular/storage.md diff --git a/docs/angular/testing.md b/guide/angular/testing.md similarity index 100% rename from docs/angular/testing.md rename to guide/angular/testing.md diff --git a/docs/angular/virtual-scroll.md b/guide/angular/virtual-scroll.md similarity index 100% rename from docs/angular/virtual-scroll.md rename to guide/angular/virtual-scroll.md diff --git a/docs/angular/your-first-app.md b/guide/angular/your-first-app.md similarity index 100% rename from docs/angular/your-first-app.md rename to guide/angular/your-first-app.md diff --git a/docs/angular/your-first-app/2-taking-photos.md b/guide/angular/your-first-app/2-taking-photos.md similarity index 100% rename from docs/angular/your-first-app/2-taking-photos.md rename to guide/angular/your-first-app/2-taking-photos.md diff --git a/docs/angular/your-first-app/3-saving-photos.md b/guide/angular/your-first-app/3-saving-photos.md similarity index 100% rename from docs/angular/your-first-app/3-saving-photos.md rename to guide/angular/your-first-app/3-saving-photos.md diff --git a/docs/angular/your-first-app/4-loading-photos.md b/guide/angular/your-first-app/4-loading-photos.md similarity index 100% rename from docs/angular/your-first-app/4-loading-photos.md rename to guide/angular/your-first-app/4-loading-photos.md diff --git a/docs/angular/your-first-app/5-adding-mobile.md b/guide/angular/your-first-app/5-adding-mobile.md similarity index 100% rename from docs/angular/your-first-app/5-adding-mobile.md rename to guide/angular/your-first-app/5-adding-mobile.md diff --git a/docs/angular/your-first-app/6-deploying-mobile.md b/guide/angular/your-first-app/6-deploying-mobile.md similarity index 100% rename from docs/angular/your-first-app/6-deploying-mobile.md rename to guide/angular/your-first-app/6-deploying-mobile.md diff --git a/docs/angular/your-first-app/7-live-reload.md b/guide/angular/your-first-app/7-live-reload.md similarity index 100% rename from docs/angular/your-first-app/7-live-reload.md rename to guide/angular/your-first-app/7-live-reload.md diff --git a/docs/angular/your-first-app/8-distribute.md b/guide/angular/your-first-app/8-distribute.md similarity index 100% rename from docs/angular/your-first-app/8-distribute.md rename to guide/angular/your-first-app/8-distribute.md diff --git a/docs/contributing/coc.md b/guide/contributing/coc.md similarity index 100% rename from docs/contributing/coc.md rename to guide/contributing/coc.md diff --git a/docs/contributing/how-to-contribute.md b/guide/contributing/how-to-contribute.md similarity index 100% rename from docs/contributing/how-to-contribute.md rename to guide/contributing/how-to-contribute.md diff --git a/docs/core-concepts/cross-platform.md b/guide/core-concepts/cross-platform.md similarity index 100% rename from docs/core-concepts/cross-platform.md rename to guide/core-concepts/cross-platform.md diff --git a/docs/core-concepts/fundamentals.md b/guide/core-concepts/fundamentals.md similarity index 100% rename from docs/core-concepts/fundamentals.md rename to guide/core-concepts/fundamentals.md diff --git a/docs/core-concepts/webview.md b/guide/core-concepts/webview.md similarity index 100% rename from docs/core-concepts/webview.md rename to guide/core-concepts/webview.md diff --git a/docs/core-concepts/what-are-progressive-web-apps.md b/guide/core-concepts/what-are-progressive-web-apps.md similarity index 100% rename from docs/core-concepts/what-are-progressive-web-apps.md rename to guide/core-concepts/what-are-progressive-web-apps.md diff --git a/docs/deployment/app-store.md b/guide/deployment/app-store.md similarity index 100% rename from docs/deployment/app-store.md rename to guide/deployment/app-store.md diff --git a/docs/deployment/desktop-app.md b/guide/deployment/desktop-app.md similarity index 100% rename from docs/deployment/desktop-app.md rename to guide/deployment/desktop-app.md diff --git a/docs/deployment/play-store.mdx b/guide/deployment/play-store.mdx similarity index 100% rename from docs/deployment/play-store.mdx rename to guide/deployment/play-store.mdx diff --git a/docs/deployment/progressive-web-app.md b/guide/deployment/progressive-web-app.md similarity index 100% rename from docs/deployment/progressive-web-app.md rename to guide/deployment/progressive-web-app.md diff --git a/docs/developer-resources/books.md b/guide/developer-resources/books.md similarity index 100% rename from docs/developer-resources/books.md rename to guide/developer-resources/books.md diff --git a/docs/developer-resources/courses.md b/guide/developer-resources/courses.md similarity index 100% rename from docs/developer-resources/courses.md rename to guide/developer-resources/courses.md diff --git a/docs/developer-resources/guides.md b/guide/developer-resources/guides.md similarity index 100% rename from docs/developer-resources/guides.md rename to guide/developer-resources/guides.md diff --git a/docs/developer-resources/guides/first-app-v3/creating-photo-gallery-device-storage.md b/guide/developer-resources/guides/first-app-v3/creating-photo-gallery-device-storage.md similarity index 100% rename from docs/developer-resources/guides/first-app-v3/creating-photo-gallery-device-storage.md rename to guide/developer-resources/guides/first-app-v3/creating-photo-gallery-device-storage.md diff --git a/docs/developer-resources/guides/first-app-v3/intro.md b/guide/developer-resources/guides/first-app-v3/intro.md similarity index 100% rename from docs/developer-resources/guides/first-app-v3/intro.md rename to guide/developer-resources/guides/first-app-v3/intro.md diff --git a/docs/developer-resources/guides/first-app-v3/ios-android-camera.md b/guide/developer-resources/guides/first-app-v3/ios-android-camera.md similarity index 100% rename from docs/developer-resources/guides/first-app-v3/ios-android-camera.md rename to guide/developer-resources/guides/first-app-v3/ios-android-camera.md diff --git a/docs/developer-resources/guides/first-app-v3/realtime-updates-ionic-deploy.md b/guide/developer-resources/guides/first-app-v3/realtime-updates-ionic-deploy.md similarity index 100% rename from docs/developer-resources/guides/first-app-v3/realtime-updates-ionic-deploy.md rename to guide/developer-resources/guides/first-app-v3/realtime-updates-ionic-deploy.md diff --git a/docs/developer-resources/guides/first-app-v3/theming.md b/guide/developer-resources/guides/first-app-v3/theming.md similarity index 100% rename from docs/developer-resources/guides/first-app-v3/theming.md rename to guide/developer-resources/guides/first-app-v3/theming.md diff --git a/docs/developer-resources/guides/first-app-v3/track-bugs-ionic-monitoring.md b/guide/developer-resources/guides/first-app-v3/track-bugs-ionic-monitoring.md similarity index 100% rename from docs/developer-resources/guides/first-app-v3/track-bugs-ionic-monitoring.md rename to guide/developer-resources/guides/first-app-v3/track-bugs-ionic-monitoring.md diff --git a/docs/developer-resources/guides/first-app-v4/creating-photo-gallery-device-storage.md b/guide/developer-resources/guides/first-app-v4/creating-photo-gallery-device-storage.md similarity index 100% rename from docs/developer-resources/guides/first-app-v4/creating-photo-gallery-device-storage.md rename to guide/developer-resources/guides/first-app-v4/creating-photo-gallery-device-storage.md diff --git a/docs/developer-resources/guides/first-app-v4/intro.md b/guide/developer-resources/guides/first-app-v4/intro.md similarity index 100% rename from docs/developer-resources/guides/first-app-v4/intro.md rename to guide/developer-resources/guides/first-app-v4/intro.md diff --git a/docs/developer-resources/guides/first-app-v4/ios-android-camera.md b/guide/developer-resources/guides/first-app-v4/ios-android-camera.md similarity index 100% rename from docs/developer-resources/guides/first-app-v4/ios-android-camera.md rename to guide/developer-resources/guides/first-app-v4/ios-android-camera.md diff --git a/docs/developer-resources/guides/first-app-v4/theming.md b/guide/developer-resources/guides/first-app-v4/theming.md similarity index 100% rename from docs/developer-resources/guides/first-app-v4/theming.md rename to guide/developer-resources/guides/first-app-v4/theming.md diff --git a/docs/developer-resources/posts.md b/guide/developer-resources/posts.md similarity index 100% rename from docs/developer-resources/posts.md rename to guide/developer-resources/posts.md diff --git a/docs/developer-resources/tools.md b/guide/developer-resources/tools.md similarity index 100% rename from docs/developer-resources/tools.md rename to guide/developer-resources/tools.md diff --git a/docs/developer-resources/videos.md b/guide/developer-resources/videos.md similarity index 100% rename from docs/developer-resources/videos.md rename to guide/developer-resources/videos.md diff --git a/docs/developing/android.md b/guide/developing/android.md similarity index 100% rename from docs/developing/android.md rename to guide/developing/android.md diff --git a/versioned_docs/version-v6/developing/config.md b/guide/developing/config.md similarity index 98% rename from versioned_docs/version-v6/developing/config.md rename to guide/developing/config.md index 662e790864d..c234d117b53 100644 --- a/versioned_docs/version-v6/developing/config.md +++ b/guide/developing/config.md @@ -117,7 +117,7 @@ Below are the config options that Ionic uses. | `backButtonDefaultHref` | `string` | Overrides the default value for the `defaultHref` property in all `` components. | | `backButtonIcon` | `string` | Overrides the default icon in all `` components. | | `backButtonText` | `string` | Overrides the default text in all `` components. | -| `innerHTMLTemplatesEnabled` | `boolean` | Relevant Components: `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, `ion-toast`. If `false`, all `innerHTML` usage will be disabled in Ionic, and custom HTML will not be usable in the relevant components. `innerHTML` usage is enabled by default. | +| `innerHTMLTemplatesEnabled` | `boolean` | Relevant Components: `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, `ion-toast`. If `true`, content passed to the relevant components will be parsed as HTML instead of plaintext. Defaults to `false`. | | `hardwareBackButton` | `boolean` | If `true`, Ionic will respond to the hardware back button in an Android device. | | `infiniteLoadingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | | `loadingEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-loading`, overriding the default "animation". | diff --git a/docs/developing/config/global/index.md b/guide/developing/config/global/index.md similarity index 100% rename from docs/developing/config/global/index.md rename to guide/developing/config/global/index.md diff --git a/docs/developing/config/per-component/index.md b/guide/developing/config/per-component/index.md similarity index 100% rename from docs/developing/config/per-component/index.md rename to guide/developing/config/per-component/index.md diff --git a/docs/developing/config/per-platform-fallback/index.md b/guide/developing/config/per-platform-fallback/index.md similarity index 100% rename from docs/developing/config/per-platform-fallback/index.md rename to guide/developing/config/per-platform-fallback/index.md diff --git a/docs/developing/config/per-platform-overrides/index.md b/guide/developing/config/per-platform-overrides/index.md similarity index 100% rename from docs/developing/config/per-platform-overrides/index.md rename to guide/developing/config/per-platform-overrides/index.md diff --git a/docs/developing/config/per-platform/index.md b/guide/developing/config/per-platform/index.md similarity index 100% rename from docs/developing/config/per-platform/index.md rename to guide/developing/config/per-platform/index.md diff --git a/docs/developing/hardware-back-button.md b/guide/developing/hardware-back-button.md similarity index 100% rename from docs/developing/hardware-back-button.md rename to guide/developing/hardware-back-button.md diff --git a/docs/developing/ios.md b/guide/developing/ios.md similarity index 100% rename from docs/developing/ios.md rename to guide/developing/ios.md diff --git a/docs/developing/keyboard.md b/guide/developing/keyboard.md similarity index 100% rename from docs/developing/keyboard.md rename to guide/developing/keyboard.md diff --git a/docs/developing/previewing.md b/guide/developing/previewing.md similarity index 100% rename from docs/developing/previewing.md rename to guide/developing/previewing.md diff --git a/docs/developing/scaffolding.md b/guide/developing/scaffolding.md similarity index 100% rename from docs/developing/scaffolding.md rename to guide/developing/scaffolding.md diff --git a/docs/developing/starting.md b/guide/developing/starting.md similarity index 100% rename from docs/developing/starting.md rename to guide/developing/starting.md diff --git a/docs/developing/tips.md b/guide/developing/tips.md similarity index 100% rename from docs/developing/tips.md rename to guide/developing/tips.md diff --git a/docs/index.md b/guide/index.md similarity index 96% rename from docs/index.md rename to guide/index.md index 5a5909b1b3e..db841e894a0 100644 --- a/docs/index.md +++ b/guide/index.md @@ -29,7 +29,7 @@ Get started building by [installing Ionic](intro/cli.md) or following our [First - +

Step-by-step guides to setting up your system and installing the framework.

@@ -51,7 +51,7 @@ Get started building by [installing Ionic](intro/cli.md) or following our [First

Integrate native device plugins, like Bluetooth, Maps, HealthKit, and more.

- +

Learn to easily customize and modify your Ionic app's visual design to fit your brand.

diff --git a/docs/intro/cdn.md b/guide/intro/cdn.md similarity index 100% rename from docs/intro/cdn.md rename to guide/intro/cdn.md diff --git a/docs/intro/cli.md b/guide/intro/cli.md similarity index 100% rename from docs/intro/cli.md rename to guide/intro/cli.md diff --git a/docs/intro/environment.md b/guide/intro/environment.md similarity index 100% rename from docs/intro/environment.md rename to guide/intro/environment.md diff --git a/docs/intro/first-app.md b/guide/intro/first-app.md similarity index 100% rename from docs/intro/first-app.md rename to guide/intro/first-app.md diff --git a/docs/intro/next.md b/guide/intro/next.md similarity index 100% rename from docs/intro/next.md rename to guide/intro/next.md diff --git a/docs/intro/vscode-extension.md b/guide/intro/vscode-extension.md similarity index 100% rename from docs/intro/vscode-extension.md rename to guide/intro/vscode-extension.md diff --git a/docs/layout/css-utilities.md b/guide/layout/css-utilities.md similarity index 100% rename from docs/layout/css-utilities.md rename to guide/layout/css-utilities.md diff --git a/docs/layout/global-stylesheets.md b/guide/layout/global-stylesheets.md similarity index 100% rename from docs/layout/global-stylesheets.md rename to guide/layout/global-stylesheets.md diff --git a/docs/layout/structure.md b/guide/layout/structure.md similarity index 100% rename from docs/layout/structure.md rename to guide/layout/structure.md diff --git a/docs/react.md b/guide/react.md similarity index 100% rename from docs/react.md rename to guide/react.md diff --git a/docs/react/adding-ionic-react-to-an-existing-react-project.md b/guide/react/adding-ionic-react-to-an-existing-react-project.md similarity index 100% rename from docs/react/adding-ionic-react-to-an-existing-react-project.md rename to guide/react/adding-ionic-react-to-an-existing-react-project.md diff --git a/docs/react/lifecycle.md b/guide/react/lifecycle.md similarity index 100% rename from docs/react/lifecycle.md rename to guide/react/lifecycle.md diff --git a/docs/react/navigation.md b/guide/react/navigation.md similarity index 100% rename from docs/react/navigation.md rename to guide/react/navigation.md diff --git a/docs/react/overlays.md b/guide/react/overlays.md similarity index 100% rename from docs/react/overlays.md rename to guide/react/overlays.md diff --git a/docs/react/overview.md b/guide/react/overview.md similarity index 100% rename from docs/react/overview.md rename to guide/react/overview.md diff --git a/docs/react/performance.md b/guide/react/performance.md similarity index 100% rename from docs/react/performance.md rename to guide/react/performance.md diff --git a/docs/react/platform.md b/guide/react/platform.md similarity index 100% rename from docs/react/platform.md rename to guide/react/platform.md diff --git a/docs/react/pwa.md b/guide/react/pwa.md similarity index 100% rename from docs/react/pwa.md rename to guide/react/pwa.md diff --git a/docs/react/quickstart.md b/guide/react/quickstart.md similarity index 100% rename from docs/react/quickstart.md rename to guide/react/quickstart.md diff --git a/docs/react/slides.md b/guide/react/slides.md similarity index 100% rename from docs/react/slides.md rename to guide/react/slides.md diff --git a/docs/react/storage.md b/guide/react/storage.md similarity index 100% rename from docs/react/storage.md rename to guide/react/storage.md diff --git a/docs/react/testing.md b/guide/react/testing.md similarity index 100% rename from docs/react/testing.md rename to guide/react/testing.md diff --git a/docs/react/virtual-scroll.md b/guide/react/virtual-scroll.md similarity index 100% rename from docs/react/virtual-scroll.md rename to guide/react/virtual-scroll.md diff --git a/docs/react/your-first-app.md b/guide/react/your-first-app.md similarity index 100% rename from docs/react/your-first-app.md rename to guide/react/your-first-app.md diff --git a/docs/react/your-first-app/2-taking-photos.md b/guide/react/your-first-app/2-taking-photos.md similarity index 100% rename from docs/react/your-first-app/2-taking-photos.md rename to guide/react/your-first-app/2-taking-photos.md diff --git a/docs/react/your-first-app/3-saving-photos.md b/guide/react/your-first-app/3-saving-photos.md similarity index 100% rename from docs/react/your-first-app/3-saving-photos.md rename to guide/react/your-first-app/3-saving-photos.md diff --git a/docs/react/your-first-app/4-loading-photos.md b/guide/react/your-first-app/4-loading-photos.md similarity index 100% rename from docs/react/your-first-app/4-loading-photos.md rename to guide/react/your-first-app/4-loading-photos.md diff --git a/docs/react/your-first-app/5-adding-mobile.md b/guide/react/your-first-app/5-adding-mobile.md similarity index 100% rename from docs/react/your-first-app/5-adding-mobile.md rename to guide/react/your-first-app/5-adding-mobile.md diff --git a/docs/react/your-first-app/6-deploying-mobile.md b/guide/react/your-first-app/6-deploying-mobile.md similarity index 100% rename from docs/react/your-first-app/6-deploying-mobile.md rename to guide/react/your-first-app/6-deploying-mobile.md diff --git a/docs/react/your-first-app/7-live-reload.md b/guide/react/your-first-app/7-live-reload.md similarity index 100% rename from docs/react/your-first-app/7-live-reload.md rename to guide/react/your-first-app/7-live-reload.md diff --git a/docs/react/your-first-app/8-distribute.md b/guide/react/your-first-app/8-distribute.md similarity index 100% rename from docs/react/your-first-app/8-distribute.md rename to guide/react/your-first-app/8-distribute.md diff --git a/docs/reference/browser-support.md b/guide/reference/browser-support.md similarity index 100% rename from docs/reference/browser-support.md rename to guide/reference/browser-support.md diff --git a/docs/reference/glossary.md b/guide/reference/glossary.md similarity index 100% rename from docs/reference/glossary.md rename to guide/reference/glossary.md diff --git a/docs/reference/release-notes.md b/guide/reference/release-notes.md similarity index 100% rename from docs/reference/release-notes.md rename to guide/reference/release-notes.md diff --git a/docs/reference/support.md b/guide/reference/support.md similarity index 100% rename from docs/reference/support.md rename to guide/reference/support.md diff --git a/docs/reference/versioning.md b/guide/reference/versioning.md similarity index 100% rename from docs/reference/versioning.md rename to guide/reference/versioning.md diff --git a/docs/studio/assets.md b/guide/studio/assets.md similarity index 100% rename from docs/studio/assets.md rename to guide/studio/assets.md diff --git a/docs/studio/code.md b/guide/studio/code.md similarity index 100% rename from docs/studio/code.md rename to guide/studio/code.md diff --git a/docs/studio/designer.md b/guide/studio/designer.md similarity index 100% rename from docs/studio/designer.md rename to guide/studio/designer.md diff --git a/docs/studio/faq.md b/guide/studio/faq.md similarity index 100% rename from docs/studio/faq.md rename to guide/studio/faq.md diff --git a/docs/studio/guides/routing-and-navigation.md b/guide/studio/guides/routing-and-navigation.md similarity index 100% rename from docs/studio/guides/routing-and-navigation.md rename to guide/studio/guides/routing-and-navigation.md diff --git a/docs/studio/guides/using-native-functionality.md b/guide/studio/guides/using-native-functionality.md similarity index 100% rename from docs/studio/guides/using-native-functionality.md rename to guide/studio/guides/using-native-functionality.md diff --git a/docs/studio/running.md b/guide/studio/running.md similarity index 100% rename from docs/studio/running.md rename to guide/studio/running.md diff --git a/docs/studio/settings.md b/guide/studio/settings.md similarity index 100% rename from docs/studio/settings.md rename to guide/studio/settings.md diff --git a/docs/studio/setup/installation.md b/guide/studio/setup/installation.md similarity index 100% rename from docs/studio/setup/installation.md rename to guide/studio/setup/installation.md diff --git a/docs/studio/setup/native.md b/guide/studio/setup/native.md similarity index 100% rename from docs/studio/setup/native.md rename to guide/studio/setup/native.md diff --git a/docs/studio/start.md b/guide/studio/start.md similarity index 100% rename from docs/studio/start.md rename to guide/studio/start.md diff --git a/docs/studio/support.md b/guide/studio/support.md similarity index 100% rename from docs/studio/support.md rename to guide/studio/support.md diff --git a/docs/studio/terminal.md b/guide/studio/terminal.md similarity index 100% rename from docs/studio/terminal.md rename to guide/studio/terminal.md diff --git a/docs/studio/theming.md b/guide/studio/theming.md similarity index 100% rename from docs/studio/theming.md rename to guide/studio/theming.md diff --git a/docs/techniques/security.md b/guide/techniques/security.md similarity index 100% rename from docs/techniques/security.md rename to guide/techniques/security.md diff --git a/docs/test/page1.md b/guide/test/page1.md similarity index 100% rename from docs/test/page1.md rename to guide/test/page1.md diff --git a/docs/test/page2.md b/guide/test/page2.md similarity index 100% rename from docs/test/page2.md rename to guide/test/page2.md diff --git a/docs/theming/advanced.md b/guide/theming/advanced.md similarity index 100% rename from docs/theming/advanced.md rename to guide/theming/advanced.md diff --git a/docs/theming/basics.md b/guide/theming/basics.md similarity index 100% rename from docs/theming/basics.md rename to guide/theming/basics.md diff --git a/docs/theming/color-generator.md b/guide/theming/color-generator.md similarity index 100% rename from docs/theming/color-generator.md rename to guide/theming/color-generator.md diff --git a/docs/theming/colors.md b/guide/theming/colors.md similarity index 100% rename from docs/theming/colors.md rename to guide/theming/colors.md diff --git a/docs/theming/css-shadow-parts.md b/guide/theming/css-shadow-parts.md similarity index 100% rename from docs/theming/css-shadow-parts.md rename to guide/theming/css-shadow-parts.md diff --git a/docs/theming/css-variables.md b/guide/theming/css-variables.md similarity index 100% rename from docs/theming/css-variables.md rename to guide/theming/css-variables.md diff --git a/docs/theming/dark-mode.md b/guide/theming/dark-mode.md similarity index 100% rename from docs/theming/dark-mode.md rename to guide/theming/dark-mode.md diff --git a/docs/theming/platform-styles.md b/guide/theming/platform-styles.md similarity index 100% rename from docs/theming/platform-styles.md rename to guide/theming/platform-styles.md diff --git a/docs/theming/themes.md b/guide/theming/themes.md similarity index 100% rename from docs/theming/themes.md rename to guide/theming/themes.md diff --git a/docs/troubleshooting/build.md b/guide/troubleshooting/build.md similarity index 100% rename from docs/troubleshooting/build.md rename to guide/troubleshooting/build.md diff --git a/docs/troubleshooting/cors.md b/guide/troubleshooting/cors.md similarity index 100% rename from docs/troubleshooting/cors.md rename to guide/troubleshooting/cors.md diff --git a/docs/troubleshooting/debugging.md b/guide/troubleshooting/debugging.md similarity index 100% rename from docs/troubleshooting/debugging.md rename to guide/troubleshooting/debugging.md diff --git a/docs/troubleshooting/native.md b/guide/troubleshooting/native.md similarity index 100% rename from docs/troubleshooting/native.md rename to guide/troubleshooting/native.md diff --git a/docs/troubleshooting/runtime.md b/guide/troubleshooting/runtime.md similarity index 100% rename from docs/troubleshooting/runtime.md rename to guide/troubleshooting/runtime.md diff --git a/docs/updating/4-0.md b/guide/updating/4-0.md similarity index 100% rename from docs/updating/4-0.md rename to guide/updating/4-0.md diff --git a/docs/updating/5-0.md b/guide/updating/5-0.md similarity index 100% rename from docs/updating/5-0.md rename to guide/updating/5-0.md diff --git a/docs/updating/6-0.md b/guide/updating/6-0.md similarity index 100% rename from docs/updating/6-0.md rename to guide/updating/6-0.md diff --git a/docs/updating/7-0.md b/guide/updating/7-0.md similarity index 100% rename from docs/updating/7-0.md rename to guide/updating/7-0.md diff --git a/docs/utilities/animations.md b/guide/utilities/animations.md similarity index 100% rename from docs/utilities/animations.md rename to guide/utilities/animations.md diff --git a/docs/utilities/gestures.md b/guide/utilities/gestures.md similarity index 100% rename from docs/utilities/gestures.md rename to guide/utilities/gestures.md diff --git a/docs/vue/lifecycle.md b/guide/vue/lifecycle.md similarity index 100% rename from docs/vue/lifecycle.md rename to guide/vue/lifecycle.md diff --git a/docs/vue/navigation.md b/guide/vue/navigation.md similarity index 100% rename from docs/vue/navigation.md rename to guide/vue/navigation.md diff --git a/docs/vue/overview.md b/guide/vue/overview.md similarity index 100% rename from docs/vue/overview.md rename to guide/vue/overview.md diff --git a/docs/vue/performance.md b/guide/vue/performance.md similarity index 100% rename from docs/vue/performance.md rename to guide/vue/performance.md diff --git a/docs/vue/platform.md b/guide/vue/platform.md similarity index 100% rename from docs/vue/platform.md rename to guide/vue/platform.md diff --git a/docs/vue/pwa.md b/guide/vue/pwa.md similarity index 100% rename from docs/vue/pwa.md rename to guide/vue/pwa.md diff --git a/docs/vue/quickstart.md b/guide/vue/quickstart.md similarity index 100% rename from docs/vue/quickstart.md rename to guide/vue/quickstart.md diff --git a/docs/vue/slides.md b/guide/vue/slides.md similarity index 100% rename from docs/vue/slides.md rename to guide/vue/slides.md diff --git a/docs/vue/storage.md b/guide/vue/storage.md similarity index 100% rename from docs/vue/storage.md rename to guide/vue/storage.md diff --git a/docs/vue/testing.md b/guide/vue/testing.md similarity index 100% rename from docs/vue/testing.md rename to guide/vue/testing.md diff --git a/docs/vue/troubleshooting.md b/guide/vue/troubleshooting.md similarity index 100% rename from docs/vue/troubleshooting.md rename to guide/vue/troubleshooting.md diff --git a/docs/vue/utility-functions.md b/guide/vue/utility-functions.md similarity index 100% rename from docs/vue/utility-functions.md rename to guide/vue/utility-functions.md diff --git a/docs/vue/virtual-scroll.md b/guide/vue/virtual-scroll.md similarity index 100% rename from docs/vue/virtual-scroll.md rename to guide/vue/virtual-scroll.md diff --git a/docs/vue/your-first-app.md b/guide/vue/your-first-app.md similarity index 100% rename from docs/vue/your-first-app.md rename to guide/vue/your-first-app.md diff --git a/docs/vue/your-first-app/2-taking-photos.md b/guide/vue/your-first-app/2-taking-photos.md similarity index 100% rename from docs/vue/your-first-app/2-taking-photos.md rename to guide/vue/your-first-app/2-taking-photos.md diff --git a/docs/vue/your-first-app/3-saving-photos.md b/guide/vue/your-first-app/3-saving-photos.md similarity index 100% rename from docs/vue/your-first-app/3-saving-photos.md rename to guide/vue/your-first-app/3-saving-photos.md diff --git a/docs/vue/your-first-app/4-loading-photos.md b/guide/vue/your-first-app/4-loading-photos.md similarity index 100% rename from docs/vue/your-first-app/4-loading-photos.md rename to guide/vue/your-first-app/4-loading-photos.md diff --git a/docs/vue/your-first-app/5-adding-mobile.md b/guide/vue/your-first-app/5-adding-mobile.md similarity index 100% rename from docs/vue/your-first-app/5-adding-mobile.md rename to guide/vue/your-first-app/5-adding-mobile.md diff --git a/docs/vue/your-first-app/6-deploying-mobile.md b/guide/vue/your-first-app/6-deploying-mobile.md similarity index 100% rename from docs/vue/your-first-app/6-deploying-mobile.md rename to guide/vue/your-first-app/6-deploying-mobile.md diff --git a/docs/vue/your-first-app/7-live-reload.md b/guide/vue/your-first-app/7-live-reload.md similarity index 100% rename from docs/vue/your-first-app/7-live-reload.md rename to guide/vue/your-first-app/7-live-reload.md diff --git a/docs/vue/your-first-app/8-distribute.md b/guide/vue/your-first-app/8-distribute.md similarity index 100% rename from docs/vue/your-first-app/8-distribute.md rename to guide/vue/your-first-app/8-distribute.md diff --git a/sidebars-guide.js b/sidebars-guide.js new file mode 100644 index 00000000000..bf196e100e3 --- /dev/null +++ b/sidebars-guide.js @@ -0,0 +1,236 @@ +module.exports = { + guide: [ + { + type: 'category', + label: 'Getting Started', + collapsed: false, + items: ['index', 'intro/environment', 'intro/cli', 'intro/cdn', 'intro/vscode-extension', 'intro/next'], + }, + { + type: 'category', + label: 'Upgrade Guides', + collapsed: false, + items: [ + 'updating/7-0', + 'updating/6-0', + 'updating/5-0', + 'updating/4-0' + ] + }, + { + type: 'category', + label: 'Developing', + collapsed: false, + items: [ + 'developing/starting', + 'developing/previewing', + 'developing/scaffolding', + 'developing/ios', + 'developing/android', + 'developing/tips', + 'developing/hardware-back-button', + 'developing/keyboard', + 'developing/config' + ], + }, + { + type: 'category', + label: 'Layout', + collapsed: false, + items: [ + 'layout/structure', + { + type: 'link', + label: 'Responsive Grid', + href: '/docs/api/grid', + }, + 'layout/global-stylesheets', + 'layout/css-utilities' + ], + }, + { + type: 'category', + label: 'Theming', + collapsed: false, + items: [ + 'theming/basics', + 'theming/platform-styles', + 'theming/css-variables', + 'theming/css-shadow-parts', + 'theming/colors', + 'theming/themes', + 'theming/dark-mode', + 'theming/advanced', + 'theming/color-generator', + ], + }, + { + type: 'category', + label: 'Angular', + collapsed: false, + items: [ + 'angular/overview', + { + type: 'category', + label: 'Build Your First App', + items: [ + 'angular/your-first-app', + 'angular/your-first-app/taking-photos', + 'angular/your-first-app/saving-photos', + 'angular/your-first-app/loading-photos', + 'angular/your-first-app/adding-mobile', + 'angular/your-first-app/deploying-mobile', + 'angular/your-first-app/live-reload', + 'angular/your-first-app/distribute', + ], + }, + 'angular/lifecycle', + 'angular/navigation', + 'angular/virtual-scroll', + 'angular/slides', + 'angular/platform', + 'angular/testing', + 'angular/storage', + 'angular/performance', + 'angular/pwa', + ], + }, + { + type: 'category', + label: 'React', + collapsed: false, + items: [ + 'react', + 'react/quickstart', + { + type: 'category', + label: 'Build Your First App', + items: [ + 'react/your-first-app', + 'react/your-first-app/taking-photos', + 'react/your-first-app/saving-photos', + 'react/your-first-app/loading-photos', + 'react/your-first-app/adding-mobile', + 'react/your-first-app/deploying-mobile', + 'react/your-first-app/live-reload', + 'react/your-first-app/distribute', + ], + }, + 'react/adding-ionic-react-to-an-existing-react-project', + 'react/lifecycle', + 'react/navigation', + 'react/virtual-scroll', + 'react/slides', + 'react/platform', + 'react/pwa', + 'react/overlays', + 'react/storage', + 'react/testing', + 'react/performance', + ], + }, + { + type: 'category', + label: 'Vue', + collapsed: false, + items: [ + 'vue/overview', + 'vue/quickstart', + { + type: 'category', + label: 'Build Your First App', + items: [ + 'vue/your-first-app', + 'vue/your-first-app/taking-photos', + 'vue/your-first-app/saving-photos', + 'vue/your-first-app/loading-photos', + 'vue/your-first-app/adding-mobile', + 'vue/your-first-app/deploying-mobile', + 'vue/your-first-app/live-reload', + 'vue/your-first-app/distribute', + ], + }, + 'vue/lifecycle', + 'vue/navigation', + 'vue/virtual-scroll', + 'vue/slides', + 'vue/utility-functions', + 'vue/platform', + 'vue/pwa', + 'vue/storage', + 'vue/testing', + 'vue/troubleshooting', + 'vue/performance', + ], + }, + { + type: 'category', + label: 'Utilities', + collapsed: false, + items: ['utilities/animations', 'utilities/gestures'], + }, + { + type: 'category', + label: 'Deployment', + collapsed: false, + items: [ + 'deployment/app-store', + 'deployment/play-store', + 'deployment/progressive-web-app', + 'deployment/desktop-app', + ], + }, + { + type: 'category', + label: 'Techniques', + collapsed: false, + items: ['techniques/security'], + }, + { + type: 'category', + label: 'Troubleshooting', + collapsed: false, + items: [ + 'troubleshooting/debugging', + 'troubleshooting/build', + 'troubleshooting/runtime', + 'troubleshooting/native', + 'troubleshooting/cors', + ], + }, + { + type: 'category', + label: 'Core Concepts', + collapsed: false, + items: [ + 'core-concepts/fundamentals', + 'core-concepts/cross-platform', + 'core-concepts/webview', + 'core-concepts/what-are-progressive-web-apps', + ], + }, + { + type: 'category', + label: 'Contributing', + collapsed: false, + items: ['contributing/how-to-contribute', 'contributing/coc'], + }, + { + type: 'category', + label: 'Reference', + collapsed: false, + items: [ + 'reference/glossary', + 'reference/versioning', + 'reference/release-notes', + { + type: 'link', + label: 'GitHub Changelog', + href: 'https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md', + }, + 'reference/support', + 'reference/browser-support' + ], + }, + ], +} diff --git a/sidebars.js b/sidebars.js index b1bb34bd6de..2da3e64d63e 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,239 +1,4 @@ module.exports = { - docs: [ - { - type: 'category', - label: 'Getting Started', - collapsed: false, - items: ['index', 'intro/environment', 'intro/cli', 'intro/cdn', 'intro/vscode-extension', 'intro/next'], - }, - { - type: 'category', - label: 'Upgrade Guides', - collapsed: false, - items: [ - 'updating/7-0', - 'updating/6-0', - 'updating/5-0', - 'updating/4-0' - ] - }, - { - type: 'category', - label: 'Developing', - collapsed: false, - items: [ - 'developing/starting', - 'developing/previewing', - 'developing/scaffolding', - 'developing/ios', - 'developing/android', - 'developing/tips', - 'developing/hardware-back-button', - 'developing/keyboard', - 'developing/config' - ], - }, - { - type: 'category', - label: 'Layout', - collapsed: false, - items: [ - 'layout/structure', - { - type: 'link', - label: 'Responsive Grid', - href: '/docs/api/grid', - }, - 'layout/global-stylesheets', - 'layout/css-utilities' - ], - }, - { - type: 'category', - label: 'Theming', - collapsed: false, - items: [ - 'theming/basics', - 'theming/platform-styles', - 'theming/css-variables', - 'theming/css-shadow-parts', - 'theming/colors', - 'theming/themes', - 'theming/dark-mode', - 'theming/advanced', - 'theming/color-generator', - ], - }, - { - type: 'category', - label: 'Angular', - collapsed: false, - items: [ - 'angular/overview', - { - type: 'category', - label: 'Build Your First App', - items: [ - 'angular/your-first-app', - 'angular/your-first-app/taking-photos', - 'angular/your-first-app/saving-photos', - 'angular/your-first-app/loading-photos', - 'angular/your-first-app/adding-mobile', - 'angular/your-first-app/deploying-mobile', - 'angular/your-first-app/live-reload', - 'angular/your-first-app/distribute', - ], - }, - 'angular/lifecycle', - 'angular/navigation', - 'angular/virtual-scroll', - 'angular/slides', - 'angular/platform', - 'angular/testing', - 'angular/storage', - 'angular/performance', - 'angular/pwa', - ], - }, - { - type: 'category', - label: 'React', - collapsed: false, - items: [ - 'react', - 'react/quickstart', - { - type: 'category', - label: 'Build Your First App', - items: [ - 'react/your-first-app', - 'react/your-first-app/taking-photos', - 'react/your-first-app/saving-photos', - 'react/your-first-app/loading-photos', - 'react/your-first-app/adding-mobile', - 'react/your-first-app/deploying-mobile', - 'react/your-first-app/live-reload', - 'react/your-first-app/distribute', - ], - }, - 'react/adding-ionic-react-to-an-existing-react-project', - 'react/lifecycle', - 'react/navigation', - 'react/virtual-scroll', - 'react/slides', - 'react/platform', - 'react/pwa', - 'react/overlays', - 'react/storage', - 'react/testing', - 'react/performance', - ], - }, - { - type: 'category', - label: 'Vue', - collapsed: false, - items: [ - 'vue/overview', - 'vue/quickstart', - { - type: 'category', - label: 'Build Your First App', - items: [ - 'vue/your-first-app', - 'vue/your-first-app/taking-photos', - 'vue/your-first-app/saving-photos', - 'vue/your-first-app/loading-photos', - 'vue/your-first-app/adding-mobile', - 'vue/your-first-app/deploying-mobile', - 'vue/your-first-app/live-reload', - 'vue/your-first-app/distribute', - ], - }, - 'vue/lifecycle', - 'vue/navigation', - 'vue/virtual-scroll', - 'vue/slides', - 'vue/utility-functions', - 'vue/platform', - 'vue/pwa', - 'vue/storage', - 'vue/testing', - 'vue/troubleshooting', - 'vue/performance', - ], - }, - { - type: 'category', - label: 'Utilities', - collapsed: false, - items: ['utilities/animations', 'utilities/gestures'], - }, - { - type: 'category', - label: 'Deployment', - collapsed: false, - items: [ - 'deployment/app-store', - 'deployment/play-store', - 'deployment/progressive-web-app', - 'deployment/desktop-app', - ], - }, - { - type: 'category', - label: 'Techniques', - collapsed: false, - items: ['techniques/security'], - }, - { - type: 'category', - label: 'Troubleshooting', - collapsed: false, - items: [ - 'troubleshooting/debugging', - 'troubleshooting/build', - 'troubleshooting/runtime', - 'troubleshooting/native', - 'troubleshooting/cors', - ], - }, - { - type: 'category', - label: 'Core Concepts', - collapsed: false, - items: [ - 'core-concepts/fundamentals', - 'core-concepts/cross-platform', - 'core-concepts/webview', - 'core-concepts/what-are-progressive-web-apps', - ], - }, - { - type: 'category', - label: 'Contributing', - collapsed: false, - items: ['contributing/how-to-contribute', 'contributing/coc'], - }, - { - type: 'category', - label: 'Reference', - collapsed: false, - items: [ - 'reference/glossary', - 'reference/versioning', - 'reference/release-notes', - { - type: 'link', - label: 'GitHub Changelog', - href: 'https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md', - }, - 'reference/support', - 'reference/browser-support' - ], - }, - ], - api: [ { type: 'category', diff --git a/versioned_docs/version-v5/angular/config.md b/versioned_docs/version-v5/angular/config.md deleted file mode 100644 index 0db21cdf0aa..00000000000 --- a/versioned_docs/version-v5/angular/config.md +++ /dev/null @@ -1,175 +0,0 @@ -# Config - -Ionic Config provides a way to change the properties of components globally across an app. It can set the app mode, tab button layout, animations, and more. - -## Global Config - -To override the initial Ionic config for the app, provide a config in `IonicModule.forRoot` in the `app.module.ts` file. - -```tsx -import { IonicModule } from '@ionic/angular'; - -@NgModule({ - ... - imports: [ - BrowserModule, - IonicModule.forRoot({ - rippleEffect: false, - mode: 'md' - }), - AppRoutingModule - ], - ... -}) -``` - -In the above example, we are disabling the Material Design ripple effect across the app, as well as forcing the mode to be Material Design. - -## Per-Component Config - -Ionic Config is not reactive, so it is recommended to use a component's properties when you want to override its default behavior rather than set its config globally. - -```tsx -import { IonicModule } from '@ionic/angular'; - -@NgModule({ - ... - imports: [ - BrowserModule, - IonicModule.forRoot({ - backButtonText: 'Go Back' - }), - AppRoutingModule - ], - ... -}) -``` - -This will set the default text for `ion-back-button` to `Go Back`. However, if you were to change the value of the `backButtonText` config to `Do Not Go Back`, the `ion-back-button` default text would still default to `Go Back` as the component has already been initialized and rendered. Instead, it is recommended to use the `text` property on `ion-back-button`. - -```html - -``` - -In this example we have used our `ion-back-button` in such a way that the text can be dynamically updated if there were to be a change that warranted it, such as a language or locale change. The `getBackButtonText` method would be responsible for returning the correct text. - -## Per-Platform Config - -Ionic Config can also be set on a per-platform basis. For example, this allows you to disable animations if the app is being run in a browser on a potentially slower device. Developers can take advantage of the Platform utilities to accomplish this. - -Since the config is set at runtime, you will not have access to the Platform Dependency Injection. Instead, you can use the underlying functions that the provider uses directly. - -In the following example, we are disabling all animations in our Ionic app only if the app is running in a mobile web browser. -The `isPlatform()` call returns `true` or `false` based upon the platform that is passed in. See the [Platform Documentation](platform.md#platforms) for a list of possible values. - -```tsx -import { isPlatform, IonicModule } from '@ionic/angular'; - -@NgModule({ - ... - imports: [ - BrowserModule, - IonicModule.forRoot({ - animated: !isPlatform('mobileweb') - }), - AppRoutingModule - ], - ... -}) -``` - -The next example allows you to set an entirely different configuration based upon the platform, falling back to a default config if no platforms match: - -```tsx -import { isPlatform, IonicModule } from '@ionic/angular'; - -const getConfig = () => { - if (isPlatform('hybrid')) { - return { - backButtonText: 'Previous', - tabButtonLayout: 'label-hide' - } - } - - return { - menuIcon: 'ellipsis-vertical' - } -} -@NgModule({ - ... - imports: [ - BrowserModule, - IonicModule.forRoot(getConfig()), - AppRoutingModule - ], - ... -}) -``` - -Finally, this example allows you to accumulate a config object based upon different platform requirements: - -```tsx -import { isPlatform, IonicModule } from '@ionic/angular'; - -const getConfig = () => { - let config = { - animated: false - }; - - if (isPlatform('iphone')) { - config = { - ...config, - backButtonText: 'Previous' - } - } - - return config; -} -@NgModule({ - ... - imports: [ - BrowserModule, - IonicModule.forRoot(getConfig()), - AppRoutingModule - ], - ... -}) -``` - -## Config Options - -Below is a list of config options that Ionic uses. - -| Config | Type | Description | -| ------------------------ | ------------------ | -------------------------------------------------------------------------------------------------------- | -| `actionSheetEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-action-sheet`, overriding the default "animation". | -| `actionSheetLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-action-sheet`, overriding the default "animation". | -| `alertEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-alert`, overriding the default "animation". | -| `alertLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-alert`, overriding the default "animation". | -| `animated` | `boolean` | If `true`, Ionic will enable all animations and transitions across the app. | -| `backButtonIcon` | `string` | Overrides the default icon in all `` components. | -| `backButtonText` | `string` | Overrides the default text in all `` components. | -| `hardwareBackButton` | `boolean` | If `true`, Ionic will respond to the hardware back button in an Android device. | -| `infiniteLoadingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | -| `loadingEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-loading`, overriding the default "animation". | -| `loadingLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-loading`, overriding the default "animation". | -| `loadingSpinner` | `SpinnerTypes` | Overrides the default spinner for all `ion-loading` overlays. | -| `menuIcon` | `string` | Overrides the default icon in all `` components. | -| `menuType` | `string` | Overrides the default menu type for all `` components. | -| `modalEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-modal`, overriding the default "animation". | -| `modalLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-modal`, overriding the default "animation". | -| `mode` | `Mode` | The mode determines which platform styles to use for the whole application. | -| `navAnimation` | `AnimationBuilder` | Overrides the default "animation" of all `ion-nav` and `ion-router-outlet` across the whole application. | -| `pickerEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-picker`, overriding the default "animation". | -| `pickerLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-picker`, overriding the default "animation". | -| `popoverEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-popover`, overriding the default "animation". | -| `popoverLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-popover`, overriding the default "animation". | -| `refreshingIcon` | `string` | Overrides the default icon in all `` components. | -| `refreshingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | -| `sanitizerEnabled` | `boolean` | If `true`, Ionic will enable a basic DOM sanitizer on component properties that accept custom HTML. | -| `spinner` | `SpinnerTypes` | Overrides the default spinner in all `` components. | -| `statusTap` | `boolean` | If `true`, clicking or tapping the status bar will cause the content to scroll to the top. | -| `swipeBackEnabled` | `boolean` | If `true`, Ionic will enable the "swipe-to-go-back" gesture across the application. | -| `tabButtonLayout` | `TabButtonLayout` | Overrides the default "layout" of all `ion-bar-button` across the whole application. | -| `toastEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-toast`, overriding the default "animation". | -| `toastLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-toast`, overriding the default "animation". | diff --git a/versioned_docs/version-v5/angular/lifecycle.md b/versioned_docs/version-v5/angular/lifecycle.md deleted file mode 100644 index fc948673727..00000000000 --- a/versioned_docs/version-v5/angular/lifecycle.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -sidebar_label: Lifecycle ---- - -# Ionic Page Life Cycle - -This guide covers how the page life cycle works in an app built with Ionic and Angular. - -![Ionic life cycle events demo](/img/guides/lifecycle/ioniclifecycle.png) - -## Angular Life Cycle Events - -Ionic embraces the life cycle events provided by Angular. The two Angular events you will find using the most are: - -| Event Name | Description | -| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ngOnInit` | Fired once during component initialization. This event can be used to initialize local members and make calls into services that only need to be done once. | -| `ngOnDestroy` | Fired right before Angular destroys the view. Useful for cleanup like unsubscribing from observables. | - -For more info on the Angular Component Life Cycle events, visit their [component lifecycle docs](https://angular.io/guide/lifecycle-hooks). - -:::note -Components that use `ion-nav` or `ion-router-outlet` should not use the `OnPush` change detection strategy. Doing so will prevent lifecycle hooks such as `ngOnInit` from firing. Additionally, asynchronous state changes may not render properly. -::: - -## Ionic Page Events - -In addition to the Angular life cycle events, Ionic Angular provides a few additional events that you can use: - -| Event Name | Description | -| ------------------ | ------------------------------------------------------------------ | -| `ionViewWillEnter` | Fired when the component routing to is about to animate into view. | -| `ionViewDidEnter` | Fired when the component routing to has finished animating. | -| `ionViewWillLeave` | Fired when the component routing from is about to animate. | -| `ionViewDidLeave` | Fired when the component routing to has finished animating. | - -The difference between `ionViewWillEnter` and `ionViewDidEnter` is when they fire. The former fires right after `ngOnInit` but before the page transition begins, and the latter directly after the transition ends. - -For `ionViewWillLeave` and `ionViewDidLeave`, `ionViewWillLeave` gets called directly before the transition away from the current page begins, and `ionViewDidLeave` does not get called until after the new page gets successfully transitioned into (after the new pages `ionViewDidEnter` fires). - -![Ionic life cycle events demo](/img/guides/lifecycle/ioniclifecycle.gif) - -## How Ionic Handles the Life of a Page - -Ionic has its router outlet, called ``. This outlet extends Angular's `` with some additional functionality to enable better experiences for mobile devices. - -When an app is wrapped in ``, Ionic treats navigation a bit differently. When you navigate to a new page, Ionic will keep the old page in the existing DOM, but hide it from your view and transition the new page. The reason we do this is two-fold: - -1. We can maintain the state of the old page (data on the screen, scroll position, etc..) -2. We can provide a smoother transition back to the page since it is already there and doesn't need to be recreated. - -Pages are only removed from the DOM when they are "popped", for instance, by pressing the back button in the UI or the browsers back button. - -Because of this special handling, the `ngOnInit` and `ngOnDestroy` methods might not fire when you would usually think they should. - -`ngOnInit` will only fire each time the page is freshly created, but not when navigated back to the page. For instance, navigating between each page in a tabs interface will only call each page's `ngOnInit` method once, but not on subsequent visits. `ngOnDestroy` will only fire when a page "popped". - -## Route Guards - -In Ionic 3, there were a couple of additional life cycle methods that were useful to control when a page could be entered (`ionViewCanEnter`) and left (`ionViewCanLeave`). These could be used to protect pages from unauthorized users and to keep a user on a page when you don't want them to leave (like during a form fill). - -These methods were removed in Ionic 4 in favor of using Angular's Route Guards. - -A route guard helps determine if a particular action can be taken against a route. They are classes that implement a certain interface. The `CanActivate` and `CanDeactivate` interfaces can be used to implement the same type of logic that the removed events `ionViewCanEnter` and `ionViewCanLeave` did. - -```tsx -@Injectable() -export class AuthGuard implements CanActivate { - constructor(private authService: AuthService) {} - - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - return this.authService.isAuthenticated(); - } -} -``` - -To use this guard, add it to the appropriate param in the route definition: - -```tsx -{ path: 'settings', canActivate: [AuthGuard], loadChildren: '...', } -``` - -For more info on how to use route guards, go to Angular's [router documentation](https://angular.io/guide/router). - -## Guidance for Each Life Cycle Method - -Below are some tips on use cases for each of the life cycle events. - -- `ngOnInit` - Initialize your component and load data from services that don't need refreshing on each subsequent visit. -- `ionViewWillEnter` - Since `ionViewWillEnter` is called every time the view is navigated to (regardless if initialized or not), it's a good method to load data from services. However, if your data comes back during the animation, it can start lots of DOM manipulation, which can cause some janky animations. -- `ionViewDidEnter` - If you see performance problems from using `ionViewWillEnter` when loading data, you can do your data calls in `ionViewDidEnter` instead. This event won't fire until after the page is visible by the user, however, so you might want to use either a loading indicator or a skeleton screen, so content doesn't flash in un-naturally after the transition is complete. -- `ionViewWillLeave` - Can be used for cleanup, like unsubscribing from observables. Since `ngOnDestroy` might not fire when you navigate from the current page, put your cleanup code here if you don't want it active while the screen is not in view. -- `ionViewDidLeave` - When this event fires, you know the new page has fully transitioned in, so any logic you might not normally do when the view is visible can go here. -- `ngOnDestroy` - Cleanup logic for your pages that you don't want to clean up in `ionViewWillLeave`. diff --git a/versioned_docs/version-v5/angular/navigation.md b/versioned_docs/version-v5/angular/navigation.md deleted file mode 100644 index 0e5148442b8..00000000000 --- a/versioned_docs/version-v5/angular/navigation.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -sidebar_label: Navigation/Routing ---- - -# Angular Navigation - -This guide covers how routing works in an app built with Ionic and Angular. - -The Angular Router is one of the most important libraries in an Angular application. Without it, apps would be single view/single context apps or would not be able to maintain their navigation state on browser reloads. With Angular Router, we can create rich apps that are linkable and have rich animations (when paired with Ionic of course). Let's look at the basics of the Angular Router and how we can configure it for Ionic apps. - -## A simple Route - -For most apps, having some sort of route is often required. The most basic configuration looks a bit like this: - -```tsx - -import { RouterModule } from '@angular/router'; - -@NgModule({ - imports: [ - ... - RouterModule.forRoot([ - { path: '', component: LoginComponent }, - { path: 'detail', component: DetailComponent }, - ]) - ], -}) -``` - -The simplest breakdown for what we have here is a path/component lookup. When our app loads, the router kicks things off by reading the URL the user is trying to load. In our sample, our route looks for `''`, which is essentially our index route. So for this, we load the `LoginComponent`. Fairly straight forward. This pattern of matching paths with a component continues for every entry we have in the router config. But what if we wanted to load a different path on our initial load? - -## Handling Redirects - -For this we can use router redirects. Redirects work the same way that a typical route object does, but just includes a few different keys. - -```tsx -[ - { path: '', redirectTo: 'login', pathMatch: 'full' }, - { path: 'login', component: LoginComponent }, - { path: 'detail', component: DetailComponent }, -]; -``` - -In our redirect, we look for the index path of our app. Then if we load that, we redirect to the `login` route. The last key of `pathMatch` is required to tell the router how it should look up the path. - -Since we use `full`, we're telling the router that we should compare the full path, even if ends up being something like `/route1/route2/route3`. Meaning that if we have: - -```tsx -{ path: '/route1/route2/route3', redirectTo: 'login', pathMatch: 'full' }, -{ path: 'login', component: LoginComponent }, -``` - -And load `/route1/route2/route3` we'll redirect. But if we loaded `/route1/route2/route4`, we won't redirect, as the paths don't match fully. - -Alternatively, if we used: - -```tsx -{ path: '/route1/route2', redirectTo: 'login', pathMatch: 'prefix' }, -{ path: 'login', component: LoginComponent }, -``` - -Then load both `/route1/route2/route3` and `/route1/route2/route4`, we'll be redirected for both routes. This is because `pathMatch: 'prefix'` will match only part of the path. - -## Navigating to different routes - -Talking about routes is good and all, but how does one actually navigate to said routes? For this, we can use the `routerLink` directive. Let's go back and take our simple router setup from earlier: - -```ts -RouterModule.forRoot([ - { path: '', component: LoginComponent }, - { path: 'detail', component: DetailComponent }, -]); -``` - -Now from the `LoginComponent`, we can use the following HTML to navigate to the detail route. - -```html - - - Login - - - - - Go to detail - -``` - -The important part here is the `ion-button` and `routerLink` directive. RouterLink works on a similar idea as typical `href`s, but instead of building out the URL as a string, it can be built as an array, which can provide more complicated paths. - -We also can programmatically navigate in our app by using the router API. - -```tsx -import { Component } from '@angular/core'; -import { Router } from '@angular/router'; - -@Component({ - ... -}) -export class LoginComponent { - - constructor(private router: Router){} - - navigate(){ - this.router.navigate(['/detail']) - } -} -``` - -Both options provide the same navigation mechanism, just fitting different use cases. - -:::note -A note on navigation with relative URLs: Currently, to support multiple navigation stacks, relative URLs are something not supported -::: - -## Lazy loading routes - -Now the current way our routes are setup makes it so they are included in the same chunk as the root app.module, which is not ideal. Instead, the router has a setup that allows the components to be isolated to their own chunks. - -```tsx - -import { RouterModule } from '@angular/router'; - -@NgModule({ - imports: [ - ... - RouterModule.forRoot([ - { path: '', redirectTo: 'login', pathMatch: 'full' }, - { path: 'login', loadChildren: () => import('./login/login.module').then(m => m.LoginModule) }, - { path: 'detail', loadChildren: () => import('./detail/detail.module').then(m => m.DetailModule) } - ]) - ], -}) -``` - -While similar, the `loadChildren` property is a way to reference a module by using native import instead of a component directly. In order to do this though, we need to create a module for each of the components. - -```tsx -... -import { RouterModule } from '@angular/router'; -import { LoginComponent } from './login.component'; - -@NgModule({ - imports: [ - ... - RouterModule.forChild([ - { path: '', component: LoginComponent }, - ]) - ], -}) -``` - -:::note -We're excluding some additional content and only including the necessary parts. -::: - -Here, we have a typical Angular Module setup, along with a RouterModule import, but we're now using `forChild` and declaring the component in that setup. With this setup, when we run our build, we will produce separate chunks for both the app component, the login component, and the detail component. - -## Live Example - -If you would prefer to get hands on with the concepts and code described above, please checkout our [live example](https://stackblitz.com/edit/ionic-angular-routing?file=src/app/app-routing.module.ts) of the topics above on StackBlitz. - -## Working with Tabs - -With Tabs, the Angular Router provides Ionic the mechanism to know what components should be loaded, but the heavy lifting is actually done by the tabs component. Let's look at a simple example. - -```ts -const routes: Routes = [ - { - path: 'tabs', - component: TabsPage, - children: [ - { - path: 'tab1', - children: [ - { - path: '', - loadChildren: () => import('../tab1/tab1.module').then((m) => m.Tab1PageModule), - }, - ], - }, - { - path: '', - redirectTo: '/tabs/tab1', - pathMatch: 'full', - }, - ], - }, - { - path: '', - redirectTo: '/tabs/tab1', - pathMatch: 'full', - }, -]; -``` - -Here we have a "tabs" path that we load. In this example we call the path "tabs", but the name of the paths can be changed. They can be called whatever fits your app. In that route object, we can define a child route as well. In this example, the top level child route "tab1" acts as our "outlet", and can load additional child routes. For this example, we have a single sub-child-route, which just loads a new component. The markup for the tab is as followed: - -```html - - - - - Tab One - - - -``` - -If you've built apps with Ionic before, this should feel familiar. We create a `ion-tabs` component, and provide a `ion-tab-bar`. The `ion-tab-bar` provides a `ion-tab-button` with a `tab` property that is associated with the tab "outlet" in the router config. Note that the latest version of `@ionic/angular` no longer requires ``, but instead allows developers to fully customize the tab bar, and the single source of truth lives within the router configuration. diff --git a/versioned_docs/version-v5/angular/overview.md b/versioned_docs/version-v5/angular/overview.md deleted file mode 100644 index 42acc51dd04..00000000000 --- a/versioned_docs/version-v5/angular/overview.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: 'Ionic Angular Overview | Angular Version Support and Tooling' -description: '@ionic/angular combines the Ionic experience with the tooling and APIs tailored to Angular Developers. Learn more about version support in our Angular Overview.' -sidebar_label: Overview ---- - -# Ionic Angular Overview - -`@ionic/angular` combines the core Ionic experience with the tooling and APIs that are tailored to Angular Developers. - -## Angular Version Support - -Ionic supports `Angular 6.0.0 and up`. As part of their upgrade strategy, Angular has built-in tooling to help automate upgrades and provide feedback to developers whenever changes to an API occurred. This reduces update friction and keeps the ecosystem in a evergreen state. - -## Angular Tooling - -With Ionic 4+, the official Angular stack for building an app and routing are used, so your app can fall in-line with the rest of the great Angular ecosystem. In cases where more opinionated features are needed, Ionic provides `@ionic/angular-toolkit`, which builds and integrates with the [official Angular CLI](https://angular.io/cli) and provides features that are specific to `@ionic/angular` apps. diff --git a/versioned_docs/version-v5/angular/performance.md b/versioned_docs/version-v5/angular/performance.md deleted file mode 100644 index ba542f38f46..00000000000 --- a/versioned_docs/version-v5/angular/performance.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -sidebar_label: Performance ---- - -# Angular Performance - -## \*ngFor with Ionic Components - -When using `*ngFor` with Ionic components, we recommend using Angular's `trackBy` option. This allows Angular to manage change propagation in a much more efficient way and only update the content inside of the component rather than re-create the component altogether. - -By using `trackBy` you can provide a stable identity for each loop element so Angular can track insertions and deletions within the iterator. Below is an example of how to use `trackBy`: - -**home.page.html** - -```html - - {{ item.value }} - -``` - -**home.component.ts** - -```tsx - -items = [ - { id: 0, value: 'Item 0' }, - { id: 1, value: 'Item 1' }, - ... -] - -trackItems(index: number, itemObject: any) { - return itemObject.id; -} -``` - -In this example, we have an array of objects called `items`. Each object contains a `value` and an `id`. Using `trackBy`, we pass a `trackItems` function which returns the `id` of each object. This `id` is used to provide a stable identity for each loop element. - -For more information on how Angular manages change propagation with `ngFor` see https://angular.io/api/common/NgForOf#change-propagation. - -## From the Ionic Team - -[How to Lazy Load in Ionic Angular](https://ionicframework.com/blog/how-to-lazy-load-in-ionic-angular/) - -[Improved Perceived Performance with Skeleton Screens](https://ionicframework.com/blog/improved-perceived-performance-with-skeleton-screens/) - -## From the Angular Team - -[Build performant and progressive Angular apps](https://web.dev/angular) - web.dev - -## From the Community - -[High Performance Animations in Ionic](https://www.joshmorony.com/high-performance-animations-in-ionic/) - Josh Morony - -[High Performance List Filtering in Ionic](https://www.joshmorony.com/high-performance-list-filtering-in-ionic-2/) - Josh Morony - -[Increasing Performance with Efficient DOM Writes in Ionic](https://www.joshmorony.com/increasing-performance-with-efficient-dom-writes-in-ionic-2/) - Josh Morony - -[Ionic Framework is Fast (But Your Code Might Not Be)](https://www.joshmorony.com/ionic-framework-is-fast-but-your-code-might-not-be/) - Josh Morony - -:::note -Do you have a guide you'd like to share? Click the _Edit this page_ button below. -::: diff --git a/versioned_docs/version-v5/angular/platform.md b/versioned_docs/version-v5/angular/platform.md deleted file mode 100644 index 7020ca4e603..00000000000 --- a/versioned_docs/version-v5/angular/platform.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: Platform | Ionic Platform to Customize Apps to Fit Any Device -description: Ionic Platform service can be used to get information about your current device. With this information you can completely customize your app to fit any device. -sidebar_label: Platform ---- - -# Platform - -The Platform service can be used to get information about your current device. You can get all of the platforms associated with the device using the `platforms` method, including whether the app is being viewed from a tablet, if it's on a mobile device or browser, and the exact platform (iOS, Android, etc). You can also get the orientation of the device, if it uses right-to-left language direction, and much much more. With this information you can completely customize your app to fit any device. - -## Usage - -```tsx -import { Platform } from '@ionic/angular'; - -@Component({...}) -export class MyPage { - constructor(public platform: Platform) { - - } -} -``` - -## Methods - -### `is(platformName: Platforms) => boolean` - -Depending on the platform the user is on, `is(platformName)` will return true or false. Note that the same app can return true for more than one platform name. For example, an app running from an iPad would return true for the platform names: `mobile`, `ios`, `ipad`, and `tablet`. Additionally, if the app was running from Cordova then `cordova` would be true. - -#### Parameters - -| Name | Type | Description | -| -------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| `platformName` | `Platforms` | Name of the platform. Available options are android, capacitor, cordova, desktop, electron, hybrid, ios, ipad, iphone, mobile, phablet, pwa, tablet | - -#### Platforms - -Below is a table listing all the possible platform values along with corresponding descriptions. - -| Platform Name | Description | -| ------------- | ---------------------------------------- | -| android | a device running Android | -| capacitor | a device running Capacitor | -| cordova | a device running Cordova | -| desktop | a desktop device | -| electron | a desktop device running Electron | -| hybrid | a device running Capacitor or Cordova | -| ios | a device running iOS | -| ipad | an iPad device | -| iphone | an iPhone device | -| mobile | a mobile device | -| mobileweb | a web browser running in a mobile device | -| phablet | a phablet device | -| pwa | a PWA app | -| tablet | a tablet device | - -### `platforms() => string[]` - -Depending on what device you are on, `platforms` can return multiple values. Each possible value is a hierarchy of platforms. For example, on an iPhone, it would return `mobile`, `ios`, and `iphone`. - -### `ready() => Promise` - -Returns a promise when the platform is ready and native functionality can be called. If the app is running from within a web browser, then the promise will resolve when the DOM is ready. When the app is running from an application engine such as Cordova, then the promise will resolve when Cordova triggers the `deviceready` event. The resolved value is the `readySource`, which states the platform that was used. - -For example, when Cordova is ready, the resolved ready source is `cordova`. The default ready source value will be `dom`. The `readySource` is useful if different logic should run depending on the platform the app is running from. For example, only Capacitor and Cordova can execute the status bar plugin, so the web should not run status bar plugin logic. - -### `isRTL() => boolean` - -Returns if this app is using right-to-left language direction or not. We recommend the app's `index.html` file already has the correct `dir` attribute value set, such as `` or ``. [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir) - -### `isLandscape() => boolean` - -Returns `true` if the app is in landscape mode. - -### `isPortrait() => boolean` - -Returns `true` if the app is in portrait mode. - -### `width() => number` - -Gets the width of the platform's viewport using `window.innerWidth`. - -### `height() => number` - -Gets the height of the platform's viewport using `window.innerHeight`. - -### `url() => string` - -Get the current url. - -### `testUserAgent(expression: string) => boolean` - -Returns `true` if the expression is included in the user agent string. - -### Parameters - -| Name | Type | Description | -| ---------- | ------ | ------------------------------------- | -| expression | string | The string to check in the user agent | - -## Events - -### `pause` - -The `pause` event emits when the native platform puts the application into the background, typically when the user switches to a different application. This event emits when a Cordova/Capacitor app is put into the background but doesn't fire in a standard web browser. - -#### Usage - -```tsx -this.platform.pause.subscribe(async () => { - alert('Pause event detected'); -}); -``` - -### `resize` - -The `resize` event emits when the browser window has changed dimensions. This could be from a browser window being physically resized, or from a device changing orientation. - -#### Usage - -```tsx -this.platform.resize.subscribe(async () => { - alert('Resize event detected'); -}); -``` - -### `resume` - -The `resume` event fires when the native platform pulls the application out from the background. This event emits when a Cordova/Capacitor app comes out from the background but doesn't fire in a standard web browser. - -#### Usage - -```tsx -this.platform.resume.subscribe(async () => { - alert('Resume event detected'); -}); -``` diff --git a/versioned_docs/version-v5/angular/pwa.md b/versioned_docs/version-v5/angular/pwa.md deleted file mode 100644 index 45947df209b..00000000000 --- a/versioned_docs/version-v5/angular/pwa.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -sidebar_label: Progressive Web Apps ---- - -# Progressive Web Apps in Angular - -## Making your Angular app a PWA - -The two main requirements of a PWA are a Service Worker and a Web Manifest. While it's possible to add both of these to an app manually, the Angular team has an `@angular/pwa` package that can be used to automate this. - -The `@angular/pwa` package will automatically add a service worker and an app manifest to the app. -To add this package to the app, run: - -```shell -ng add @angular/pwa -``` - -Once this package has been added run `ionic build --prod` and the `www` directory will be ready to deploy as a PWA. - -:::note -By default, the `@angular/pwa` package comes with the Angular logo for the app icons. Be sure to update the manifest to use the correct app name and also replace the icons. -::: - -:::note -Features like Service Workers and many JavaScript APIs (such as geolocation) require the app be hosted in a secure context. When deploying an app through a hosting service, be aware that HTTPS will be required to take full advantage of Service Workers. -::: - -## Service Worker configuration - -After `@angular/pwa` has been added, a new `ngsw-config.json` file will be created at the root of the project. This file is responsible for configuring how Angular's service worker mechanism will handle caching assets. By default, the following will be provided: - -```json -{ - "$schema": "./node_modules/@angular/service-worker/config/schema.json", - "index": "/index.html", - "assetGroups": [ - { - "name": "app", - "installMode": "prefetch", - "resources": { - "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"] - } - }, - { - "name": "assets", - "installMode": "lazy", - "updateMode": "prefetch", - "resources": { - "files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] - } - } - ] -} -``` - -There are two sections in here, one for app specific resources (JS, CSS, HTML) and assets the app will load on demand. Depending on your app, these options can be customized. For a more detailed guide, read [the official guide from the Angular Team.](https://angular.io/guide/service-worker-config) - -## Deploying - -### Firebase - -Firebase hosting provides many benefits for Progressive Web Apps, including fast response times thanks to CDNs, HTTPS enabled by default, and support for [HTTP2 push](https://firebase.googleblog.com/2016/09/http2-comes-to-firebase-hosting.html). - -First, if not already available, [create the project](https://console.firebase.google.com) in Firebase. - -Next, in a Terminal, install the Firebase CLI: - -```shell -npm install -g firebase-tools -``` - -:::note -If it's the first time you use firebase-tools, login to your Google account with `firebase login` command. -::: - -With the Firebase CLI installed, run `firebase init` within your Ionic project. The CLI prompts: - -**"Which Firebase CLI features do you want to set up for this folder?"** Choose "Hosting: Configure and deploy Firebase Hosting sites." - -**"Select a default Firebase project for this directory:"** Choose the project you created on the Firebase website. - -**"What do you want to use as your public directory?"** Enter "www". - -:::note -Answering these next two questions will ensure that routing, hard reload, and deep linking work in the app: -::: - -**Configure as a single-page app (rewrite all urls to /index.html)?"** Enter "Yes". - -**"File www/index.html already exists. Overwrite?"** Enter "No". - -A `firebase.json` config file is generated, configuring the app for deployment. - -The last thing needed is to make sure caching headers are being set correctly. To do this, add a `headers` snippet to the `firebase.json` file. The complete `firebase.json` looks like: - -```json -{ - "hosting": { - "public": "www", - "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], - "rewrites": [ - { - "source": "**", - "destination": "/index.html" - } - ], - "headers": [ - { - "source": "/build/app/**", - "headers": [ - { - "key": "Cache-Control", - "value": "public, max-age=31536000" - } - ] - }, - { - "source": "ngsw-worker.js", - "headers": [ - { - "key": "Cache-Control", - "value": "no-cache" - } - ] - } - ] - } -} -``` - -For more information about the `firebase.json` properties, see the [Firebase documentation](https://firebase.google.com/docs/hosting/full-config#section-firebase-json). - -Next, build an optimized version of the app by running: - -```shell -ionic build --prod -``` - -Last, deploy the app by running: - -```shell -firebase deploy -``` - -After this completes, the app will be live. diff --git a/versioned_docs/version-v5/angular/storage.md b/versioned_docs/version-v5/angular/storage.md deleted file mode 100644 index ea044c7ccc6..00000000000 --- a/versioned_docs/version-v5/angular/storage.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_label: Storage ---- - -# Data Storage - -There are variety of options available for storing data within an Ionic app. - -Here are two official Ionic options: - -## Ionic Secure Storage - -For teams building mission-critical apps or requiring encryption support, [Ionic Secure Storage](https://ionic.io/docs/secure-storage) is an official premium solution from the Ionic team that provides a cross-platform data storage system that works on iOS and Android. - -It makes it easy to build high performance, offline-ready Ionic apps across iOS, Android, and the web. - -[Learn more](https://ionic.io/products/secure-storage) - -## @ionic/storage - -For developers not requiring encryption nor relational data support, [@ionic/storage](https://github.com/ionic-team/ionic-storage) is an open source key/value API for building apps that work across storage engines on multiple platforms. - -Additionally, Ionic Secure Storage has a driver that works with the key/value API in `@ionic/storage` while providing encryption and SQLite support. - -Learn more about [@ionic/storage](https://github.com/ionic-team/ionic-storage) diff --git a/versioned_docs/version-v5/angular/testing.md b/versioned_docs/version-v5/angular/testing.md deleted file mode 100644 index ce608728a73..00000000000 --- a/versioned_docs/version-v5/angular/testing.md +++ /dev/null @@ -1,585 +0,0 @@ -# Testing - -When an `@ionic/angular` application is generated using the Ionic CLI, it is automatically set up for unit testing and end-to-end testing of the application. This is the same setup that is used by the Angular CLI. Refer to the Angular Testing Guide for detailed information on testing Angular applications. - -## Testing Principles - -When testing an application, it is best to keep in mind that testing can show if defects are present in a system. However, it is impossible to prove that any non-trivial system is completely free of defects. For this reason, the goal of testing is not to verify that the code is correct but to find problems within the code. This is a subtle but important distinction. - -If we set out to prove that the code is correct, we are more likely to stick to the happy path through the code. If we set out to find problems, we are more likely to more fully exercise the code and find the bugs that are lurking there. - -It is also best to begin testing an application from the very start. This allows defects to be found early in the process when they are easier to fix. This also allows code to be refactored with confidence as new features are added to the system. - -## Unit Testing - -Unit tests exercise a single unit of code (component, page, service, pipe, etc) in isolation from the rest of the system. Isolation is achieved through the injection of mock objects in place of the code's dependencies. The mock objects allow the test to have fine-grained control of the outputs of the dependencies. The mocks also allow the test to determine which dependencies have been called and what has been passed to them. - -Well-written unit tests are structured such that the unit of code and the features it contains are described via `describe()` callbacks. The requirements for the unit of code and its features are tested via `it()` callbacks. When the descriptions for the `describe()` and `it()` callbacks are read, they make sense as a phrase. When the descriptions for nested `describe()`s and a final `it()` are concatenated together, they form a sentence that fully describes the test case. - -Since unit tests exercise the code in isolation, they are fast, robust, and allow for a high degree of code coverage. - -### Using Mocks - -Unit tests exercise a code module in isolation. To facilitate this, we recommend using Jasmine (https://jasmine.github.io/). Jasmine creates mock objects (which Jasmine calls "spies") to take the place of dependencies while testing. When a mock object is used, the test can control the values returned by calls to that dependency, making the current test independent of changes made to the dependency. This also makes the test setup easier, allowing the test to only be concerned with the code within the module under test. - -Using mocks also allows the test to query the mock to determine if it was called and how it was called via the `toHaveBeenCalled*` set of functions. Tests should be as specific as possible with these functions, favoring calls to `toHaveBeenCalledTimes` over calls to `toHaveBeenCalled` when testing that a method has been called. That is `expect(mock.foo).toHaveBeenCalledTimes(1)` is better than `expect(mock.foo).toHaveBeenCalled()`. The opposite advice should be followed when testing that something has not been called (`expect(mock.foo).not.toHaveBeenCalled()`). - -There are two common ways to create mock objects in Jasmine. Mock objects can be constructed from scratch using `jasmine.createSpy` and `jasmine.createSpyObj` or spies can be installed onto existing objects using `spyOn()` and `spyOnProperty()`. - -#### Using `jasmine.createSpy` and `jasmine.createSpyObj` - -`jasmine.createSpyObj` creates a full mock object from scratch with a set of mock methods defined on creation. This is useful in that it is very simple. Nothing needs to be constructed or injected into the test. The disadvantage of using this function is that it allows the creation of objects that may not match the real objects. - -`jasmine.createSpy` is similar but it creates a stand-alone mock function. - -#### Using `spyOn()` and `spyOnProperty()` - -`spyOn()` installs the spy on an existing object. The advantage of using this technique is that if an attempt is made to spy on a method that does not exist on the object, an exception is raised. This prevents the test from mocking methods that do not exist. The disadvantage is that the test needs a fully formed object to begin with, which may increase the amount of test setup required. - -`spyOnProperty()` is similar with the difference being that it spies on a property and not a method. - -### General Testing Structure - -Unit tests are contained in `spec` files with one `spec` file per entity (component, page, service, pipe, etc.). The `spec` files live side-by-side with and are named after the source that they are testing. For example, if the project has a service called WeatherService, the code for it is in a file named `weather.service.ts` with the tests in a file named `weather.service.spec.ts`. Both of those files are in the same folder. - -The `spec` files themselves contain a single `describe` call that defines that overall test. Nested within it are other `describe` calls that define major areas of functionality. Each `describe` call can contain setup and teardown code (generally handled via `beforeEach` and `afterEach` calls), more `describe` calls forming a hierarchical breakdown of functionality, and `it` calls which define individual test cases. - -The `describe` and `it` calls also contain a descriptive text label. In well-formed tests, the `describe` and `it` calls combine with their labels to perform proper phrases and the full label for each test case, formed by combining the `describe` and `it` labels, creates a full sentence. - -For example: - -```tsx -describe('Calculation', () => { - describe('divide', () => { - it('calculates 4 / 2 properly' () => {}); - it('cowardly refuses to divide by zero' () => {}); - ... - }); - - describe('multiply', () => { - ... - }); -}); -``` - -The outer `describe` call states that the `Calculation` service is being tested, the inner `describe` calls state exactly what functionality is being tested, and the `it` calls state what the test cases are. When run the full label for each test case is a sentence that makes sense (Calculation divide cowardly refuses to divide by zero). - -### Pages and Components - -Pages are just Angular components. Thus, pages and components are both tested using Angular's Component Testing guidelines. - -Since pages and components contain both TypeScript code and HTML template markup it is possible to perform both component class testing and component DOM testing. When a page is created, the template test that is generated looks like this: - -```tsx -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TabsPage } from './tabs.page'; - -describe('TabsPage', () => { - let component: TabsPage; - let fixture: ComponentFixture; - - beforeEach(async () => { - TestBed.configureTestingModule({ - declarations: [TabsPage], - schemas: [CUSTOM_ELEMENTS_SCHEMA], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TabsPage); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); -``` - -When doing component class testing, the component object is accessed using the component object defined via `component = fixture.componentInstance;`. This is an instance of the component class. When doing DOM testing, the `fixture.nativeElement` property is used. This is the actual `HTMLElement` for the component, which allows the test to use standard HTML API methods such as `HTMLElement.querySelector` in order to examine the DOM. - -## Services - -Services often fall into one of two broad categories: utility services that perform calculations and other operations, and data services that perform primarily HTTP operations and data manipulation. - -### Basic Service Testing - -The suggested way to test most services is to instantiate the service and manually inject mocks for any dependency the service has. This way, the code can be tested in isolation. - -Let's say that there is a service with a method that takes an array of timecards and calculates net pay. Let's also assume that the tax calculations are handled via another service that the current service depends on. This payroll service could be tested as such: - -```tsx -import { PayrollService } from './payroll.service'; - -describe('PayrollService', () => { - let service: PayrollService; - let taxServiceSpy; - - beforeEach(() => { - taxServiceSpy = jasmine.createSpyObj('TaxService', { - federalIncomeTax: 0, - stateIncomeTax: 0, - socialSecurity: 0, - medicare: 0 - }); - service = new PayrollService(taxServiceSpy); - }); - - describe('net pay calculations', () => { - ... - }); -}); -``` - -This allows the test to control the values returned by the various tax calculations via mock setup such as `taxServiceSpy.federalIncomeTax.and.returnValue(73.24)`. This allows the "net pay" tests to be independent of the tax calculation logic. When the tax codes change, only the tax service related code and tests need to change. The tests for the net pay can continue to operate as they are since these tests do not care how the tax is calculated, just that the value is applied properly. - -The scaffolding that is used when a service is generated via `ionic g service name` uses Angular's testing utilities and sets up a testing module. Doing so is not strictly necessary. That code may be left in, however, allowing the service to be built manually or injected as such: - -```tsx -import { TestBed, inject } from '@angular/core/testing'; - -import { PayrollService } from './payroll.service'; -import { TaxService } from './tax.service'; - -describe('PayrolService', () => { - let taxServiceSpy; - - beforeEach(() => { - taxServiceSpy = jasmine.createSpyObj('TaxService', { - federalIncomeTax: 0, - stateIncomeTax: 0, - socialSecurity: 0, - medicare: 0, - }); - TestBed.configureTestingModule({ - providers: [PayrollService, { provide: TaxService, useValue: taxServiceSpy }], - }); - }); - - it('does some test where it is injected', inject([PayrollService], (service: PayrollService) => { - expect(service).toBeTruthy(); - })); - - it('does some test where it is manually built', () => { - const service = new PayrollService(taxServiceSpy); - expect(service).toBeTruthy(); - }); -}); -``` - -#### Testing HTTP Data Services - -Most services that perform HTTP operations will use Angular's HttpClient service in order to perform those operations. For such tests, it is suggested to use Angular's `HttpClientTestingModule`. For detailed documentation of this module, please see Angular's Angular's Testing HTTP requests guide. - -This basic setup for such a test looks like this: - -```tsx -import { HttpBackend, HttpClient } from '@angular/common/http'; -import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; -import { TestBed, inject } from '@angular/core/testing'; - -import { IssTrackingDataService } from './iss-tracking-data.service'; - -describe('IssTrackingDataService', () => { - let httpClient: HttpClient; - let httpTestingController: HttpTestingController; - let issTrackingDataService: IssTrackingDataService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [IssTrackingDataService], - }); - - httpClient = TestBed.get(HttpClient); - httpTestingController = TestBed.get(HttpTestingController); - issTrackingDataService = new IssTrackingDataService(httpClient); - }); - - it('exists', inject([IssTrackingDataService], (service: IssTrackingDataService) => { - expect(service).toBeTruthy(); - })); - - describe('location', () => { - it('gets the location of the ISS now', () => { - issTrackingDataService.location().subscribe((x) => { - expect(x).toEqual({ longitude: -138.1719, latitude: 44.4423 }); - }); - const req = httpTestingController.expectOne('http://api.open-notify.org/iss-now.json'); - expect(req.request.method).toEqual('GET'); - req.flush({ - iss_position: { longitude: '-138.1719', latitude: '44.4423' }, - timestamp: 1525950644, - message: 'success', - }); - httpTestingController.verify(); - }); - }); -}); -``` - -### Pipes - -A pipe is like a service with a specifically defined interface. It is a class that contains one public method, `transform`, which manipulates the input value (and other optional arguments) in order to create the output that is rendered on the page. To test a pipe: instantiate the pipe, call the transform method, and verify the results. - -As a simple example, let's look at a pipe that takes a `Person` object and formats the name. For the sake of simplicity, let's say a `Person` consists of an `id`, `firstName`, `lastName`, and `middleInitial`. The requirements for the pipe are to print the name as "Last, First M." handling situations where a first name, last name, or middle initial do not exist. Such a test might look like this: - -```tsx -import { NamePipe } from './name.pipe'; - -import { Person } from '../../models/person'; - -describe('NamePipe', () => { - let pipe: NamePipe; - let testPerson: Person; - - beforeEach(() => { - pipe = new NamePipe(); - testPerson = { - id: 42, - firstName: 'Douglas', - lastName: 'Adams', - middleInitial: 'N', - }; - }); - - it('exists', () => { - expect(pipe).toBeTruthy(); - }); - - it('formats a full name properly', () => { - expect(pipe.transform(testPerson)).toBeEqual('Adams, Douglas N.'); - }); - - it('handles having no middle initial', () => { - delete testPerson.middleInitial; - expect(pipe.transform(testPerson)).toBeEqual('Adams, Douglas'); - }); - - it('handles having no first name', () => { - delete testPerson.firstName; - expect(pipe.transform(testPerson)).toBeEqual('Adams N.'); - }); - - it('handles having no last name', () => { - delete testPerson.lastName; - expect(pipe.transform(testPerson)).toBeEqual('Douglas N.'); - }); -}); -``` - -It is also beneficial to exercise the pipe via DOM testing in the components and pages that utilize the pipe. - -## End-to-end Testing - -End-to-end testing is used to verify that an application works as a whole and often includes a connection to live data. Whereas unit tests focus on code units in isolation and thus allow for low-level testing of the application logic, end-to-end tests focus on various user stories or usage scenarios, providing high-level testing of the overall flow of data through the application. Whereas unit tests try to uncover problems with an application's logic, end-to-end tests try to uncover problems that occur when those individual units are used together. End-to-end tests uncover problems with the overall architecture of the application. - -Since end-to-end tests exercise user stories and cover the application as a whole rather than individual code modules, end-to-end tests exist in their own application in the project apart from the code for the main application itself. Most end-to-end tests operate by automating common user interactions with the application and examining the DOM to determine the results of those interactions. - -### Test Structure - -When an `@ionic/angular` application is generated, a default end-to-end test application is generated in the `e2e` folder. This application uses Protractor to control the browser and Jasmine to structure and execute the tests. The application initially consists of four files: - -- `protractor.conf.js` - the Protractor configuration file -- `tsconfig.e2e.json` - specific TypeScript configuration for the testing application -- `src/app.po.ts` - a page object containing methods that navigate the application, query elements in the DOM, and maninpulate elements on the page -- `src/app.e2e-spec.ts` - a testing script - -#### Page Objects - -End-to-end tests operate by automating common user interactions with the application, waiting for the application to respond, and examining the DOM to determine the results of the interaction. This involves a lot of DOM manipulation and examination. If this were all done manually, the tests would be very brittle and difficult to read and maintain. - -Page objects encapsulate the HTML for a single page in a TypeScript class, providing an API that the test scripts use to interact with the application. The encapsulation of the DOM manipulation logic in page objects makes the tests more readable and far easier to reason about, lowering the maintenance costs of the test. Creating well-crafted page objects is the key to creating high quality and maintainable end-to-end tests. - -##### Base Page Object - -A lot of tests rely on actions such as waiting for a page to be visible, entering text into an input, and clicking a button. The methods used to do this remain consistent with only the CSS selectors used to get the appropriate DOM element changing. Therefore it makes sense to abstract this logic into a base class that can be used by the other page objects. - -Here is an example that implements a few basic methods that all page objects will need to support. - -```tsx -import { browser, by, element, ExpectedConditions } from 'protractor'; - -export class PageObjectBase { - private path: string; - protected tag: string; - - constructor(tag: string, path: string) { - this.tag = tag; - this.path = path; - } - - load() { - return browser.get(this.path); - } - - rootElement() { - return element(by.css(this.tag)); - } - - waitUntilInvisible() { - browser.wait(ExpectedConditions.invisibilityOf(this.rootElement()), 3000); - } - - waitUntilPresent() { - browser.wait(ExpectedConditions.presenceOf(this.rootElement()), 3000); - } - - waitUntilNotPresent() { - browser.wait(ExpectedConditions.not(ExpectedConditions.presenceOf(this.rootElement())), 3000); - } - - waitUntilVisible() { - browser.wait(ExpectedConditions.visibilityOf(this.rootElement()), 3000); - } - - getTitle() { - return element(by.css(`${this.tag} ion-title`)).getText(); - } - - protected enterInputText(sel: string, text: string) { - const el = element(by.css(`${this.tag} ${sel}`)); - const inp = el.element(by.css('input')); - inp.sendKeys(text); - } - - protected enterTextareaText(sel: string, text: string) { - const el = element(by.css(`${this.tag} ${sel}`)); - const inp = el.element(by.css('textarea')); - inp.sendKeys(text); - } - - protected clickButton(sel: string) { - const el = element(by.css(`${this.tag} ${sel}`)); - browser.wait(ExpectedConditions.elementToBeClickable(el)); - el.click(); - } -} -``` - -##### Per-Page Abstractions - -Each page in the application will have its own page object class that abstracts the elements on that page. If a base page object class is used, creating the page object involves mostly creating custom methods for elements that are specific to that page. Often, these custom elements take advantage of methods in the base class in order to perform the work that is required. - -Here is an example page object for a simple but typical login page. Notice that many of the methods, such as `enterEMail()`, call methods in the base class that perform the bulk of the work. - -```tsx -import { browser, by, element, ExpectedConditions } from 'protractor'; -import { PageObjectBase } from './base.po'; - -export class LoginPage extends PageObjectBase { - constructor() { - super('app-login', '/login'); - } - - waitForError() { - browser.wait(ExpectedConditions.presenceOf(element(by.css('.error'))), 3000); - } - - getErrorMessage() { - return element(by.css('.error')).getText(); - } - - enterEMail(email: string) { - this.enterInputText('#email-input', email); - } - - enterPassword(password: string) { - this.enterInputText('#password-input', password); - } - - clickSignIn() { - this.clickButton('#signin-button'); - } -} -``` - -#### Testing Scripts - -Similar to unit tests, end-to-end test scripts consist of nested `describe()` and `it()` functions. In the case of end-to-end tests, the `describe()` functions generally denote specific scenarios with the `it()` functions denoting specific behaviors that should be exhibited by the application as actions are performed within that scenario. - -Also similar to unit tests, the labels used in the `describe()` and `it()` functions should make sense both with the "describe" or "it" and when concatenated together to form the complete test case. - -Here is a sample end-to-end test script that exercises some typical login scenarios. - -```tsx -import { AppPage } from '../page-objects/pages/app.po'; -import { AboutPage } from '../page-objects/pages/about.po'; -import { CustomersPage } from '../page-objects/pages/customers.po'; -import { LoginPage } from '../page-objects/pages/login.po'; -import { MenuPage } from '../page-objects/pages/menu.po'; -import { TasksPage } from '../page-objects/pages/tasks.po'; - -describe('Login', () => { - const about = new AboutPage(); - const app = new AppPage(); - const customers = new CustomersPage(); - const login = new LoginPage(); - const menu = new MenuPage(); - const tasks = new TasksPage(); - - beforeEach(() => { - app.load(); - }); - - describe('before logged in', () => { - it('displays the login screen', () => { - expect(login.rootElement().isDisplayed()).toEqual(true); - }); - - it('allows in-app navigation to about', () => { - menu.clickAbout(); - about.waitUntilVisible(); - login.waitUntilInvisible(); - }); - - it('does not allow in-app navigation to tasks', () => { - menu.clickTasks(); - app.waitForPageNavigation(); - expect(login.rootElement().isDisplayed()).toEqual(true); - }); - - it('does not allow in-app navigation to customers', () => { - menu.clickCustomers(); - app.waitForPageNavigation(); - expect(login.rootElement().isDisplayed()).toEqual(true); - }); - - it('displays an error message if the login fails', () => { - login.enterEMail('test@test.com'); - login.enterPassword('bogus'); - login.clickSignIn(); - login.waitForError(); - expect(login.getErrorMessage()).toEqual('The password is invalid or the user does not have a password.'); - }); - - it('navigates to the tasks page if the login succeeds', () => { - login.enterEMail('test@test.com'); - login.enterPassword('testtest'); - login.clickSignIn(); - tasks.waitUntilVisible(); - }); - }); - - describe('once logged in', () => { - beforeEach(() => { - tasks.waitUntilVisible(); - }); - - it('allows navigation to the customers page', () => { - menu.clickCustomers(); - customers.waitUntilVisible(); - tasks.waitUntilInvisible(); - }); - - it('allows navigation to the about page', () => { - menu.clickAbout(); - about.waitUntilVisible(); - tasks.waitUntilInvisible(); - }); - - it('allows navigation back to the tasks page', () => { - menu.clickAbout(); - tasks.waitUntilInvisible(); - menu.clickTasks(); - tasks.waitUntilVisible(); - }); - }); -}); -``` - -### Configuration - -The default configuration uses the same `environment.ts` file that is used for development. In order to provide better control over the data used by the end-to-end tests, it is often useful to create a specific environment for testing and use that environment for the tests. This section shows one possible way to create this configuration. - -#### Testing Environment - -Setting up a testing environment involves creating a new environment file that uses a dedicated testing backend, updating the `angular.json` file to use that environment, and modifying the `e2e` script in the `package.json` to specify the `test` environment. - -##### Create the `environment.e2e.ts` File - -The Angular `environment.ts` and `environment.prod.ts` files are often used to store information such as the base URL for the application's backend data services. Create an `environment.e2e.ts` that provides the same information, only connecting to backend services that are dedicated to testing rather than the development or production backend services. Here is an example: - -```tsx -export const environment = { - production: false, - databaseURL: 'https://e2e-test-api.my-great-app.com', - projectId: 'my-great-app-e2e', -}; -``` - -##### Modify the `angular.json` File - -The `angular.json` file needs to be modified to use this file. This is a layered process. Follow the XPaths listed below to add the configuration that is required. - -Add a configuration at `/projects/app/architect/build/configurations` called `test` that does the file replacement: - -```json - "test": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.e2e.ts" - } - ] - } -``` - -Add a configuration at `/projects/app/architect/serve/configurations` called `test` that points the browser target at the `test` build configuration that was defined above. - -```json - "test": { - "browserTarget": "app:build:test" - } -``` - -Add a configuration at `/projects/app-e2e/architect/e2e/configurations` called `test` that does points the dev server target at the `test` serve configuration defined above. - -```json - "test": { - "devServerTarget": "app:serve:test" - } -``` - -##### Modify the `package.json` File - -Modify the `package.json` file so that `npm run e2e` uses the `test` configuration. - -```json - "scripts": { - "e2e": "ng e2e --configuration=test", - "lint": "ng lint", - "ng": "ng", - "start": "ng serve", - "test": "ng test", - "test:dev": "ng test --browsers=ChromeHeadlessCI", - "test:ci": "ng test --no-watch --browsers=ChromeHeadlessCI" - }, -``` - -#### Test Cleanup - -If the end-to-end tests modify data in any way it is helpful to reset the data to a known state once the test completes. One way to do that is to: - -1. Create an endpoint that performs the cleanup. -1. Add a `onCleanUp()` function to the `config` object exported by the `protractor.conf.js` file. - -Here is an example: - -```javascript - onCleanUp() { - const axios = require('axios'); - return axios - .post( - 'https://e2e-test-api.my-great-app.com/purgeDatabase', - {} - ) - .then(res => { - console.log(res.data); - }) - .catch(err => console.log(err)); - } -``` diff --git a/versioned_docs/version-v5/angular/your-first-app.md b/versioned_docs/version-v5/angular/your-first-app.md deleted file mode 100644 index a44f9cab7dc..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -sidebar_label: Build Your First App ---- - -# Your First Ionic App: Angular - -The great thing about Ionic is that with one codebase, you can build for any platform using just HTML, CSS, and JavaScript. Follow along as we learn the fundamentals of Ionic app development by creating a realistic app step by step. - -Here’s the finished app running on all 3 platforms: - - - -:::note -Looking for the previous version of this guide that covered Ionic 4 and Cordova? [See here.](../developer-resources/guides/first-app-v4/intro.md) -::: - -## What We'll Build - -We'll create a Photo Gallery app that offers the ability to take photos with your device's camera, display them in a grid, and store them permanently on the device. - -Highlights include: - -- One Angular-based codebase that runs on the web, iOS, and Android using Ionic Framework [UI components](https://ionicframework.com/docs/components). -- Deployed as a native iOS and Android mobile app using [Capacitor](https://capacitorjs.com), Ionic's official native app runtime. -- Photo Gallery functionality powered by the Capacitor [Camera](https://capacitorjs.com/docs/apis/camera), [Filesystem](https://capacitorjs.com/docs/apis/filesystem), and [Preferences](https://capacitorjs.com/docs/apis/preferences) APIs. - -Find the complete app code referenced in this guide [on GitHub](https://github.com/ionic-team/photo-gallery-capacitor-ng). - -## Download Required Tools - -Download and install these right away to ensure an optimal Ionic development experience: - -- **Node.js** for interacting with the Ionic ecosystem. [Download the LTS version here](https://nodejs.org/en/). -- **A code editor** for... writing code! We are fans of [Visual Studio Code](https://code.visualstudio.com/). -- **Command-line interface/terminal (CLI)**: - - **Windows** users: for the best Ionic experience, we recommend the built-in command line (cmd) or the Powershell - CLI, running in Administrator mode. - - **Mac/Linux** users, virtually any terminal will work. - -## Install Ionic Tooling - -Run the following in the command line terminal to install the Ionic CLI (`ionic`), `native-run`, used to run native binaries on devices and simulators/emulators, and `cordova-res`, used to generate native app icons and splash screens: - -:::note -To open a terminal in Visual Studio Code, go to Terminal -> New Terminal. -::: - -```shell -npm install -g @ionic/cli native-run cordova-res -``` - -:::note -The `-g` option means _install globally_. When packages are installed globally, `EACCES` permission errors can occur. - -Consider setting up npm to operate globally without elevated permissions. See [Resolving Permission Errors](../developing/tips.md#resolving-permission-errors) for more information. -::: - -## Create an App - -Next, create an Ionic Angular app that uses the “Tabs” starter template and adds Capacitor for native functionality: - -```shell -ionic start photo-gallery tabs --type=angular --capacitor -``` - -This starter project comes complete with three pre-built pages and best practices for Ionic development. With common building blocks already in place, we can add more features easily! - -Next, change into the app folder: - -```shell -cd photo-gallery -``` - -Next we'll need to install the necessary Capacitor plugins to make the app's native functionality work: - -```shell -npm install @capacitor/camera @capacitor/preferences @capacitor/filesystem -``` - -### PWA Elements - -Some Capacitor plugins, including the Camera API, provide the web-based functionality and UI via the Ionic [PWA Elements library](https://github.com/ionic-team/ionic-pwa-elements). - -It's a separate dependency, so install it next: - -```shell -npm install @ionic/pwa-elements -``` - -Next, import `@ionic/pwa-elements` by editing `src/main.ts`. - -```tsx -import { defineCustomElements } from '@ionic/pwa-elements/loader'; - -// Call the element loader after the platform has been bootstrapped -defineCustomElements(window); -``` - -That’s it! Now for the fun part - let’s see the app in action. - -## Run the App - -Run this command next: - -```shell -ionic serve -``` - -And voilà! Your Ionic app is now running in a web browser. Most of your app can be built and tested right in the browser, greatly increasing development and testing speed. - -## Photo Gallery!!! - -There are three tabs. Click on the Tab2 tab. It’s a blank canvas, aka the perfect spot to transform into a Photo Gallery. The Ionic CLI features Live Reload, so when you make changes and save them, the app is updated immediately! - -![Before and after going through this tutorial](/img/guides/first-app-cap-ng/email-photogallery.gif) - -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 - - - -``` - -`ion-header` represents the top navigation and toolbar, with "Tab 2" as the title (there are two of them due to iOS [Collapsible Large Title](https://ionicframework.com/docs/api/title#collapsible-large-titles) support). Rename both `ion-title` elements to: - -```html -Photo Gallery -``` - -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 - - - - - - - -``` - -Next, open `src/app/tabs/tabs.page.html`. Change the label to “Photos” and the icon name to “images”: - -```html - - - Photos - -``` - -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. diff --git a/versioned_docs/version-v5/angular/your-first-app/2-taking-photos.md b/versioned_docs/version-v5/angular/your-first-app/2-taking-photos.md deleted file mode 100644 index 70f6ed01e3f..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app/2-taking-photos.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -sidebar_label: Taking Photos ---- - -# Taking Photos with the Camera - -Now for the fun part - adding the ability to take photos with the device’s camera using the Capacitor [Camera API](https://capacitorjs.com/docs/apis/camera). We’ll begin with building it for the web, then make some small tweaks to make it work on mobile (iOS and Android). - -## Photo Service - -All Capacitor logic (Camera usage and other native features) will be encapsulated in a service class. Create `PhotoService` using the `ionic generate` command: - -```shell -ionic g service services/photo -``` - -Open the new `services/photo.service.ts` file, and let’s add the logic that will power the camera functionality. First, import Capacitor dependencies and get references to the Camera, Filesystem, and Storage plugins: - -```tsx -import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera'; -import { Filesystem, Directory } from '@capacitor/filesystem'; -import { Preferences } from '@capacitor/preferences'; -``` - -Next, define a new class method, `addNewToGallery`, that will contain the core logic to take a device photo and save it to the filesystem. Let’s start by opening the device camera: - -```tsx -public async addNewToGallery() { - // Take a photo - const capturedPhoto = await Camera.getPhoto({ - resultType: CameraResultType.Uri, - source: CameraSource.Camera, - quality: 100 - }); -} -``` - -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. - -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 { PhotoService } from '../services/photo.service'; - -constructor(public photoService: PhotoService) { } - -addPhotoToGallery() { - this.photoService.addNewToGallery(); -} -``` - -Then, open `tab2.page.html` and call the `addPhotoToGallery()` function when the FAB is tapped/clicked: - -```html - - - - - - - -``` - -Save the file, and if it's not running already, restart the development server in your browser by running `ionic serve`. On the Photo Gallery tab, click the Camera button. If your computer has a webcam of any sort, a modal window appears. Take a selfie! - -![Camera API on the web](/img/guides/first-app-cap-ng/camera-web.png) - -_(Your selfie is probably much better than mine)_ - -After taking a photo, it disappears right away. We need to display it within our app and save it for future access. - -## Displaying Photos - -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 { - filepath: string; - webviewPath: string; -} -``` - -Back at the top of the file, define an array of Photos, which will contain a reference to each photo captured with the Camera. - -```tsx -export class PhotoService { - public photos: UserPhoto[] = []; - - // other code -} -``` - -Over in the `addNewToGallery` function, add the newly captured photo to the beginning of the Photos array. - -```tsx - const capturedPhoto = await Camera.getPhoto({ - resultType: CameraResultType.Uri, - source: CameraSource.Camera, - quality: 100 - }); - - this.photos.unshift({ - filepath: "soon...", - webviewPath: capturedPhoto.webPath - }); -} -``` - -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 - - - - - - - - - - - -``` - -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! - -Up next, we’ll add support for saving the photos to the filesystem, so they can be retrieved and displayed in our app at a later time. diff --git a/versioned_docs/version-v5/angular/your-first-app/3-saving-photos.md b/versioned_docs/version-v5/angular/your-first-app/3-saving-photos.md deleted file mode 100644 index f6a0874aca8..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app/3-saving-photos.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -sidebar_label: Saving Photos ---- - -# Saving Photos to the Filesystem - -We’re now able to take multiple photos and display them in a photo gallery on the second tab of our app. These photos, however, are not currently being stored permanently, so when the app is closed, they will be deleted. - -## Filesystem API - -Fortunately, saving them to the filesystem only takes a few steps. Begin by creating a new class method, `savePicture()`, in the `PhotoService` class (`src/app/services/photo.service.ts`). We pass in the `photo` object, which represents the newly captured device photo: - -```tsx -private async savePicture(photo: Photo) { } -``` - -We can use this new method immediately in `addNewToGallery()`: - -```tsx -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) - }); - - // Save the picture and add it to photo collection - const savedImageFile = await this.savePicture(capturedPhoto); - this.photos.unshift(savedImageFile); -} -``` - -We’ll use the Capacitor [Filesystem API](https://capacitorjs.com/docs/apis/filesystem) to save the photo to the filesystem. To start, convert the photo to base64 format, then feed the data to the Filesystem’s `writeFile` function. As you’ll recall, we display each photo on the screen by setting each image’s source path (`src` attribute) in `tab2.page.html` to the webviewPath property. So, set it then return the new Photo object. - -```tsx -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 = new Date().getTime() + '.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 - }; -} -``` - -`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: - -```tsx -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; -} - -convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onerror = reject; - reader.onload = () => { - resolve(reader.result); - }; - reader.readAsDataURL(blob); -}); -``` - -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. diff --git a/versioned_docs/version-v5/angular/your-first-app/4-loading-photos.md b/versioned_docs/version-v5/angular/your-first-app/4-loading-photos.md deleted file mode 100644 index 1e6896557cf..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app/4-loading-photos.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_label: Loading Photos ---- - -# Loading Photos from the Filesystem - -We’ve implemented photo taking and saving to the filesystem. There’s one last piece of functionality missing: the photos are stored in the filesystem, but we need a way to save pointers to each file so that they can be displayed again in the photo gallery. - -Fortunately, this is easy: we’ll leverage the Capacitor [Preferences API](https://capacitorjs.com/docs/apis/preferences) to store our array of Photos in a key-value store. - -## Preferences API - -Begin by defining a constant variable that will act as the key for the store: - -```tsx -export class PhotoService { - public photos: UserPhoto[] = []; - private PHOTO_STORAGE: string = 'photos'; - - // 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), -}); -``` - -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: - -```tsx -public async loadSaved() { - // Retrieve cached photo array data - const photoList = await Preferences.get({ key: this.PHOTO_STORAGE }); - this.photos = JSON.parse(photoList.value) || []; - - // more to come... -} -``` - -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: - -```tsx -// 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}`; -} -``` - -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. - -```tsx -async ngOnInit() { - await this.photoService.loadSaved(); -} -``` - -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! diff --git a/versioned_docs/version-v5/angular/your-first-app/5-adding-mobile.md b/versioned_docs/version-v5/angular/your-first-app/5-adding-mobile.md deleted file mode 100644 index 72ef3429132..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app/5-adding-mobile.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -strip_number_prefixes: false ---- - -# Adding Mobile - -Our photo gallery app won’t be complete until it runs on iOS, Android, and the web - all using one codebase. All it takes is some small logic changes to support mobile platforms, installing some native tooling, then running the app on a device. Let’s go! - -## Import Platform API - -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): - -```tsx -import { Platform } from '@ionic/angular'; - -export class PhotoService { - public photos: UserPhoto[] = []; - private PHOTO_STORAGE: string = 'photos'; - private platform: Platform; - - constructor(platform: Platform) { - this.platform = platform; - } - - // 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: - -```tsx -private async readAsBase64(photo: Photo) { - // "hybrid" will detect Cordova or Capacitor - if (this.platform.is('hybrid')) { - // Read the file into base64 format - const file = await Filesystem.readFile({ - path: photo.path - }); - - return file.data; - } - else { - // 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; - } -} -``` - -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)). - -```tsx -// Save picture to file on device - 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 = new Date().getTime() + '.jpeg'; - const savedFile = await Filesystem.writeFile({ - path: fileName, - data: base64Data, - directory: Directory.Data - }); - - if (this.platform.is('hybrid')) { - // Display the new image by rewriting the 'file://' path to HTTP - // Details: https://ionicframework.com/docs/building/webview#file-protocol - return { - filepath: savedFile.uri, - webviewPath: Capacitor.convertFileSrc(savedFile.uri), - }; - } - else { - // Use webPath to display the new image instead of base64 since it's - // already loaded into memory - return { - filepath: fileName, - webviewPath: photo.webPath - }; - } - } -``` - -Next, head back over to the `loadSaved()` function we implemented for the web earlier. On mobile, we can directly set the source of an image tag - `` - to each photo file on the Filesystem, displaying them automatically. Thus, only the web requires reading each image from the Filesystem into base64 format. Update this function to add an _if statement_ around the Filesystem code: - -```tsx -public async loadSaved() { - // Retrieve cached photo array data - const photoList = await Preferences.get({ key: this.PHOTO_STORAGE }); - this.photos = JSON.parse(photoList.value) || []; - - // Easiest way to detect when running on the web: - // “when the platform is NOT hybrid, do this” - if (!this.platform.is('hybrid')) { - // 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}`; - } - } -} -``` - -Our Photo Gallery now consists of one codebase that runs on the web, Android, and iOS. Next up, the part you’ve been waiting for - deploying the app to a device. diff --git a/versioned_docs/version-v5/angular/your-first-app/6-deploying-mobile.md b/versioned_docs/version-v5/angular/your-first-app/6-deploying-mobile.md deleted file mode 100644 index 790e83ef828..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app/6-deploying-mobile.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -sidebar_label: Deploying Mobile ---- - -# Deploying to iOS and Android - -Since we added Capacitor to our project when it was first created, there’s only a handful of steps remaining until the Photo Gallery app is on our device! Remember, you can find the complete source code for this app [here](https://github.com/ionic-team/photo-gallery-capacitor-ng). - -## Capacitor Setup - -Capacitor is Ionic’s official app runtime that makes it easy to deploy web apps to native platforms like iOS, Android, and more. If you’ve used Cordova in the past, consider reading more about the differences [here](https://capacitorjs.com/docs/cordova#differences-between-capacitor-and-cordova). - -If you’re still running `ionic serve` in the terminal, cancel it. Complete a fresh build of your Ionic project, fixing any errors that it reports: - -```shell -ionic build -``` - -Next, create both the iOS and Android projects: - -```shell -$ ionic cap add ios -$ ionic cap add android -``` - -Both android and ios folders at the root of the project are created. These are entirely standalone native projects that should be considered part of your Ionic app (i.e., check them into source control, edit them using their native tooling, etc.). - -Every time you perform a build (e.g. `ionic build`) that updates your web directory (default: `www`), you'll need to copy those changes into your native projects: - -```shell -ionic cap copy -``` - -Note: After making updates to the native portion of the code (such as adding a new plugin), use the `sync` command: - -```shell -ionic cap sync -``` - -## iOS Deployment - -:::note -To build an iOS app, you’ll need a Mac computer. -::: - -Capacitor iOS apps are configured and managed through Xcode (Apple’s iOS/Mac IDE), with dependencies managed by [CocoaPods](https://cocoapods.org/). Before running this app on an iOS device, there's a couple of steps to complete. - -First, run the Capacitor `open` command, which opens the native iOS project in Xcode: - -```shell -ionic cap open ios -``` - -In order for some native plugins to work, user permissions must be configured. In our photo gallery app, this includes the Camera plugin: iOS displays a modal dialog automatically after the first time that `Camera.getPhoto()` is called, prompting the user to allow the app to use the Camera. The permission that drives this is labeled “Privacy - Camera Usage.” To set it, the `Info.plist` file must be modified ([more details here](https://capacitorjs.com/docs/ios/configuration)). To access it, click "Info," then expand "Custom iOS Target Properties." - -![Xcode Custom iOS Target Properties](/img/guides/first-app-cap-ng/xcode-info-plist.png) - -Each setting in `Info.plist` has a low-level parameter name and a high-level name. By default, the property list editor shows the high-level names, but it's often useful to switch to showing the raw, low-level names. To do this, right-click anywhere in the property list editor and toggle "Raw Keys/Values." - -Add the `NSCameraUsageDescription` Key and set the Value to something that describes why the app needs to use the camera, such as "To Take Photos." The Value field is displayed to the app user when the permission prompt opens. - -Follow the same process to add the other two Keys required of the Camera plugin: `NSPhotoLibraryAddUsageDescription` and `NSPhotoLibraryUsageDescription`. - -Next, click on `App` in the Project Navigator on the left-hand side, then within the `Signing & Capabilities` section, select your Development Team. - -![Xcode - Selecting Development Team](/img/guides/first-app-cap-ng/xcode-signing.png) - -With permissions in place and Development Team selected, we are ready to try out the app on a real device! Connect an iOS device to your Mac computer, select it (`App -> Matthew’s iPhone` for me) then click the "Build" button to build, install, and launch the app on your device: - -![Xcode build button](/img/guides/first-app-cap-ng/xcode-build-button.png) - -Upon tapping the Camera button on the Photo Gallery tab, the permission prompt will display. Tap OK, then take a picture with the Camera. Afterward, the photo shows in the app! - -![iOS Camera permissions](/img/guides/first-app-cap-ng/ios-permissions-photo.png) - -## Android Deployment - -Capacitor Android apps are configured and managed through Android Studio. Before running this app on an Android device, there's a couple of steps to complete. - -First, run the Capacitor `open` command, which opens the native Android project in Android Studio: - -```shell -ionic cap open android -``` - -Similar to iOS, we must enable the correct permissions to use the Camera. Configure these in the `AndroidManifest.xml` file. Android Studio will likely open this file automatically, but in case it doesn't, locate it under `android/app/src/main/`. - -![Android Manifest location](/img/guides/first-app-cap-ng/android-manifest.png) - -Scroll to the `Permissions` section and ensure these entries are included: - -```xml - - -``` - -Save the file. With permissions in place, we are ready to try out the app on a real device! Connect an Android device to your computer. Within Android Studio, click the "Run" button, select the attached Android device, then click OK to build, install, and launch the app on your device. - -![Launching app on Android](/img/guides/first-app-cap-ng/android-device.png) - -Once again, upon tapping the Camera button on the Photo Gallery tab, the permission prompt should be displayed. Tap OK, then take a picture with the Camera. Afterward, the photo should appear in the app. - -![Android Camera permissions](/img/guides/first-app-cap-ng/android-permissions-photo.png) - -Our Photo Gallery app has just been deployed to Android and iOS devices. 🎉 - -In the final portion of this tutorial, we’ll use the Ionic CLI’s Live Reload functionality to quickly implement photo deletion - thus completing our Photo Gallery feature. diff --git a/versioned_docs/version-v5/angular/your-first-app/7-live-reload.md b/versioned_docs/version-v5/angular/your-first-app/7-live-reload.md deleted file mode 100644 index fdce9ac6848..00000000000 --- a/versioned_docs/version-v5/angular/your-first-app/7-live-reload.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -sidebar_label: Live Reload ---- - -# Rapid App Development with Live Reload - -So far, we’ve seen how easy it is to develop a cross-platform app that works everywhere. The development experience is pretty quick, but what if I told you there was a way to go faster? - -We can use the Ionic CLI’s [Live Reload functionality](https://ionicframework.com/docs/cli/livereload) to boost our productivity when building Ionic apps. When active, Live Reload will reload the browser and/or WebView when changes in the app are detected. - -## Live Reload - -Remember `ionic serve`? That was Live Reload working in the browser, allowing us to iterate quickly. - -We can also use it when developing on iOS and Android devices. This is particularly useful when writing code that interacts with native plugins - we must run it on a device to verify that it works. Therefore, being able to quickly write, build, test, and deploy code is crucial to keeping up our development speed. - -Let’s use Live Reload to implement photo deletion, the missing piece of our Photo Gallery feature. Select your platform of choice (iOS or Android) and connect a device to your computer. Next, run either command in a terminal, based on your chosen platform: - -```shell -$ ionic cap run ios -l --external - -$ ionic cap run android -l --external -``` - -The Live Reload server will start up, and the native IDE of choice will open if not opened already. Within the IDE, click the Play button to launch the app onto your device. - -## Deleting Photos - -With Live Reload running and the app open on your device, let’s implement photo deletion functionality. Open `tab2.page.html` and add a new click handler to each `` element. When the app user taps on a photo in our gallery, we’ll display an [Action Sheet](https://ionicframework.com/docs/api/action-sheet) dialog with the option to either delete the selected photo or cancel (close) the dialog. - -```html - - - -``` - -Over in `tab2.page.ts`, import Action Sheet and add it to the constructor: - -```tsx -import { ActionSheetController } from '@ionic/angular'; - -constructor(public photoService: PhotoService, - public actionSheetController: ActionSheetController) {} -``` - -Add `UserPhoto` to the import statement. - -```tsx -import { PhotoService, UserPhoto } from '../services/photo.service'; -``` - -Next, implement the `showActionSheet()` function. We add two options: `Delete` that calls PhotoService’s `deletePicture()` function (to be added next) and `Cancel`, which when given the role of “cancel” will automatically close the action sheet: - -```tsx -public async showActionSheet(photo: UserPhoto, position: number) { - const actionSheet = await this.actionSheetController.create({ - header: 'Photos', - buttons: [{ - text: 'Delete', - role: 'destructive', - icon: 'trash', - handler: () => { - this.photoService.deletePicture(photo, position); - } - }, { - text: 'Cancel', - icon: 'close', - role: 'cancel', - handler: () => { - // Nothing to do, action sheet is automatically closed - } - }] - }); - await actionSheet.present(); -} -``` - -Save both of the files we just edited. The Photo Gallery app will reload automatically, and now when we tap on one of the photos in the gallery, the action sheet displays. Tapping “Delete” doesn’t do anything yet, so head back into your code editor. - -In `src/app/services/photo.service.ts`, add the `deletePicture()` function: - -```tsx -public async deletePicture(photo: UserPhoto, position: number) { - // Remove this photo from the Photos reference data array - this.photos.splice(position, 1); - - // Update photos array cache by overwriting the existing photo array - Preferences.set({ - key: this.PHOTO_STORAGE, - value: JSON.stringify(this.photos) - }); - - // delete photo file from filesystem - const filename = photo.filepath - .substr(photo.filepath.lastIndexOf('/') + 1); - - await Filesystem.deleteFile({ - path: filename, - directory: Directory.Data - }); -} -``` - -The selected photo is removed from the Photos array first. Then, we use the Capacitor Preferences API to update the cached version of the Photos array. Finally, we delete the actual photo file itself using the Filesystem API. - -Save this file, then tap on a photo again and choose the “Delete” option. This time, the photo is deleted! Implemented much faster using Live Reload. 💪 - -## What’s Next? - -Congratulations! You built a complete cross-platform Photo Gallery app that runs on the web, iOS, and Android. There are many paths to follow from here. Try adding another [Ionic UI component](https://ionicframework.com/docs/components) to the app, or more [native functionality](https://capacitorjs.com/docs/apis). The sky’s the limit. - -Happy app building! 💙 diff --git a/versioned_docs/version-v5/contributing/coc.md b/versioned_docs/version-v5/contributing/coc.md deleted file mode 100644 index b347ee6e9b0..00000000000 --- a/versioned_docs/version-v5/contributing/coc.md +++ /dev/null @@ -1,13 +0,0 @@ -# Code of Conduct - -As contributors and maintainers of the Ionic project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities. - -Communication through any of Ionic's channels (GitHub, Slack, Forum, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. - -We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Ionic project to do the same. - -If any member of the community violates this code of conduct, the maintainers of the Ionic project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate. - -If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at devrel@ionic.io. - -Please click here to review to Ionic's full code of conduct. diff --git a/versioned_docs/version-v5/contributing/how-to-contribute.md b/versioned_docs/version-v5/contributing/how-to-contribute.md deleted file mode 100644 index 76372852bff..00000000000 --- a/versioned_docs/version-v5/contributing/how-to-contribute.md +++ /dev/null @@ -1,252 +0,0 @@ ---- -sidebar_label: How to Contribute ---- - -# Contributing to Ionic - -Thanks for the interest in contributing to Ionic Framework! - -## Contributing Etiquette - -Please see the [Contributor Code of Conduct](coc.md) for information on the rules of conduct. - -## Creating an Issue - -- If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Worldwide Slack](https://ionicworldwide.herokuapp.com/) group. - -- It is required that you clearly describe the steps necessary to reproduce the issue you are running into. Although we would love to help our users as much as possible, diagnosing issues without clear reproduction steps is extremely time-consuming and simply not sustainable. - -- The issue list of the [Ionic](https://github.com/ionic-team/ionic) repository is exclusively for bug reports and feature requests. Non-conforming issues will be closed immediately. - -- Issues with no clear steps to reproduce will not be triaged. If an issue is labeled with "needs: reply" and receives no further replies from the author of the issue for more than 14 days, it will be closed. - -- If you think you have found a bug, or have a new feature idea, please start by making sure it hasn't already been [reported](https://github.com/ionic-team/ionic/issues?utf8=%E2%9C%93&q=is%3Aissue). You can search through existing issues to see if there is a similar one reported. Include closed issues as it may have been closed with a solution. - -- Next, [create a new issue](https://github.com/ionic-team/ionic/issues/new/choose) that thoroughly explains the problem. Please fill out the populated issue form before submitting the issue. - -## Creating a Good Code Reproduction - -### What is a Code Reproduction? - -A code reproduction is a small application that is built to demonstrate a particular issue. The code reproduction should contain the minimum amount of code needed to recreate the issue and should focus on a single issue. - -### Why Should You Create a Reproduction? - -A code reproduction of the issue you are experiencing helps us better isolate the cause of the problem. This is an important first step to getting any bug fixed! - -Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed. In other words, creating a code reproduction of the issue helps us help you. - -### How to Create a Reproduction - -- Create a new Ionic application using one of our starter templates. The `blank` starter application is a great choice for this. You can create one using the following Ionic CLI command: `ionic start myApp blank` -- Add the minimum amount of code needed to recreate the issue you are experiencing. Do not include anything that is not required to reproduce the issue. This includes any 3rd party plugins you have installed. -- Publish the application on GitHub and include a link to it when [creating an issue](#creating-an-issue). -- Be sure to include steps to reproduce the issue. These steps should be clear and easy to follow. - -### Benefits of Creating a Reproduction - -- **Uses the latest version of Ionic:** By creating a new Ionic application, you are ensuring that you are testing against the latest version of the framework. Sometimes the issues you are experiencing have already been resolved in a newer version of the framework! -- **Minimal surface area:** By removing code that is not needed in order to reproduce the issue, it makes it easier to identify the cause of the issue. -- **No secret code needed:** Creating a minimal reproduction of the issue prevents you from having to publish any proprietary code used in your project. -- **Get help fixing the issue:** If we can reliably reproduce an issue, there is a good chance we will be able to address it. - -## Creating a Pull Request - -- We appreciate you taking the time to contribute! Before submitting a pull request, we ask that you please [create an issue](#creating-an-issue) that explains the bug or feature request and let us know that you plan on creating a pull request for it. If an issue already exists, please comment on that issue letting us know you would like to submit a pull request for it. This helps us to keep track of the pull request and make sure there isn't duplicated effort. - -- Looking for an issue to fix? Make sure to look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label! - -### Setup - -1. [Download the installer](https://nodejs.org/) for the LTS version of Node.js. This is the best way to also [install npm](https://blog.npmjs.org/post/85484771375/how-to-install-npm#_=_). -2. Fork the [Ionic](https://github.com/ionic-team/ionic) repository. -3. Clone your fork. -4. Create a new branch from master for your change. -5. Navigate into the directory of the package you wish to modify (core, angular, etc.). -6. Run `npm install` to install dependencies for this package. -7. Follow the steps for the specific package below. - -### Core - -#### Modifying Components - -1. Locate the component(s) to modify inside `/core/src/components/`. -2. Take a look at the [Stencil Documentation](https://stenciljs.com/docs/introduction/) and other components to understand the implementation of these components. -3. Make your changes to the component. If the change is overly complex or out of the ordinary, add comments so we can understand the changes. -4. [Preview your changes](#preview-changes) locally. -5. [Modify the documentation](#modifying-documentation) if needed. -6. [Run lint](#lint-changes) on the directory and make sure there are no errors. -7. [Build the project](#building-changes). -8. After the build is finished, commit the changes. Please follow the [commit message format](#commit-message-format) for every commit. -9. [Submit a Pull Request](#submit-pull-request) of your changes. - -#### Preview Changes - -1. Run `npm start` from within the `core` directory. -2. A browser should open at `http://localhost:3333/`. -3. From here, navigate to one of the component's tests to preview your changes. -4. If a test showing your change doesn't exist, [add a new test or update an existing one](#modifying-tests). -5. To test in RTL mode, once you are in the desired component's test, add `?rtl=true` at the end of the url; for example: `http://localhost:3333/src/components/alert/test/basic?rtl=true`. - -#### Lint Changes - -1. Run `npm run lint` to lint the TypeScript and Sass. -2. If there are lint errors, run `npm run lint.fix` to automatically fix any errors. Repeat step 1 to ensure the errors have been fixed, and manually fix them if not. -3. To lint and fix only TypeScript errors, run `npm run lint.ts` and `npm run lint.ts.fix`, respectively. -4. To lint and fix only Sass errors, run `npm run lint.sass` and `npm run lint.sass.fix`, respectively. - -#### Modifying Documentation - -1. Locate the `readme.md` file in the component's directory. -2. Modify the documentation **above** the line that says `` in this file. -3. To update any of the auto generated documentation below that line, make the relevant changes in the following places: - -- `Usage`: update the component's usage examples in the component's `usage/` directory -- `Properties`, `Events`, or `Methods`: update the component's TypeScript file (`*.tsx`) -- `CSS Custom Properties`: update the component's main Sass file (`*.scss`) - -#### Modifying Tests - -1. Locate the test to modify inside the `test/` folder in the component's directory. -2. If a test exists, modify the test by adding an example to reproduce the problem fixed or feature added. -3. If a new test is needed, the easiest way is to copy the `basic/` directory from the component's `test/` directory, rename it, and edit the content in both the `index.html` and `e2e.ts` file (see [Screenshot Tests](#screenshot-tests) for more information on this file). -4. The `preview/` directory is used in the documentation as a demo. Only update this test if there is a bug in the test or if the API has a change that hasn't been updated in the test. - -##### Screenshot Tests - -1. If the test exists in screenshot, there will be a file named `e2e.ts` in the directory of the test. -2. A screenshot test can be added by including this file and adding one or more `test()` calls that include a call to `page.compareScreenshot()`. See [Stencil end-to-end testing](https://stenciljs.com/docs/end-to-end-testing) and existing tests in `core/` for examples. -3. **Important:** each `test()` should have only one screenshot (`page.compareScreenshot()`) call **or** it should check the expect at the end of each test. If there is a mismatch it will fail the test which will prevent the rest of the test from running, i.e. if the first screenshot fails the remaining screenshot calls would not be called _unless_ they are in a separate test or all of the expects are called at the end. -4. To run screenshot locally, use the following command: `npm run test.screenshot`. - - To run screenshot for a specific test, pass the path to the test or a string to search for. - - For example, running all `alert` tests: `npm run test.screenshot alert`. - - Or, running the basic `alert` tests: `npm run test.screenshot src/components/alert/test/basic/e2e.ts`. - -#### Building Changes - -1. Once all changes have been made and the documentation has been updated, run `npm run build` inside of the `core` directory. This will add your changes to any auto-generated files, if necessary. -2. Review the changes and, if everything looks correct, [commit](#commit-message-format) the changes. -3. Make sure the build has finished before committing. If you made changes to the documentation, properties, methods, or anything else that requires an update to a generate file, this needs to be committed. -4. After the changes have been pushed, publish the branch and [create a pull request](#creating-a-pull-request). - -### Submit Pull Request - -1. [Create a new pull request](https://github.com/ionic-team/ionic/compare) with the `master` branch as the `base`. You may need to click on `compare across forks` to find your changes. -2. See the [Creating a pull request from a fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) GitHub help article for more information. -3. Please fill out the provided Pull Request template to the best of your ability and include any issues that are related. - -## Commit Message Guidelines - -We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our [changelog](https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md). Our format closely resembles Angular's [commit message guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit). - -### Commit Message Format - -We follow the [Conventional Commits specification](https://www.conventionalcommits.org/). A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**: - -``` -(): - - - -