All iframes in widgets refresh when any widget gains focus (using ng-dynamic-component)
See original GitHub issueContext
I have defined various widget type components, and I’m using ng-dynamic-component so that each widget is dynamically associated with the component at runtime. One of these components is a youtube iframe widget component, i.e. all widgets that embed youtube videos will use this component. For now, the HTML of this component is simply
<iframe width={{iframeWidth}} height={{iframeHeight}} [src]="getUrl()" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
Issue
The issue I’m having is that whenever any widget is dragged, resized, or even clicked (so simply when a widget gains focus), all of the iframe widgets refresh their iframe. You can see an example here. Note that this does not happen when I click anywhere else, such as on the background grid. I imagine all widget content is being refreshed, not just the iframes, but since iframes take longer to load it’s only noticeable for them.
I have a feeling it’s something related to ng-dynamic-component and the Angular lifecycle, but I’m new to Angular so it might be something simple I’m not setting up correctly. I’ll share the relevant pieces of code in case that might help.
Relevant Code
I use a similar dashboard array as the Home example in dashboard-component.ts:
this.dashboard = [
{cols: 2, rows: 1, y: 0, x: 0, widgetType: 'iframe', widgetData: {src: "https://www.youtube.com/embed/H-WEhug-up8", iframeWidth: 240, iframeHeight: 160}},
{cols: 2, rows: 2, y: 0, x: 2, widgetType: 'gettingStarted'},
{cols: 1, rows: 1, y: 0, x: 4, widgetType: 'numericQuery'},
{cols: 1, rows: 1, y: 2, x: 5, widgetType: 'favorites'},
{cols: 1, rows: 1, y: 1, x: 0, widgetType: 'numericQuery'},
{cols: 1, rows: 1, y: 1, x: 0, widgetType: 'iframe', widgetData: {src: "https://www.youtube.com/embed/IDaqFiLvcB0", iframeWidth: 640, iframeHeight: 360}},
{cols: 2, rows: 2, y: 3, x: 5, widgetType: 'gettingStarted'},
{cols: 2, rows: 2, y: 2, x: 0, widgetType: 'numericQuery'},
{cols: 2, rows: 1, y: 2, x: 2, widgetType: 'favorites'},
{cols: 1, rows: 1, y: 2, x: 4, widgetType: 'numericQuery'},
{cols: 1, rows: 1, y: 2, x: 6, widgetType: 'iframe', widgetData: {src: "https://www.youtube.com/embed/PYOSKYWg-5E", iframeWidth: 340, iframeHeight: 260}}
];
I made a WidgetItemComponent which acts as the ng-dynamic-component facilitator for choosing the correct dynamic component:
@Component({
selector: 'app-widget-item',
template: `<ndc-dynamic [ndcDynamicComponent]="component" [ndcDynamicInputs]="inputs"></ndc-dynamic>`,
styleUrls: ['./widget-item.component.css']
})
export class WidgetItemComponent implements OnInit {
component : any;
inputs = { widgetData: {} };
constructor() { }
ngOnInit() {
this.component = this.getComponent();
if (this.dashboardItem != null) {
this.inputs.widgetData = this.dashboardItem.widgetData;
}
}
@Input() dashboardItem: GridsterItem;
getComponent() {
if (this.dashboardItem != null) {
if (this.dashboardItem.widgetType == 'iframe') {
return IframeWidgetComponent;
}
else if (this.dashboardItem.widgetType == 'numericQuery') {
return NumberQueryWidgetComponent;
}
else if (this.dashboardItem.widgetType == 'favorites') {
return FavoritesWidgetComponent;
}
else if (this.dashboardItem.widgetType == 'gettingStarted') {
return GettingStartedWidgetComponent;
}
}
}
}
And finally in iframe-widget-component.ts I have:
@Component({
selector: 'app-iframe-widget',
templateUrl: './iframe-widget.component.html',
styleUrls: ['./iframe-widget.component.css']
})
export class IframeWidgetComponent implements OnInit {
src: string;
iframeWidth: number;
iframeHeight: number;
@Input() widgetData: any;
constructor(public sanitizer: DomSanitizer) { }
ngOnInit() {
if (this.widgetData != null) {
this.src = this.widgetData.src;
this.iframeWidth = this.widgetData.iframeWidth;
this.iframeHeight = this.widgetData.iframeHeight;
}
}
getUrl() {
return this.sanitizer.bypassSecurityTrustResourceUrl(this.src);
}
}
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:8 (1 by maintainers)

Top Related StackOverflow Question
Hello guys! A little late to the party, but here’s what I think it’s happening. Gridster components are all made with the
changeDetectionStrategyset toOnPushif I remember correctly. Which means that they trigger a change detection when their input changes or when an external event affects them (such as a click or a resize). Which means that on every click/resize a change detection will be triggered in the component.Now, you are trying to get the url by binding it with the
getUrl()method. The problem here is that Angular doesn’t know what the result of a function changes, therefore, when a change detection is triggered, the function is executed again to get the result. To avoid this kind of behavior, you will have to execute the function at an earlier point (such as in the constructor orngOnInithook) and save the result in a property on the class.Now, even if the change detection is triggered, Angular knows that the url hasn’t changed (since it’s an object with a proper reference) and it won’t try to rerender the component to which the url is binded again.
I hope this helps. ^.^
I think you’ve stumbled upon a very interesting bug. I’ve taken a look at it and I think that this might actually be related to angular gridster2 as I can’t reproduce any of this behavior without it. I was able to strip out most of your code and still reproduce the issue here: https://stackblitz.com/edit/angular-dktb63.
I removed all the dynamic component related code as well as all styling, all of your gridster options, and all the logic in the iframe component except the getUrl() function. You can still see the bug by checking the console after/during a screen resize.
I also tested in both Chrome and Edge just to check if it was browser specific, and it seems it isn’t. Interestingly, simply having a gridster element in the dom seems to trigger the bug, refreshing elements that are not even inside the grid. This merits more looking into.