{"id":19857431,"url":"https://github.com/qwikdev/qwik-angular","last_synced_at":"2025-05-02T02:30:35.607Z","repository":{"id":193126934,"uuid":"661347136","full_name":"QwikDev/qwik-angular","owner":"QwikDev","description":null,"archived":false,"fork":false,"pushed_at":"2024-05-10T00:03:35.000Z","size":520,"stargazers_count":36,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-30T16:08:06.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/QwikDev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-02T15:02:28.000Z","updated_at":"2024-11-15T08:36:55.000Z","dependencies_parsed_at":"2024-10-29T21:55:14.466Z","dependency_job_id":null,"html_url":"https://github.com/QwikDev/qwik-angular","commit_stats":{"total_commits":8,"total_committers":2,"mean_commits":4.0,"dds":0.25,"last_synced_commit":"5b61b96eb2abeb807eaff1ef6781e013561c5af2"},"previous_names":["qwikdev/qwik-angular"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QwikDev%2Fqwik-angular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QwikDev%2Fqwik-angular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QwikDev%2Fqwik-angular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QwikDev%2Fqwik-angular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/QwikDev","download_url":"https://codeload.github.com/QwikDev/qwik-angular/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251972414,"owners_count":21673600,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T14:18:40.999Z","updated_at":"2025-05-02T02:30:34.086Z","avatar_url":"https://github.com/QwikDev.png","language":"TypeScript","readme":"# Qwik Angular\n\nQwikAngular allows you to use Angular components in Qwik, including the whole ecosystem of component libraries.\n\n## Installation\n\nInside your Qwik app run:\n\n```shell\nnpx add-angular-to-qwik@latest\n```\n\nIf you don't have a Qwik app yet, then you need to [create one first](../../../docs/getting-started/index.mdx), then, follow the instructions and run the command add Angular to your app.\n\n```shell\nnpm create qwik@latest\ncd to-my-app\nnpx add-angular-to-qwik@latest\n```\n\n## Usage\n\nThe @builder.io/qwik-angular package exports the qwikify$() function that lets you convert Angular components into Qwik components, that you can use across your application.\n\nAngular and Qwik components can not be mixed in the same file, if you check your project right after running the installation command, you will see a new folder src/integrations/angular/components, from now on, all your Angular components will live there. Qwikified components are declared and exported from src/integrations/angular/index.ts file, it is important to not place Qwik code in the Angular component files.\n\n## Limitations\n\n## Defining A Component\n\nThe Qwik Angular integration **only** supports rendering standalone components. You can still compose required UIs with Angular code that uses modules by wrapping it inside standalone components:\n\n```ts\nimport { NgIf } from '@angular/common';\nimport { Component, Input } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n  selector: 'app-hello',\n  standalone: true,\n  imports: [NgIf, MatButtonModule],\n  template: `\n    \u003cp\u003eHello from Angular!!\u003c/p\u003e\n\n    \u003cp *ngIf=\"show\"\u003e{{ helpText }}\u003c/p\u003e\n\n    \u003cbutton mat-raised-button color=\"primary\" (click)=\"toggle()\"\u003eToggle\u003c/button\u003e\n  `,\n})\nexport class HelloComponent {\n  @Input() helpText = 'help';\n\n  show = false;\n\n  toggle() {\n    this.show = !this.show;\n  }\n}\n```\n\n### Adding types\n\nAngular defines inputs and outputs of the component using decorators, so it's not possible to deduce them as interface properties for the qwikified component to be used as JSX element. This package provides utility types `QwikifiedComponentProps` and `WithRequiredProps` to simplify the creation of typed qwikified components.\n\n`QwikifiedComponentProps` utility expects the list of keys that represent inputs and outputs of your component. It does the following:\n\n- validates that provided keys exist within the component\n- extracts types of the provided keys for inputs\n- converts outputs to the handlers that can be used with JSX syntax (e.g. `EventEmitter\u003cstring\u003e` is converted to `(value: string) =\u003e void`)\n- adds `$` suffix for outputs, which basically [lets Qwik treat them as QRLs](https://qwik.builder.io/docs/advanced/dollar)\n\nHere's an example of how it works:\n\n```ts\n@Component({..})\nexport class InputComponent {\n  @Input() theme: 'primary' | 'accent' | 'warn' = 'primary';\n  @Input() placeholder: string;\n  @Output() changed = new EventEmitter\u003cstring\u003e();\n}\n\ntype InputComponentInputs = 'theme' | 'placeholder';\n\ntype InputComponentOutputs = 'changed';\n\n// InputComponentProps is the interface that you can export along with your qwikified component to be used elsewhere\nexport type InputComponentProps = QwikifiedComponentProps\u003c\n  InputComponent,\n  InputComponentInputs, // inputs of the \"InputComponent\"\n  InputComponentProps // outputs of the \"InputComponent\"\n\u003e;\n\n// The final type will look like\ninterface FinalInputTypeSample {\n  theme?: 'primary' | 'accent' | 'warn';\n  placeholder?: string;\n  changed$?: (value: string) =\u003e void; // notice that \"changed\" output got a \"$\" suffix!\n}\n// qwikify it later as follows\nexport const MyNgInput = qwikify$\u003cInputComponentProps\u003e(InputComponent);\n\n// additionally you can mark types as required using \"WithRequiredProps\" util\ntype RequiredInputProps = 'theme';\nexport type RequiredInputComponentProps = WithRequiredProps\u003cInputComponentProps, RequiredInputProps\u003e;\n\n// The assembled type will have \"theme\" as a required property this time\ninterface FinalInputTypeRequiredSample {\n  theme: 'primary' | 'accent' | 'warn'; // \u003c= became required!\n  placeholder?: string;\n  changed$?: (value: string) =\u003e void;\n}\n```\n\n### Every qwikified Angular component is isolated\n\nEach instance of a qwikified Angular component becomes an independent Angular app. Fully isolated.\n\n```tsx\nexport const AngularHelloComponent = qwikify$(HelloComponent);\n\u003cAngularHelloComponent\u003e\u003c/AngularHelloComponent\u003e;\n```\n\n- Each `AngularHelloComponent` is a fully isolated Angular application, with its own state, lifecycle, etc.\n- Styles will be duplicated\n- State will not be shared.\n- Islands will hydrate independently\n\n### Use `qwikify$()` as a migration strategy\n\nUsing Angular components in Qwik is a great way to migrate your application to Qwik, but it's not a silver bullet, you will need to rewrite your components to take advantage of Qwik's features.\n\nIt's also a great way to enjoy the Angular ecosystem.\n\n\u003e Don't abuse of `qwikify$()` to build your own application, all performance gains will be lost.\n\n### Build wide islands, not leaf nodes\n\nFor example, if you need to use several Angular components, to build a list, don't qwikify each individual component, instead, build the whole list as a single qwikified Angular component.\n\n#### GOOD: Wide island\n\nA single qwikified component, with all the Angular components inside. Styles will not be duplicated, and context and theming will work as expected.\n\n```ts\n// folder.component.ts\nimport List from './list.component';\nimport ListItem from './list-item.component';\nimport ListItemText from './list-item-text.component';\nimport ListItemAvatar from './list-item-avatar.component';\nimport Avatar from './avatar.component';\nimport Icon from './icon.component';\n\n// Qwikify the whole list\n@Component({\n  standalone: true,\n  imports: [List, ListItem, ListItemText, ListItemAvatar, Avatar, Icon],\n  template: `\n     \u003capp-list [sx]={ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }\u003e\n      \u003capp-list-item\u003e\n        \u003capp-list-item-avatar\u003e\n          \u003capp-avatar\u003e\n            \u003capp-icon fontIcon=\"image\"\u003e\u003c/app-icon\u003e\n          \u003c/app-avatar\u003e\n        \u003c/app-list-item-avatar\u003e\n        \u003capp-list-item-text primary=\"Photos\" secondary=\"Jan 9, 2014\"\u003e\u003c/app-list-item-text\u003e\n      \u003c/app-list-item\u003e\n      \u003capp-list-item\u003e\n        \u003capp-list-item-avatar\u003e\n          \u003capp-avatar\u003e\n            \u003capp-icon fontIcon=\"work\"\u003e\u003c/app-icon\u003e\n          \u003c/app-avatar\u003e\n        \u003c/app-list-item-avatar\u003e\n        \u003capp-list-item-text primary=\"Work\" secondary=\"Jan 7, 2014\"\u003e\u003c/app-list-item-text\u003e\n      \u003c/app-list-item\u003e\n      \u003capp-list-item\u003e\n        \u003capp-list-item-avatar\u003e\n          \u003capp-avatar\u003e\n            \u003capp-icon fontIcon=\"beach-access\"\u003e\u003c/app-icon\u003e\n          \u003c/app-avatar\u003e\n        \u003c/app-list-item-avatar\u003e\n        \u003capp-list-item-text primary=\"Vacation\" secondary=\"July 20, 2014\"\u003e\u003c/app-list-item-text\u003e\n      \u003c/app-list-item\u003e\n    \u003c/app-list\u003e\n`,\n})\nexport class FolderList {}\n```\n\n#### BAD: Leaf nodes\n\nLeaf nodes are qwikified independently, effectively rendering dozens of nested Angular applications, each fully isolated from the others, and styles being duplicated.\n\n```tsx\nimport List from './list.component';\nimport ListItem from './list-item.component';\nimport ListItemText from './list-item-text.component';\nimport ListItemAvatar from './list-item-avatar.component';\nimport Avatar from './avatar.component';\nimport Icon from './icon.component';\n\nexport const AngularList = qwikify$(List);\nexport const AngularListItem = qwikify$(ListItem);\nexport const AngularListItemText = qwikify$(ListItemText);\nexport const AngularListItemAvatar = qwikify$(ListItemAvatar);\nexport const AngularAvatar = qwikify$(Avatar);\nexport const AngularIcon = qwikify$(Icon);\n```\n\n```tsx\n// Qwik component using dozens of nested Angular islands\n// Each Angular-* is an independent Angular application\nexport const FolderList = component$(() {\n  return (\n    \u003cAngularList sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}\u003e\n      \u003cAngularListItem\u003e\n        \u003cAngularListItemAvatar\u003e\n          \u003cAngularAvatar\u003e\n            \u003cAngularIcon fontIcon=\"image\" /\u003e\n          \u003c/Avatar\u003e\n        \u003c/ListItemAvatar\u003e\n        \u003cListItemText primary=\"Photos\" secondary=\"Jan 9, 2014\" /\u003e\n      \u003c/ListItem\u003e\n      \u003cAngularListItem\u003e\n        \u003cAngularListItemAvatar\u003e\n          \u003cAngularAvatar\u003e\n            \u003cAngularIcon fontIcon=\"work\" /\u003e\n          \u003c/Avatar\u003e\n        \u003c/ListItemAvatar\u003e\n        \u003cAngularListItemText primary=\"Work\" secondary=\"Jan 7, 2014\" /\u003e\n      \u003c/ListItem\u003e\n      \u003cAngularListItem\u003e\n        \u003cAngularListItemAvatar\u003e\n          \u003cAngularAvatar\u003e\n            \u003cAngularIcon fontIcon=\"beach-access\" /\u003e\n          \u003c/Avatar\u003e\n        \u003c/ListItemAvatar\u003e\n        \u003cAngularListItemText primary=\"Vacation\" secondary=\"July 20, 2014\" /\u003e\n      \u003c/ListItem\u003e\n    \u003c/List\u003e\n  );\n});\n```\n\n## Adding interactivity\n\nIn order to add interactivity it is required to hydrate the Angular application. Angular uses [destructive hydration](https://blog.angular.io/angulars-vision-for-the-future-3cfca5e7b448), which means components are rendered on the server and then are fully recreated on the client. This [adds a massive overhead](https://www.builder.io/blog/hydration-is-pure-overhead) and making sites slow.\n\nQwik allows you decide when to hydrate your components, by using the `client:` JSX properties, this technique is commonly called partial hydration, popularized by [Astro](https://astro.build/).\n\n```diff\nexport default component$(() =\u003e {\n  return (\n    \u003c\u003e\n-      \u003cAngularComponent\u003e\u003c/AngularComponent\u003e\n+      \u003cAngularComponent client:visible\u003e\u003c/AngularComponent\u003e\n    \u003c/\u003e\n  );\n});\n```\n\nQwik comes with different strategies out of the box:\n\n### `client:load`\n\nThe component eagerly hydrates when the document loads.\n\n```tsx\n\u003cAngularComponent client:load\u003e\u003c/AngularComponent\u003e\n```\n\n**Use case:** Immediately-visible UI elements that need to be interactive as soon as possible.\n\n### `client:idle`\n\nThe component eagerly hydrates when the browser first become idle, ie, when everything important as already run before.\n\n```tsx\n\u003cAngularComponent client:idle\u003e\u003cAngularComponentlider\u003e\n```\n\n**Use case:** Lower-priority UI elements that don’t need to be immediately interactive.\n\n### `client:visible`\n\nThe component eagerly hydrates when it becomes visible in the viewport.\n\n```tsx\n\u003cAngularComponent client:visible\u003e\u003c/AngularComponent\u003e\n```\n\n**Use case:** Low-priority UI elements that are either far down the page (“below the fold”) or so resource-intensive to load that you would prefer not to load them at all if the user never saw the element.\n\n### `client:hover`\n\nThe component eagerly hydrates when the mouse is over the component.\n\n```tsx\n\u003cAngularComponent client:hover\u003e\u003c/AngularComponent\u003e\n```\n\n**Use case:** Lowest-priority UI elements which interactivity is not crucial, and only needs to run in desktop.\n\n### `client:signal`\n\nThis is an advanced API that allows to hydrate the component whenever the passed signal becomes `true`.\n\n```tsx\nexport default component$(() =\u003e {\n  const hydrateAngular = useSignal(false);\n  return (\n    \u003c\u003e\n      \u003cbutton onClick$={() =\u003e (hydrateAngular.value = true)}\u003e\n        Hydrate Angular Component when clicked\n      \u003c/button\u003e\n      \u003cAngularComponent client:signal={hydrateAngular}\u003e\u003c/AngularComponent\u003e\n    \u003c/\u003e\n  );\n});\n```\n\nThis effectively allows you to implement custom strategies for hydration.\n\n### `client:event`\n\nThe component eagerly hydrates when specified DOM events are dispatched.\n\n```tsx\n\u003cAngularComponent client:event=\"click\"\u003e\u003c/AngularComponent\u003e\n```\n\n### `client:only`\n\nWhen `true`, the component will not run in SSR, only in the browser.\n\n```tsx\n\u003cAngularComponent client:only\u003e\u003c/AngularComponent\u003e\n```\n\n## Listening to Angular events\n\nEvents in Angular are propagated as component outputs:\n\n```html\n\u003c!-- Angular code (won't work in Qwik) --\u003e\n\u003capp-slider (change)=\"console.log('value changed', $event)\"\u003e\u003c/app-slider\u003e;\n```\n\nThe `qwikify()` function will convert all outputs into properties with functional handlers\n\n```tsx\nimport { Slider } from './components';\nimport { qwikify$ } from '@builder.io/qwik-angular';\nconst AngularSlider = qwikify$(Slider);\n\u003cAngularSlider client:visible change={() =\u003e console.log('value changed')} /\u003e;\n```\n\n\u003e Notice that we use the `client:visible` property to eagerly hydrate the component, otherwise the component would not be interactive and the events would never be dispatched.\n\n## Host element\n\nWhen wrapping an Angular component with `qwikify$()`, under the hood, a new DOM element is created, such as:\n\n```html\n\u003cqwik-angular\u003e\n  \u003cbutton class=\"button\"\u003e\u003c/button\u003e\n\u003c/qwik-angular\u003e\n```\n\n\u003e Notice, that the tag name of the wrapper element is configurable via `tagName`: `qwikify$(AngularCmp, { tagName: 'my-ng' })`.\n\n### Listen to DOM events without hydration\n\nThe host element is not part of Angular, meaning that hydration is not necessary to listen for events, in order to add custom attributes and events to the host element, you can use the `host:` prefix in the JSX properties, such as:\n\n```tsx\n\u003cAngularButton\n  host:onClick$={() =\u003e {\n    console.log('click an Angular component without hydration!!');\n  }}\n/\u003e\n```\n\nThis will effectively allow you to respond to a click in an Angular button without downloading a single byte of Angular code.\n\nHappy hacking!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqwikdev%2Fqwik-angular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqwikdev%2Fqwik-angular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqwikdev%2Fqwik-angular/lists"}