带有用户单击所选组件的动态选项卡

2023-12-08

我正在尝试设置一个选项卡系统,允许组件自行注册(带有标题)。第一个选项卡就像一个收件箱,有很多操作/链接项可供用户选择,并且每次单击都应该能够在单击时实例化一个新组件。操作/链接来自 JSON。

然后,实例化的组件会将其自身注册为新选项卡。

我不确定这是否是“最好”的方法?到目前为止,我见过的唯一指南是针对静态选项卡的,这没有帮助。

到目前为止,我只获得了在 main 中引导的选项卡服务,以便在整个应用程序中持续存在。它看起来像这样:

export interface ITab { title: string; }

@Injectable()
export class TabsService {
    private tabs = new Set<ITab>();

    addTab(title: string): ITab {
        let tab: ITab = { title };
        this.tabs.add(tab);
        return tab;
    }

    removeTab(tab: ITab) {
        this.tabs.delete(tab);
    }
}

问题:

  1. 如何在收件箱中创建一个动态列表来创建新的(不同的)选项卡?我有点猜测DynamicComponentBuilder会被使用吗?
  2. 如何从收件箱(单击)创建组件,将其自身注册为选项卡并显示?我正在猜测ng-content,但我找不到太多关于如何使用它的信息

EDIT:试图澄清。

将收件箱视为邮件收件箱。项目以 JSON 形式获取,并显示多个项目。单击其中一项后,将使用该项操作“类型”创建一个新选项卡。那么该类型就是一个组件。

EDIT 2: Image.


update

Angular 5 StackBlitz 示例

update

ngComponentOutlet已添加到 4.0.0-beta.3

update

有一个NgComponentOutlet正在进行类似操作的工作https://github.com/angular/angular/pull/11235

RC.7

Plunker 示例 RC.7

// Helper component to add dynamic components
@Component({
  selector: 'dcl-wrapper',
  template: `<div #target></div>`
})
export class DclWrapper {
  @ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef;
  @Input() type: Type<Component>;
  cmpRef: ComponentRef<Component>;
  private isViewInitialized:boolean = false;

  constructor(private componentFactoryResolver: ComponentFactoryResolver, private compiler: Compiler) {}

  updateComponent() {
    if(!this.isViewInitialized) {
      return;
    }
    if(this.cmpRef) {
      // when the `type` input changes we destroy a previously 
      // created component before creating the new one
      this.cmpRef.destroy();
    }

    let factory = this.componentFactoryResolver.resolveComponentFactory(this.type);
    this.cmpRef = this.target.createComponent(factory)
    // to access the created instance use
    // this.compRef.instance.someProperty = 'someValue';
    // this.compRef.instance.someOutput.subscribe(val => doSomething());
  }

  ngOnChanges() {
    this.updateComponent();
  }

  ngAfterViewInit() {
    this.isViewInitialized = true;
    this.updateComponent();  
  }

  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }    
  }
}

使用示例

// Use dcl-wrapper component
@Component({
  selector: 'my-tabs',
  template: `
  <h2>Tabs</h2>
  <div *ngFor="let tab of tabs">
    <dcl-wrapper [type]="tab"></dcl-wrapper>
  </div>
`
})
export class Tabs {
  @Input() tabs;
}
@Component({
  selector: 'my-app',
  template: `
  <h2>Hello {{name}}</h2>
  <my-tabs [tabs]="types"></my-tabs>
`
})
export class App {
  // The list of components to create tabs from
  types = [C3, C1, C2, C3, C3, C1, C1];
}
@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, DclWrapper, Tabs, C1, C2, C3],
  entryComponents: [C1, C2, C3],
  bootstrap: [ App ]
})
export class AppModule {}

也可以看看angular.io 动态组件加载器

旧版本 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这在 Angular2 RC.5 中再次发生了变化

我将更新下面的示例,但这是假期前的最后一天。

This 笨蛋的例子演示如何在 RC.5 中动态创建组件

更新-使用视图容器引用.createComponent()

Because DynamicComponentLoader已弃用,该方法需要再次更新。

@Component({
  selector: 'dcl-wrapper',
  template: `<div #target></div>`
})
export class DclWrapper {
  @ViewChild('target', {read: ViewContainerRef}) target;
  @Input() type;
  cmpRef:ComponentRef;
  private isViewInitialized:boolean = false;

  constructor(private resolver: ComponentResolver) {}

  updateComponent() {
    if(!this.isViewInitialized) {
      return;
    }
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }
   this.resolver.resolveComponent(this.type).then((factory:ComponentFactory<any>) => {
      this.cmpRef = this.target.createComponent(factory)
      // to access the created instance use
      // this.compRef.instance.someProperty = 'someValue';
      // this.compRef.instance.someOutput.subscribe(val => doSomething());
    });
  }

  ngOnChanges() {
    this.updateComponent();
  }

  ngAfterViewInit() {
    this.isViewInitialized = true;
    this.updateComponent();  
  }

  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }    
  }
}

Plunker 示例 RC.4
Plunker 示例 beta.17

更新 - 使用 loadNextToLocation

export class DclWrapper {
  @ViewChild('target', {read: ViewContainerRef}) target;
  @Input() type;
  cmpRef:ComponentRef;
  private isViewInitialized:boolean = false;

  constructor(private dcl:DynamicComponentLoader) {}

  updateComponent() {
    // should be executed every time `type` changes but not before `ngAfterViewInit()` was called 
    // to have `target` initialized
    if(!this.isViewInitialized) {
      return;
    }
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }
    this.dcl.loadNextToLocation(this.type, this.target).then((cmpRef) => {
      this.cmpRef = cmpRef;
    });
  }

  ngOnChanges() {
    this.updateComponent();
  }

  ngAfterViewInit() {
    this.isViewInitialized = true;
    this.updateComponent();  
  }

  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }    
  }
}

Plunker 示例 beta.17

original

从您的问题中不能完全确定您的要求是什么,但我认为这应该满足您的要求。

The Tabs组件获取传递的类型数组,并为数组中的每个项目创建“选项卡”。

@Component({
  selector: 'dcl-wrapper',
  template: `<div #target></div>`
})
export class DclWrapper {
  constructor(private elRef:ElementRef, private dcl:DynamicComponentLoader) {}
  @Input() type;

  ngOnChanges() {
    if(this.cmpRef) {
      this.cmpRef.dispose();
    }
    this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
      this.cmpRef = cmpRef;
    });
  }
}

@Component({
  selector: 'c1',
  template: `<h2>c1</h2>`

})
export class C1 {
}

@Component({
  selector: 'c2',
  template: `<h2>c2</h2>`

})
export class C2 {
}

@Component({
  selector: 'c3',
  template: `<h2>c3</h2>`

})
export class C3 {
}

@Component({
  selector: 'my-tabs',
  directives: [DclWrapper],
  template: `
  <h2>Tabs</h2>
  <div *ngFor="let tab of tabs">
    <dcl-wrapper [type]="tab"></dcl-wrapper>
  </div>
`
})
export class Tabs {
  @Input() tabs;
}


@Component({
  selector: 'my-app',
  directives: [Tabs]
  template: `
  <h2>Hello {{name}}</h2>
  <my-tabs [tabs]="types"></my-tabs>
`
})
export class App {
  types = [C3, C1, C2, C3, C3, C1, C1];
}

Plunker 示例 beta.15(不是基于你的 Plunker)

还有一种传递数据的方法,可以将其传递给动态创建的组件,例如(someData需要像这样传递type)

    this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
  cmpRef.instance.someProperty = someData;
  this.cmpRef = cmpRef;
});

还有一些对共享服务使用依赖注入的支持。

欲了解更多详情,请参阅https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带有用户单击所选组件的动态选项卡 的相关文章

  • Angular2 - 添加新的
  • 项目 onClick 事件
  • 我使用 ngFor 迭代数组以便在列表中显示它们 但我无法在列表中添加新项目 在 Onclick 事件期间我得到一个空的 li 也许我没有链接正确的东西 指令 或者是什么 也许我使用了错误的变量 我的导出类有我的构造函数 export cl
  • 将 Angular 4 添加到 ASP.NET Core 2.0 Web 应用程序 (MVC) [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 现在 ASP NET Core 2 已发布 我已经开始在 Visual Studio 2017 中使用 ASP NET CORE 构建
  • 滑动浏览段选项卡 - Ionic 3

    下面的代码使用 ionic 3 中的片段 文档显示了 ngSwitch ngModel 的使用 但我想简单地在该段上滑动并切换到另一个段选项卡 我怎样才能实现这个目标 我对滑动顶部的选项卡不感兴趣 但通过滑动内容我想更改分段选项卡
  • 解析器中的链相关可观察量

    我有一个解析器 需要在加载页面之前从两个依赖的 API 获取数据 第二个调用是由第一个调用的结果定义的 因此我尝试链接两个可观察量 并且需要在解析器末尾返回第二个可观察量 在我尝试链接可观察量之前 我有 resolve route Acti
  • 注入需要构造函数参数的服务

    我有一项服务需要启动一些值 Injectable export class MyService private myVals any constructor init any this myVals init 而消费者 Component
  • Ionic 4 用户登录后无法立即显示注销按钮

    我正在我的 Ionic 4 应用程序中工作 并且已经制作了登录 注册系统 当用户登录后 用户将能够访问该页面 当用户未登录并尝试访问该页面时 它将被重定向到登录页面 这是我的用户登录 page ts async UserLoginDetai
  • Angular 2 在没有 @Input 或 @Output 的情况下监视组件属性的更改

    在 Angular 1 x 中 我可以使用 watch 来观看我想要的任何内容 但是在 Angular 2 中 我们有 ngOnChanges 它非常酷且高性能 但只能监视输入和输出装饰器 然而 有时我确实需要观察本地属性 以便在它们发生变
  • 如何在菜单中显示用户名

    我有一个 user name 我的 app component ts 中的变量 我想用它来显示用户登录后从本地存储获取的用户名 但这仅在应用程序启动时加载 我想在用户登录后重新加载它 当我在登录后关闭并重新打开应用程序时 它工作正常并显示用
  • 在 pre 标签内电子加载 html

    我最近创建了一个电子 角度应用程序 它在内部运行以下内容main ts win loadURL url format pathname path join dirname dist index html protocol file slas
  • 手动刷新 Angular 之前页面无法正常显示

    我面临着非常奇怪的问题 我的角度应用程序页面显示正确 它似乎没有完全加载 当我手动重新加载 刷新页面时 它的加载正常 这是完整的场景 我创建了一个登录页面 该页面显示完美并按预期工作 登录后 我导航到仪表板页面 该页面未正确显示 似乎加载了
  • Chartjs + Angular6 未显示图表或任何错误

    我正在尝试以角度实现chart js 编写了一个简单的代码来在html上显示图表 但是页面上没有输出 也没有错误 我不明白问题出在哪里以及为什么显示图表失败 堆栈闪电战 https stackblitz com edit angularch
  • Angular - 使用 routerlink 将对象传递给 @Input 参数

    我有一个带有 Input 参数的角度分量 如下所示 Component selector app transmission history export class TransmissionHistoryComponent implemen
  • 具有动态名称的 Angular Material 2 日期选择器

    我正在尝试实现具有动态名称的日期选择器组件 我正在使用 Angular 4 开发基于 Angular Material 2 的项目 这是我的实现
  • 传单 - 导入 Geojson - Angular 6

    我尝试将 GeoJson 文件导入到 Angular 的应用程序 6 中的传单中 通过这个解决方案 我的 geojson 是在 leafletmap 中绘制的 但我有这个错误 我无法构建我的应用程序 有人知道一种解决方案吗 错误 TS234
  • html输入数字,min + step,使step忽略min?

    是否有可能使step忽略min属性
  • router.navigate 使用查询参数 Angular 5

    我在使用查询参数路由到路由时遇到问题我有一个像这样的函数 goToLink link this router navigate link split 0 queryParams this sortParams link 和这个功能 sort
  • Angular 反应式表单:使用单个 formControlName 同步多个输入

    我正在建造一个反应形式 https angular io guide reactive forms在 Angular 11 中 它分为多个 div 项目所有者希望在每个 div 中进行一些重复输入 以便用户可以编辑某个字段 A 的输入 并且
  • ionic 2 google 图表未捕获类型错误:(void 0) 不是函数

    error 我想添加谷歌图表并按照这个https www npmjs com package angular2 google chart https www npmjs com package angular2 google chart 但
  • 如何安装旧版本的 Angular?

    我需要安装 Angular 2 但是当我运行时 ng new myApp版本是4 我搜索了很多 但找不到只安装版本2的方法 有谁知道该怎么做 Edit the command ng new MYAPP ng4 false It does n
  • 在 Angular 中导入和使用 lodash 的正确方法

    我曾经能够通过如下所示的 import 语句在 Angular 中使用 lodash 方法 import debounce as debounce from lodash 我现在在使用该语句时收到以下错误 node modules type

随机推荐

  • Microsoft Graph API:尝试检索租户策略时出现 403 Forbidden 错误

    我正在尝试使用 Microsoft Graph API 检索在 Azure AD 门户上为我的租户创建的策略 据我从图形 API 文档中了解到 所有策略 CRUD 操作都需要一个范围目录 AccessAsUser All 此范围转换为权限以
  • log4j 选择错误的属性文件

    我的类路径中有 2 个 log4j properties 文件 我需要它们 其中一个是我正在使用的库所必需的 另一个是我的代码使用的库 当我运行我的 jar 文件时 它能够读取库使用的属性 但它不会读取我自己的属性文件 如何让它读取我的 l
  • 找不到与给定名称匹配的资源(在“paddingBottom”处,值为“@dimen/activity_vertical_margin”)

    我试图构建一个导航抽屉 我从某个网站复制了代码 但在其中一个 XML 中收到此错误 找不到与给定名称匹配的资源 在 paddingBottom 处 值为 dimen 活动垂直边距 如何摆脱这个错误
  • 带有 .js 扩展名的 Webpack/ts-loader 导入无法解析

    我的目录结构如下 projectRoot project server src pom xml project ui tsconfig json src file ts imports file js 我的问题是project server
  • 错误:ORA-00907:缺少右括号 - 您能帮助解决问题吗

    select regexp substr replace replace replace CA CO IL KS chr 40 chr 41 chr 39 1 level as division from dual connect by l
  • 使用 Node.js 自动将文本写入控制台

    我需要使用 SSH 和 Node js 脚本克隆 GitHub 存储库 var exec require child process exec exec git clone email protected jquery jquery git
  • 如何仅对具有特定属性集的元素使用 querySelectorAll?

    我正在尝试使用document querySelectorAll对于所有具有value属性集 页面上还有其他复选框没有value设置 并且每个复选框的值都不同 但 ID 和名称并不唯一 例子
  • HtmlUnit 按钮单击

    我正在尝试在 www meetme com 上发送消息 但不知道该怎么做 我可以在评论区域中输入消息 但单击 发送 按钮不会执行任何操作 我究竟做错了什么 当我登录并按登录按钮时 页面确实发生了变化 一切都很好 有人有任何想法或线索吗 Ht
  • 什么是对象/关系不匹配

    我是java新手 正在阅读有关对象关系映射的内容 我在此链接上发现了术语 对象 关系不匹配 休眠 谁能用 Java 解释一下什么是对象 关系不匹配 我也读到过黑客攻击网站但无法正确获得它 用示例进行解释将是值得赞赏的 Hibernate 是
  • Heroku 应用程序坚持使用 HTTPS - 为什么?

    我有一个有趣的小问题 Heroku 上的一个应用程序被配置为使用Heroku SSL 测试版 但无论我做什么 它似乎都想使用 HTTPS 浏览器错误 I have redirect to protocol gt http status gt
  • 如何使用 oozie 安排 sqoop 操作

    我是 Oozie 的新手 只是想知道 如何使用 Oozie 安排 sqoop 作业 我知道 sqoop 操作可以添加为 Oozie 工作流程的一部分 但是我如何安排 sqoop 操作并让它每隔 2 分钟或每天晚上 8 点自动运行一次 只是一
  • JDBC-JTDS 错误?对于日期和时间 (x) 类型的列

    当我尝试从中获取列类型时ResultSetMetaData用方法getColumnTypeName对于类型date and time x 我越来越nvarchar 对于其他类型似乎效果很好 这是一个错误吗 和ResultSet getStr
  • 为什么Golang创建切片时会有CAPACITY参数

    这是一个非常简单的问题 If the capacityGolang中的一个切片的容量是可以被超出的 为什么首先要有一个容量参数呢 我认为这与内存管理 某种 知道在内存中分配切片的位置 但我不确切知道 If the capacityGolan
  • MSChart 轴线

    如何显示图表中每个条形的轴线 我只有第二个 第四个 替代文本 http img35 imageshack us img35 6106 chartiu png 我找到了答案 chartArea AxisX MajorGrid Interval
  • 控制 ggparcoord 中的颜色(来自 GGally 包)

    我正在尝试对特定 ggparcoord 图硬编码所需的线条颜色 例如 当我创建下面的 ggparcoord 图时 library GGally x data frame a runif 100 0 1 b runif 100 0 1 c r
  • Bash - 查找匹配的文件对[重复]

    这个问题在这里已经有答案了 我的文件夹中有很多文件 Filename1 mp4 Filename2 mp4 Filename3 mp4 Etc 以及许多名称添加后缀的文件 Filename1 x264 mp4 Filename2 x264
  • 脚本无法正确接收 url

    我正在使用组合的批处理和java脚本 我发现使用批处理文件从网站检索html 而我们解决的一个问题是没有返回所需的输出 就像我在firefox中使用url时出现的那样 我用来拉取 html 的脚本是 if This IsBatch then
  • 我应该如何循环遍历依赖于前一个循环值的异步函数?

    我试图在 node js 中执行的操作的同步版本 为了可读性而简化 var value null var allValues do value getValue value load the next value if value allV
  • iframe 中的在新窗口中打开链接

    我的页面上有一个 iframe 并且有一个链接 在同一域上 我想在新的物理窗口中打开 当我使用 target blank 时 它只是使用新的 iframe 重新加载页面 我还尝试了这个 JavaScript jQuery 代码 docume
  • 带有用户单击所选组件的动态选项卡

    我正在尝试设置一个选项卡系统 允许组件自行注册 带有标题 第一个选项卡就像一个收件箱 有很多操作 链接项可供用户选择 并且每次单击都应该能够在单击时实例化一个新组件 操作 链接来自 JSON 然后 实例化的组件会将其自身注册为新选项卡 我不