Closed adaneam closed 3 months ago
I'm React rather than Angular 2. If you get it working please let me know and I'll add something to the docs. I suggest asking StackOverflow.
I need the same. Please help us.
Maybe this will help you. First declare types in "typings.d.ts". Here is an example, please notice that I only added methods that I needed.
interface IFrameObject {
close();
resize();
}
interface IFrameResizerComponent {
iFrameResizer: IFrameObject
}
declare const resizer: {
iframeResizer(options: any, target: HTMLElement);
};
/* Modules */
declare module 'iframe-resizer' {
export = resizer;
}
After that, you should create directive. Something like this.
import {AfterViewInit, Directive, ElementRef, OnDestroy} from '@angular/core';
import * as resizer from 'iframe-resizer';
@Directive({
selector: 'iframe.thread-content'
})
export class IFrameResizerDirective implements AfterViewInit, OnDestroy {
component: IFrameResizerComponent;
constructor(public element: ElementRef) {
}
ngAfterViewInit() {
this.component = resizer.iframeResizer({
checkOrigin: false,
heightCalculationMethod: 'documentElementOffset',
log: false
}, this.element.nativeElement);
}
ngOnDestroy(): void {
if (this.component && this.component.iFrameResizer) {
this.component.iFrameResizer.close();
}
}
}
Pay attention to tag and tag class, this is used as a selector in created directive.
<iframe #messageIframe
class="thread-content"
[srcdoc]="processedDocument"
title="Thread Content"
scrolling="no" frameborder="0"></iframe>
I know that I am late, but maybe someone will find this useful.
Does anyone have advice for consuming the contentWindow portion of it from angular2?
@gausie I've not tried, but would expect it just to work. It's just a service running in the background on the page. If your using es6/TypeScript then I think you just need to import it.
If it is more complex than that then when you get the solution please share here.
Everything is working fine - had a little issue with tree-shaking but this works fine:
import { iframeResizerContentWindow } from 'iframe-resizer';
// This needs to be referenced so it isn't tree-shaken.
iframeResizerContentWindow; // tslint:disable-line no-unused-expression
That sounds like a bug with tree-shaking. Might be worth reporting over at WebPack.
It would be great if @arminbaljic or someone else in the Angular community would publish an Angular wrapper to this project, like we have with https://github.com/davidjbradshaw/iframe-resizer-react
Hi, @davidjbradshaw I have created a repository for angular wrapper: https://github.com/arminbaljic/ngx-iframe-resizer. This week basic version should be completed and later I could add some examples. Anyone who wants to join me is welcome.
I have created pull request for type definitions. After this is merged I will start working on the wrapper.
Update: Types are published to the @types scope on NPM, you can install them using:
npm install @types/iframe-resizer --save-dev
@arminbaljic It would be great to see this wrapper working. I'm trying to make your directive working but I'm not able... Thanks!
+1
+2
Maybe this will help, more detailed explanation on how to use the library in Angular (2,4,5). First, you need install "typings" for the iframe-resizer library. In order to do that, run this command:
npm install @types/iframe-resizer --save-dev
After that, you need to create a directive that will apply iframe-resizer to the iframe. Here is an example:
import {AfterViewInit, Directive, ElementRef, OnDestroy} from '@angular/core';
import {IFrameComponent, iframeResizer} from 'iframe-resizer';
@Directive({
selector: '[appIframeResizer]'
})
export class IFrameResizerDirective implements AfterViewInit, OnDestroy {
component: IFrameComponent;
constructor(public element: ElementRef) {
}
ngAfterViewInit() {
const components = iframeResizer({
checkOrigin: false,
heightCalculationMethod: 'documentElementOffset',
log: false
}, this.element.nativeElement);
/* save component reference so we can close it later */
this.component = components && components.length > 0 ? components[0] : null;
}
ngOnDestroy(): void {
if (this.component && this.component.iFrameResizer) {
this.component.iFrameResizer.close();
}
}
}
Add a directive to your iframe:
<iframe appIframeResizer
class="thread-content"
[srcdoc]="processedDocument"
title="Thread Content"
scrolling="no" frameborder="0"></iframe>
And the last thing you should do is to add a tag that will load "iframeResizer.contentWindow.js" in iframe. In order to do this, copy "iframeResizer.contentWindow.js" file to assets/js folder.
There are different ways to append the script to iframe content. Personally, I used "DOMPurify" plugin to sanitize and convert a string to HTML. Then, create the script tag and append it to the content of iframe document:
export class MessagePreviewComponent {
@Input() document: string;
constructor(private sanitizer: DomSanitizer) {
}
get processedDocument(): SafeHtml {
if (this.document) {
const sanitized = DOMPurify.sanitize(this.document, {FORBID_TAGS: ['script'], RETURN_DOM: true});
/* Add script tag */
const script = document.createElement('script');
script.src = 'assets/js/iframeResizer.contentWindow.js';
sanitized.appendChild(script);
/* Return result */
return this.sanitizer.bypassSecurityTrustHtml(sanitized.outerHTML);
}
return null;
}
}
I hope this will help you :)
There is a simpler version of "MessagePreviewComponent" that doesn't use DOMPurify plugin.
export class MessagePreviewComponent {
@Input() document: string;
constructor(private sanitizer: DomSanitizer) {
}
get processedDocument(): SafeHtml {
if (this.document) {
/* Add script tag */
const script = document.createElement('script');
script.src = 'assets/js/iframeResizer.contentWindow.js';
/* Return result */
const content = this.document.concat(script.outerHTML);
return this.sanitizer.bypassSecurityTrustHtml(content);
}
return null;
}
}
But please notice that this is a security risk. Use this version only if you are 100% sure that content of the iframe will not contain any harmful scripts.
@arminbaljic thanks for the input on that. The only comment I have is this line.
this.component = components && components.length > 0 ? components[0] : null;
If you have more than one iFrame on the page that will loose the reference all but the first iFrame. So I think it would be better to just dothis.component = components
here
@davidjbradshaw In my case, only one iframe can be active on the page. The directive is directly used on the "iframe" element, so I am not sure how many components will iframeResizer
function return.
Your suggestion is definitely something to look out for.
If you only have one iFrame, it will return a one element array
@davidjbradshaw if I'm not mistaken we will only want the first one [0] reference since it will be applied once for every iframe using this directive, it isn't it?
I have a couple of iframes using the directive:
<section class="admin-box">
<iframe [src]="boxOfficeUrl" frameborder="0" width="100%" height="750" iframe-resizer></iframe>
<iframe [src]="boxOfficeUrl" frameborder="0" width="100%" height="750" iframe-resizer></iframe>
</section>
So the will resize independently:
@alfupe of yes I see it is passing this.element.nativeElement
to iframeResize()
, so in that case it will only get one element back. It is possible to pass looser selectors, rather than object references, in which case you can select more than one iFrame at a time.
So basically, just ignore me :)
How, when you use the directive in the iframe, is the processedDocument()
method called that @arminbaljic references in his MessagePreviewComponent
?
To clarify,
I have a component, on which i've placed 3 iframes in its html. I've got the directive setup and the attribute added to the iframe.
The MessagePreviewComponent
(referenced above) confuses me, because my iframe loads in the html of my component, and the MessagePreviewComponent
seems to be processing the html source of the iframe as a string and concat'ing the script tag.
I'm not sure how i'm supposed to hook that processedDocument
method.
@paqogomez : Simplified structure of my use case is this:
<div class="thread-container">
<app-thread> ... </app-thread>
<app-thread> ... </app-thread>
<app-thread> ... </app-thread>
<app-thread>
<!-- Some elements -->
<!-- MessagePreviewComponent -->
<app-message-preview [document]="emailContent">
<!-- BEGIN: This is located in message-preview.component.html -->
<iframe appIframeResizer
class="thread-content"
[srcdoc]="processedDocument"
title="Thread Content"
scrolling="no"
frameborder="0">
</iframe>
<!-- END: This is located in message-preview.component.html -->
</app-message-preview>
</app-thread>
<app-thread> ... </app-thread>
<app-thread> ... </app-thread>
</div>
As you can see, the iframe is inside of message preview component. The task of message preview component is to take "emailContent" passed as a document, do some processing with that and append tag that will fetch "iframeResizer.contentWindow.js". "iframeResizer.contentWindow.js" is needed inside iframe content in order for resizing to work (library requirements). From docs:
IFrame not resizing The most common cause of this is not placing the iframeResizer.contentWindow.min.js script inside the iFramed page. If the other page is on a domain outside your control and you can not add JavaScript to that page, then now is the time to give up all hope of ever getting the iFrame to size to the content. As it is impossible to work out the size of the contained page, without using JavaScript on both the parent and child pages.
MessagePreviewComponent does not run in iframe because iframe content is executed in separated context than the main window so it has no idea about Angular.
@arminbaljic - MessagePreviewComponent is given document of type string through input decorator. In my case I don't have document coming from same domain. Is there a way in Angular to convert a document into string by passing url?
@chaitanya1248 Implement angular service that will get page content using HttpClient. If page content is loaded from another domain you will probably get No 'Access-Control-Allow-Origin' header is present on the requested resource.
error. If you are not familiar with this error you can read about it here: Access Control in Angular or CORS. Check StackOverflow for any issue with CORS, there are many more responses.
The second option is to implement your own endpoint that will do the job for you. There are probably many tutorials on this topic. When you get response page, content in string form is available as someNode.outerHTML
.
Additionally, please notice that method bypassSecurityTrustHtml
is a security risk. Use this method only if you are 100% sure that content of the iframe will not contain any harmful scripts.
@arminbaljic - Thank you for the response. Ill try the options available.
@arminbaljic Is there any chance you've released your Angular wrapper on GitHub or through NPM at all? That is super useful and solved the problem that my team and I had at work.
@arminbaljic , How can i use the resize function here .
I have implemented this , but i have a problem .
When i am resizing the window screen to the minimize , the height of the iframe increases, but when i try to maximize , the height of iframe doesn't get decreased .
What can i do to achieve that ?
@arminbaljic , i created a directive that will apply iframe-resizer, and a simpler version of "MessagePreviewComponent" but it shows me on page - null. How can i fixed it? In my app i have 4 different
Does anybody import iframeResizerContentWindow
in create-react-app (TypeScript) project?
Is this way of importing valid? Why there is no type-definition for iframeResizerContentWindow
?
@arminbaljic Sorry to bring this up long after your answer, but I'm unsure where to include your MessagePreviewComponent so that it injects the script on the iFrame. Will this work with a cross-domain iframe?
Hi everyone. Actually tried all suggested methods with absolute failure, but found a way that seems to be somewhat easier as it's more direct in the way iFrameResize is called.
First you'll need a scriptLoadingService, to take teh js files and inject them dynamically in the code. Be sure to inject it on the app.module file asa provider, otherwise you'll be using a new instance of this service everytime, missing the loadedLibaries "cache" (to avoid loading a library again and again):
import { Injectable, Inject } from '@angular/core';
import { ReplaySubject, Observable, forkJoin } from 'rxjs';
import { DOCUMENT } from '@angular/common';
@Injectable({
providedIn: 'root'
})
export class RemoteLibraryService {
// Ref.: https://codeburst.io/lazy-loading-external-javascript-libraries-in-angular-3d86ada54ec7
private _loadedLibraries: { [url: string]: ReplaySubject<any> } = {};
constructor(@Inject(DOCUMENT) private readonly document: any) {}
lazyLoadJsCss(scriptUrl: string, stylesUrl: string): Observable<any> {
return forkJoin([
this.loadScript(scriptUrl),
this.loadStyle(stylesUrl)
]);
}
loadScript(url: string): Observable<any> {
if (this._loadedLibraries[url]) {
return this._loadedLibraries[url].asObservable();
}
this._loadedLibraries[url] = new ReplaySubject();
const script = this.document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = url;
script.onload = () => {
console.log(`Script ${url} loaded...`);
this._loadedLibraries[url].next();
this._loadedLibraries[url].complete();
};
this.document.body.appendChild(script);
return this._loadedLibraries[url].asObservable();
}
loadStyle(url: string): Observable<any> {
if (this._loadedLibraries[url]) {
return this._loadedLibraries[url].asObservable();
}
this._loadedLibraries[url] = new ReplaySubject();
const style = this.document.createElement('link');
style.type = 'text/css';
style.href = url;
style.rel = 'stylesheet';
style.onload = () => {
console.log(`CSS styles ${url} loaded...`);
this._loadedLibraries[url].next();
this._loadedLibraries[url].complete();
};
const head = document.getElementsByTagName('head')[0];
head.appendChild(style);
return this._loadedLibraries[url].asObservable();
}
}
Then copy iframeResizer.js and iframeResizer.contentWindow.js files to your assets folder, lets say assets/js/
Finally, make an iframe wrapper component like this one:
SCSS file:
:host {
iframe {
width: 1px;
min-width: 100vw;
}
}
Component file:
import { RemoteLibraryService } from './../services/remote-library.service';
import { Component, OnInit, AfterViewInit, OnDestroy, ElementRef, Renderer2 } from '@angular/core';
declare global {
var iFrameResize: any;
}
@Component({
selector: 'app-iframe-wrapper',
templateUrl: './iframe-wrapper.component.html',
styleUrls: ['./iframe-wrapper.component.scss']
})
export class IframeWrapperComponent implements OnInit, AfterViewInit, OnDestroy {
iframeTag: string;
hideButton: boolean;
constructor(
private el: ElementRef,
private renderer: Renderer2,
public remoteLibraryService: RemoteLibraryService) {}
ngOnInit() {
// You can improve the code and get the iframe url using an @Input()
this.iframeTag = `
<iframe src="https://iframe-url" frameborder="0" scrolling="no" allowtransparency="true"></iframe>
`;
}
ngAfterViewInit(): void {
const scriptArray: Promise<any>[] = [
this.remoteLibraryService.loadScript('/assets/iframeResizer.js').toPromise(),
this.remoteLibraryService.loadScript('/assets/iframeResizer.contentWindow.js').toPromise(),
// you can use the remoteLibraryService to load any other script required or even css files
];
Promise.all(scriptArray).then(_ => {
console.log('All scripts loaded');
iFrameResize({
log: true,
checkOrigin: false,
sizeWidth: true,
minWidth: 100,
heightCalculationMethod: 'lowestElement',
// ... any other iframeResizer parameters
}, 'iframe');
setTimeout(() => {
// For some strange reason the iframe was stuck on the mobile sized view so I added this extra
// instructions to adjust the size
const iframe = this.el.nativeElement.querySelector('iframe');
this.renderer.setStyle(iframe, 'width', '98vw');
this.renderer.setAttribute(iframe, 'width', '100%');
}, 1500);
});
}
ngOnDestroy() {
try {
const iframe = this.el.nativeElement.querySelector('iframe');
iframe.iframeResizer.close();
} catch(ifErr) {
console.warn('Couldn't close the iframe');
}
this.iframeTag = null;
}
// Had some problems with the integration that was provided so I added a method to
// make the iframe full height. Just an emergency exit in case of ... emergencies... :P
forceAdjustHeight() {
const div = this.el.nativeElement.querySelector('div');
const iframe = this.el.nativeElement.querySelector('iframe');
this.renderer.addClass(div, 'original');
this.renderer.removeStyle(iframe, 'height');
this.renderer.setStyle(iframe, 'width', '100vw !important');
this.renderer.setStyle(iframe, 'height', '2150px !important');
this.renderer.setAttribute(iframe, 'width', '100%');
this.renderer.setAttribute(iframe, 'height', '2150px');
this.hideButton = true;
}
}
And the component template:
<div #iframeWrapper [innerHTML]="iframeTag | safe: 'html'"></div>
<button
*ngIf="!hideButton"
(click)="forceAdjustHeight()" class="btn btn-sm btn-outline-dark text-light">Adjust size</button>
Also using a SafePipe, so HTML tags can be added without any problems:
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl } from '@angular/platform-browser';
@Pipe({
name: 'safe'
})
export class SafePipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(value: string, type: string = 'html'): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl {
switch (type) {
case 'html': return this.sanitizer.bypassSecurityTrustHtml(value);
case 'style': return this.sanitizer.bypassSecurityTrustStyle(value);
case 'script': return this.sanitizer.bypassSecurityTrustScript(value);
case 'url': return this.sanitizer.bypassSecurityTrustUrl(value);
case 'resourceUrl': return this.sanitizer.bypassSecurityTrustResourceUrl(value);
default: throw new Error(`Invalid safe type specified: ${type}`);
}
}
}
Thinking seriously on making a wrapper component. Hope it helps. Best regards from Chile.
@davidjbradshaw @arminbaljic I followed all your instruction but it's not working. Is this plugin working on Angular 9 ?
@Elka74100 I have no idea, never used Angular, if you find that you need to do something new in version 9, can you please update this thread.
@jsanta solution is the only one which worked fine for me. Though, when adding iframeResizer.js and iframeResizer.contentWindow.js don't forget to add iframeResizer.contentWindow.map and iframeResizer.map as well or it won't work properly.
@arminbaljic What is the 'document' passed into the 'processedDocument' fucntion?
I just wanted to mention this here in case anyone else is having problems getting the iframeResizerContentWindow working with an Angular application embedded as an iframe in a non-Angular web site.
To solve this on my end, after the NPM install, I simply added the following to the polyfills.ts file on my Angular 12 project:
import 'iframe-resizer/js/iframeResizer.contentWindow.js';
That was all I needed and everything started working. No other changes needed to my Angular project.
@arminbaljic @clinicaloffice I am trying to use iframe-resizer in angualr 10 for cross domain sites where the iframe is getting added by test.js using create element and setting dynamic src, but as per angular implementation we need to add the directive and add selector to iframe tag but here I am adding that tag by using loader file not hardcoded so how could I add that directive in case of dynamically created iframe tags, or is there any other way to implement it.
follow up to @clinicaloffice I use iframeResizerContentWindow just by adding line in angular.json file
"architect": {
"build": {
...
"scripts": [
"node_modules/iframe-resizer/js/iframeResizer.contentWindow.js"
]
...
I just wanted to mention this here in case anyone else is having problems getting the iframeResizerContentWindow working with an Angular application embedded as an iframe in a non-Angular web site.
To solve this on my end, after the NPM install, I simply added the following to the polyfills.ts file on my Angular 12 project:
import 'iframe-resizer/js/iframeResizer.contentWindow.js';
That was all I needed and everything started working. No other changes needed to my Angular project. Hi, do you mean without creating a directive and tag, just adding will line in the polyfils.ts, will make this work or do you mean in addition to creating a directive and tag , i need to follow the steps you have described. Thanks.
I just wanted to mention this here in case anyone else is having problems getting the iframeResizerContentWindow working with an Angular application embedded as an iframe in a non-Angular web site. To solve this on my end, after the NPM install, I simply added the following to the polyfills.ts file on my Angular 12 project: import 'iframe-resizer/js/iframeResizer.contentWindow.js'; That was all I needed and everything started working. No other changes needed to my Angular project. Hi, do you mean without creating a directive and tag, just adding will line in the polyfils.ts, will make this work or do you mean in addition to creating a directive and tag , i need to follow the steps you have described. Thanks.
rmahmood19: You still need to have the iFrameResizer code in your parent page (the one with the iFrame in it), but for the Angular application that is being run inside the iFrame I only needed to import the library with NPM and add the import statement to my polyfills.ts file.
First you should install types npm install @types/iframe-resizer --save-dev
Then install the package npm install iframe-resizer --save
Below is a simple demo how responsive iframe responds to content. Demo Iframe-resizer
Hi, I'm trying to use it in version 4.3.9 for a mobile version and this fails, I know that it is not what I am using as the background, because it is in the web part, it works without problems, but in the mobile part it gives me some errors and it doesn't render correctly:
i'm use two different animations for mobile and web.
Thank you
@jsanta, @spdi, @arminbaljic, @paqogomez and everyone else on this thread.
I'm currently working on a large v5 update (#1212) to iframe-resizer and I would like to includes an @iframe-resizer/angular
component. Having never used Angular before, I'm after a bit of help with this.
The new version splits the NPM module into @iframe-resizer/parent and @iframe-resizer/child.
I have just published an alpha build of version 5 on NPM and have created a draft documentation site https://iframe-resizer.github.io/docs/
This includes an upgrade guide https://iframe-resizer.github.io/docs/upgrade/
and a guide for creating Framework components https://iframe-resizer.github.io/docs/frameworks/api/
Would any of you be able to help create a small Angular wrapper component?
@jsanta, @spdi, @arminbaljic, @paqogomez and everyone else on this thread.
I'm currently working on a large v5 update (#1212) to iframe-resizer and I would like to includes an
@iframe-resizer/angular
component. Having never used Angular before, I'm after a bit of help with this.The new version splits the NPM module into @iframe-resizer/parent and @iframe-resizer/child.
I have just published an alpha build of version 5 on NPM and have created a draft documentation site https://iframe-resizer.github.io/docs/
This includes an upgrade guide https://iframe-resizer.github.io/docs/upgrade/
and a guide for creating Framework components https://iframe-resizer.github.io/docs/frameworks/api/
Would any of you be able to help create a small Angular wrapper component?
Hi David
First - thanks for all the work you put into this library! Much appreciated! I work for a swiss enterprise customer as an external developer - we use iframe resizer in an Angular/NX context and we have working components for the older 4.3.9 version available. I would be happy to help with a wrapper for the current version. For that, we would need to address two issues: First one is an easy fix, you would need to export your type namespace from the module, otherwise it's not possible to import the Typescript definitions and secondly, there is an issue with initialization. Your provided iframeResize() method only works in an Angular context, if it's wrapped in a setTimeout() call inside the ngAfterViewInit() lifecycle hook. Not sure why.. the older version works.
Let me know if you are interested, you can hit me up via my Github profile. Cheers! André
@monobasic thanks, I have just sent you an email.
V5 guidelines now on website.
Could you please provide guidelines for using this library in angular 2 project created with CLI/Webpack.