Angular CDK:将叠加层附加到单击的元素
Angular CDK : attach overlay to a clicked element
我正在尝试为 table 单元格制作自定义弹出窗口,以便在单击它时显示单元格详细信息,其方式类似于 mdBoostrap popovers。
目前,我有以下应用程序:https://stackblitz.com/edit/angular-m5rx6j
Popup 组件显示在主要组件下方,但我想将其显示在 table 上方,就在我单击的元素下方。
我想我需要做以下事情:
- 获取我点击的 'td' 的 ElementRef -> 我不知道如何
- 将叠加层附加到此元素 -> 已经这样做了,但是使用根元素
要获取元素的引用,您可以使用模板标识符:#this_element
。
您可以直接使用组件模板中的值,或者从@ViewChild
/ @ViewChildren
.
中获取一个Typescript变量
例如在您的代码中:
<td #this_element>
<div (click)="open(this_element)">Overlay Host1</div>
</td>
并且您的 open
函数现在将读取 open(element: any)
,其中 element
的类型取决于您放置 #this_element
.
的位置
你也可以通过
在你的组件代码中获取这个元素
@ViewChild('this_element', { static: true }) element;
如果您需要访问 nativeElement
,Angular docs 中会有一些警告,所以如果您 运行 在网络工作者中,请当心。在这种情况下,您可能想使用 Renderer2
。
Netanet Basal 的博客中有两篇关于使用 CDK 的 OverLay 的精彩文章
我尝试在 this stackblitz
中简单化
基本上你有一个注入覆盖的服务
constructor(private overlay: Overlay) { }
要打开一个模板,你需要传入原点(我叫他 "origin")、模板(我叫菜单)和你的组件的 viewContainerRef
this.overlayRef = this.overlay.create(
this.getOverlayConfig({ origin: origin})
);
//I can pass "data" as implicit and "close" to close the menu
this.overlayRef.attach(new TemplatePortal(menu, viewContainerRef, {
$implicit: data, close:this.close
}));
getOverLayConfig return 类似的配置
private getOverlayConfig({ origin}): OverlayConfig {
return new OverlayConfig({
hasBackdrop: false,
backdropClass: "popover-backdrop",
positionStrategy: this.getOverlayPosition(origin),
scrollStrategy: this.overlay.scrollStrategies.close()
});
}
位置策略是您要附加模板的位置 - 一个包含您的首选位置的数组,例如
[
{
originX: "center",
originY: "bottom",
overlayX: "center",
overlayY: "top"
},
]
好吧,代码的另一部分是关于关闭模板元素的。我选择在服务中创建一个函数打开那个
1.-附加元素
2.-创建
的订阅
this.sub = fromEvent<MouseEvent>(document, "click")
3.-return 一个 return null 的可观察对象或您在函数中传递的参数 "close"(*)
注意:不要忘记包含在您的 css ~@angular/cdk/overlay-prebuilt.css
中
(*) 这让我的模板像
<ng-template #tpl let-close="close" let-data>
<div class="popover" >
<h5>{{name}} {{data.data}}</h5> //<--name is a variable of component
//data.data a variable you can pass
And here's some amazing content. It's very engaging. Right?
<div>
<a (click)="close('uno')">Close</a> //<--this close and return 'uno'
</div>
</div>
</ng-template>
更新 如果我们想先附加一个组件,我们需要记住它必须在模块的 entryComponents 中
@NgModule({
imports: [ BrowserModule, FormsModule,OverlayModule ],
declarations: [ AppComponent,HelloComponent], //<--HERE
bootstrap: [ AppComponent ],
entryComponents:[HelloComponent] //<--and HERE
})
嗯,附加组件很简单,更改附加并使用 ComponentPortal,例如
const ref=this.overlayRef.attach(new ComponentPortal(HelloComponent,viewContainerRef))
然后,如果我们的组件有一些输入,例如
@Input() name="Angular";
@Input() obj={count:0};
我们可以使用ref.instance访问组件,例如
ref.instance.name="New Name"
但是因为我们要维护服务最一般的用途,所以我想用参数"data"给变量赋值,所以我们的函数"open"变成了
open(origin: any, component: any, viewContainerRef: ViewContainerRef, data: any) {
this.close(null);
this.overlayRef = this.overlay.create(
this.getOverlayConfig({ origin: origin})
);
const ref=this.overlayRef.attach(new ComponentPortal(component, viewContainerRef));
for (let key in data) //here pass all the data to our component
{
ref.instance[key]=data[key]
}
...rest of code...
}
一如既往,如果我们传递一个对象,组件中的所有变化都会改变对象的属性,所以在我们的主要组件中可以做一些像
obj={count:2}
open(origin:any,menu:any,index:number)
{
this.popupService.open(origin,HelloComponent,this.viewContainerRef,
{name:'new Name'+index,obj:this.obj})
.subscribe(res=>{
console.log(res)
})
}
请注意,当我将对象作为对象传递时,组件中的任何更改都会更改对象的属性,在我的例子中,组件非常简单
@Component({
selector: 'component',
template:`Hello {{name}}
<button (click)="obj.count=obj.count+1">click</button>
`
})
export class HelloComponent {
@Input() name="Angular";
@Input() obj={count:0};
}
你可以在new stackblitz
中看到
Update2 要从 HelloComponent 关闭面板,我们需要将服务注入 public 并使用关闭。或多或少,一个按钮
<button (click)="popupService.close(4)">close</button>
注入服务的位置
constructor(public popupService: MenuContextualService){}
我正在尝试为 table 单元格制作自定义弹出窗口,以便在单击它时显示单元格详细信息,其方式类似于 mdBoostrap popovers。
目前,我有以下应用程序:https://stackblitz.com/edit/angular-m5rx6j
Popup 组件显示在主要组件下方,但我想将其显示在 table 上方,就在我单击的元素下方。
我想我需要做以下事情:
- 获取我点击的 'td' 的 ElementRef -> 我不知道如何
- 将叠加层附加到此元素 -> 已经这样做了,但是使用根元素
要获取元素的引用,您可以使用模板标识符:#this_element
。
您可以直接使用组件模板中的值,或者从@ViewChild
/ @ViewChildren
.
例如在您的代码中:
<td #this_element>
<div (click)="open(this_element)">Overlay Host1</div>
</td>
并且您的 open
函数现在将读取 open(element: any)
,其中 element
的类型取决于您放置 #this_element
.
你也可以通过
在你的组件代码中获取这个元素@ViewChild('this_element', { static: true }) element;
如果您需要访问 nativeElement
,Angular docs 中会有一些警告,所以如果您 运行 在网络工作者中,请当心。在这种情况下,您可能想使用 Renderer2
。
Netanet Basal 的博客中有两篇关于使用 CDK 的 OverLay 的精彩文章
我尝试在 this stackblitz
中简单化基本上你有一个注入覆盖的服务
constructor(private overlay: Overlay) { }
要打开一个模板,你需要传入原点(我叫他 "origin")、模板(我叫菜单)和你的组件的 viewContainerRef
this.overlayRef = this.overlay.create(
this.getOverlayConfig({ origin: origin})
);
//I can pass "data" as implicit and "close" to close the menu
this.overlayRef.attach(new TemplatePortal(menu, viewContainerRef, {
$implicit: data, close:this.close
}));
getOverLayConfig return 类似的配置
private getOverlayConfig({ origin}): OverlayConfig {
return new OverlayConfig({
hasBackdrop: false,
backdropClass: "popover-backdrop",
positionStrategy: this.getOverlayPosition(origin),
scrollStrategy: this.overlay.scrollStrategies.close()
});
}
位置策略是您要附加模板的位置 - 一个包含您的首选位置的数组,例如
[
{
originX: "center",
originY: "bottom",
overlayX: "center",
overlayY: "top"
},
]
好吧,代码的另一部分是关于关闭模板元素的。我选择在服务中创建一个函数打开那个
1.-附加元素
2.-创建
的订阅this.sub = fromEvent<MouseEvent>(document, "click")
3.-return 一个 return null 的可观察对象或您在函数中传递的参数 "close"(*)
注意:不要忘记包含在您的 css ~@angular/cdk/overlay-prebuilt.css
(*) 这让我的模板像
<ng-template #tpl let-close="close" let-data>
<div class="popover" >
<h5>{{name}} {{data.data}}</h5> //<--name is a variable of component
//data.data a variable you can pass
And here's some amazing content. It's very engaging. Right?
<div>
<a (click)="close('uno')">Close</a> //<--this close and return 'uno'
</div>
</div>
</ng-template>
更新 如果我们想先附加一个组件,我们需要记住它必须在模块的 entryComponents 中
@NgModule({
imports: [ BrowserModule, FormsModule,OverlayModule ],
declarations: [ AppComponent,HelloComponent], //<--HERE
bootstrap: [ AppComponent ],
entryComponents:[HelloComponent] //<--and HERE
})
嗯,附加组件很简单,更改附加并使用 ComponentPortal,例如
const ref=this.overlayRef.attach(new ComponentPortal(HelloComponent,viewContainerRef))
然后,如果我们的组件有一些输入,例如
@Input() name="Angular";
@Input() obj={count:0};
我们可以使用ref.instance访问组件,例如
ref.instance.name="New Name"
但是因为我们要维护服务最一般的用途,所以我想用参数"data"给变量赋值,所以我们的函数"open"变成了
open(origin: any, component: any, viewContainerRef: ViewContainerRef, data: any) {
this.close(null);
this.overlayRef = this.overlay.create(
this.getOverlayConfig({ origin: origin})
);
const ref=this.overlayRef.attach(new ComponentPortal(component, viewContainerRef));
for (let key in data) //here pass all the data to our component
{
ref.instance[key]=data[key]
}
...rest of code...
}
一如既往,如果我们传递一个对象,组件中的所有变化都会改变对象的属性,所以在我们的主要组件中可以做一些像
obj={count:2}
open(origin:any,menu:any,index:number)
{
this.popupService.open(origin,HelloComponent,this.viewContainerRef,
{name:'new Name'+index,obj:this.obj})
.subscribe(res=>{
console.log(res)
})
}
请注意,当我将对象作为对象传递时,组件中的任何更改都会更改对象的属性,在我的例子中,组件非常简单
@Component({
selector: 'component',
template:`Hello {{name}}
<button (click)="obj.count=obj.count+1">click</button>
`
})
export class HelloComponent {
@Input() name="Angular";
@Input() obj={count:0};
}
你可以在new stackblitz
中看到Update2 要从 HelloComponent 关闭面板,我们需要将服务注入 public 并使用关闭。或多或少,一个按钮
<button (click)="popupService.close(4)">close</button>
注入服务的位置
constructor(public popupService: MenuContextualService){}