{"id":22903546,"url":"https://github.com/sawyerbutton/angular-content-projection","last_synced_at":"2025-04-01T07:24:07.772Z","repository":{"id":120748032,"uuid":"154245773","full_name":"sawyerbutton/angular-content-projection","owner":"sawyerbutton","description":"Angular content Projection inherited from AngularJS Transclusion","archived":false,"fork":false,"pushed_at":"2018-10-23T02:55:45.000Z","size":77,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-07T04:41:14.153Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/sawyerbutton.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":"2018-10-23T02:00:23.000Z","updated_at":"2018-10-23T02:56:40.000Z","dependencies_parsed_at":"2023-07-07T23:03:32.667Z","dependency_job_id":null,"html_url":"https://github.com/sawyerbutton/angular-content-projection","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2Fangular-content-projection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2Fangular-content-projection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2Fangular-content-projection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2Fangular-content-projection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sawyerbutton","download_url":"https://codeload.github.com/sawyerbutton/angular-content-projection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246599085,"owners_count":20803137,"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-12-14T02:37:46.519Z","updated_at":"2025-04-01T07:24:07.767Z","avatar_url":"https://github.com/sawyerbutton.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# angular-content-projection\n\n## Angular 内容投影\n\n### `Transclusion`\n\n\u003e `Transclusion`(嵌入包含)原本是AngularJs(1.x)中的概念，随着Angular2+的推出，`Transclusion`这一单词可能已经消失了，但是其根本概念并没有丢失\n\n\u003e 从本质上来说，`Transclusion`在Angular中就是获取node节点或者html的内容并将其插入模板或者一个特定的切入点中\n\n\u003e 该概念现在通过Web API(如Shadow DOM)在Angular中完成，称为`Content-Projection`\n\n### AngularJS `Transclusion`\n\n\u003e `transclusion`在AngularJS中的实现类似于`.directive`API\n\n\u003e 在AngularJS中可以指定一个插槽进行内容转换\n\n\n#### 单槽`transclusion`\n\n```\nfunction myComponent() {\n  scope: {},\n  transclude: true,\n  template: `\n    \u003cdiv class=\"my-component\"\u003e\n      \u003cdiv ng-transclude\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  `\n};\nangular\n  .module('app')\n  .directive('myComponent', myComponent);\n```\n\n\u003e 使用`myComponent`指令\n\n```html\n\u003cmy-component\u003e\n  This is my transcluded content!\n\u003c/my-component\u003e\n```\n\n\u003e 编译后的HTML文件为\n\n```html\n\u003cmy-component\u003e\n  \u003cdiv class=\"my-component\"\u003e\n    \u003cdiv ng-transclude\u003e\n      This is my transcluded content!\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/my-component\u003e\n```\n\n#### 多槽`transclusion`\n\n\u003e 在AngularJS1.5+后可以使用`Object`定义多个`entry point`\n\n```angularjs\nfunction myComponent() {\n  scope: {},\n  transclude: {\n    slotOne: 'p',\n    slotTwo: 'div'\n  },\n  template: `\n    \u003cdiv class=\"my-component\"\u003e\n      \u003cdiv ng-transclude=\"slotOne\"\u003e\u003c/div\u003e\n      \u003cdiv ng-transclude=\"slotTwo\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  `\n};\nangular\n  .module('app')\n  .directive('myComponent', myComponent);\n```\n\n\u003e `myComponent`指令将上例中的`p`和`div`标记与相关的插槽相匹配\n\n```html\n\u003cmy-component\u003e\n  \u003cp\u003e\n    This is my transcluded content!\n  \u003c/p\u003e\n  \u003cdiv\u003e\n    Further content\n  \u003c/div\u003e\n\u003c/my-component\u003e\n```\n\n\u003e 导出后的Dom节点树为\n\n```html\n\u003cmy-component\u003e\n  \u003cdiv class=\"my-component\"\u003e\n    \u003cdiv ng-transclude=\"slotOne\"\u003e\n      \u003cp\u003e\n        This is my transcluded content!\n      \u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv ng-transclude=\"slotTwo\"\u003e\n      \u003cdiv\u003e\n        Further content\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/my-component\u003e\n```\n\n### Angular content Projection\n\n\u003e 将`transclusion`的概念移植到Angular2+中就成为了`content projection`\n\n#### 单槽内容投影\n\n\u003e 移植之后的投影功能实现更加简洁和明了\n\n```typescript\n// my-component.component.ts\nimport { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-my-component',\n  template: `\n    \u003cdiv class=\"my-component\"\u003e\n      \u003cng-content\u003e\u003c/ng-content\u003e\n    \u003c/div\u003e\n  `\n})\nexport class MyComponent {}\n```\n\n```typescript\n// app.component.ts\nimport { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-root',\n  template: `\n   \u003cdiv class=\"app\"\u003e\n     \u003capp-my-component\u003e\n       投影内容\n     \u003c/app-my-component\u003e\n   \u003c/div\u003e\n  `\n})\nexport class AppComponent {}\n```\n\n\u003e 生成的DOM树为\n\n```html\n\u003cdiv _ngcontent-c0=\"\" class=\"app\"\u003e\n  \u003capp-my-component _ngcontent-c0=\"\" _nghost-c1=\"\"\u003e\n    \u003cdiv _ngcontent-c1=\"\" class=\"my-component\"\u003e\n      投影内容\n     \u003c/div\u003e\n  \u003c/app-my-component\u003e\n\u003c/div\u003e\n```\n\n### 多槽内容投影\n\n\u003e 与AngularJS中使用`transclude: {}`属性识别DOM引用不同，Angular2+使用更加直接的方法与DOM节点沟通\n\n```typescript\n// \nimport { Component } from '@angular/core';\n@Component({\n  selector: 'app-root',\n  template: `\n    \u003cdiv class=\"app\"\u003e\n      \u003capp-my-component\u003e\n        \u003cmy-component-title\u003e\n          组件标题\n        \u003c/my-component-title\u003e\n        \u003cmy-component-content\u003e\n          组件内容\n        \u003c/my-component-content\u003e\n      \u003c/app-my-component\u003e\n    \u003c/div\u003e\n  `\n})\n```\n\n\u003e 这里假设存在my-component-title和my-component-content作为自定义组件\n\u003e 用以获取对组件的引用，并告诉Angular在适当的位置注入\n\u003e 在引用时需要明确`\u003cng-content\u003e`标签的`select=''`属性\n\n```typescript\n// my-component.component.ts\nimport { Component } from '@angular/core';\n@Component({\n  selector: 'app-my-component',\n  template: `\n    \u003cdiv class=\"my-component\"\u003e\n      \u003cdiv\u003e\n        Title:\n        \u003cng-content select=\"my-component-title\"\u003e\u003c/ng-content\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        Content:\n        \u003cng-content select=\"my-component-content\"\u003e\u003c/ng-content\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  `\n})\n```\n\n\u003e 事实上，在声明要投影的内容时不必像上述那样使用自定义元素\n\u003e 可以使用常规元素并以类似于`document.querySelector`的对话方式来定位投影元素\n\n```typescript\n// app.component.ts\nimport { Component } from '@angular/core';\n@Component({\n  selector: 'app-root',\n  template: `\n   \u003cdiv class=\"app\"\u003e\n     \u003capp-my-component2\u003e\n       \u003cdiv class=\"my-component-title\"\u003e\n         组件标题\n       \u003c/div\u003e\n       \u003cdiv class=\"my-component-content\"\u003e\n         组件内容\n       \u003c/div\u003e\n     \u003c/app-my-component2\u003e\n   \u003c/div\u003e\n  `\n})\n```\n\n```typescript\n// my-component.component2.ts\nimport { Component } from '@angular/core';\n@Component({\n  selector: 'app-root',\n  template: `\n   \u003cdiv class=\"my-component\"\u003e\n     \u003cdiv\u003e\n       Title:\n       \u003cng-content select=\".my-component-title\"\u003e\u003c/ng-content\u003e\n     \u003c/div\u003e\n     \u003cdiv\u003e\n       Content:\n       \u003cng-content select=\".my-component-content\"\u003e\u003c/ng-content\u003e\n     \u003c/div\u003e\n   \u003c/div\u003e\n  `\n})\n```\n\n\u003e 生成的DOM树为\n\n```html\n\u003cdiv _ngcontent-c0=\"\" class=\"app\"\u003e\n  \u003capp-my-component2 _ngcontent-c0=\"\" _nghost-c2=\"\"\u003e\n    \u003cdiv _ngcontent-c2=\"\" class=\"my-component\"\u003e\n      \u003cdiv _ngcontent-c2=\"\"\u003e \n        Title: \n          \u003cdiv _ngcontent-c0=\"\" class=\"my-component-title\"\u003e \n             组件标题 \n          \u003c/div\u003e\n       \u003c/div\u003e\n       \u003cdiv _ngcontent-c2=\"\"\u003e \n         Content: \n           \u003cdiv _ngcontent-c0=\"\" class=\"my-component-content\"\u003e\n              组件内容 \n           \u003c/div\u003e\n       \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/app-my-component2\u003e\n\u003c/div\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsawyerbutton%2Fangular-content-projection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsawyerbutton%2Fangular-content-projection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsawyerbutton%2Fangular-content-projection/lists"}