Prevent Angular 15 iframe update browser history - iframe

I have an Angular 15 app with an iframe where I render html content from file.
Since this content may be changed by user I update src value. This affects browser history stack.
I was googling and found here Angular 7 iframe history back button trouble how to solve the issue. There is only one problem. My src variable is base64 content.
This is what I was using before and it renders fine but affects location stack:
.html
<div class="form-data">
<iframe width="100%" height="100%" [src]="layoutPath"></iframe>
</div>
.ts
private dbLayout: string;
layoutPath: SafeHtml;
ngOnInit() {
this.myService.getLayoutFromApi$().subscribe(result => this.dbLayout = result);
}
userChangeSomeContentOfHtml(someUserContent: string) {
const base64Layout = Base64.encode(dbLayout+ someUserContent);
this.layoutPath = this.sanitizer.bypassSecurityTrustResourceUrl("data:text/html;base64," + base64Layout);
}
}
According to solution from above link I updated my code to this:
.html
<div class="form-data">
<iframe #iframe width="100%" height="100%"></iframe>
</div>
.ts
private dbLayout: string;
ngOnInit() {
this.myService.getLayoutFromApi$().subscribe(result => this.dbLayout = result);
}
userChangeSomeContentOfHtml(someUserContent: string) {
const base64Layout = Base64.encode(dbLayout+ someUserContent);
const layouthPath = this.sanitizer.bypassSecurityTrustResourceUrl("data:text/html;base64," + base64Layout);
this.iframe.nativeElement.contentWindow.location.replace(layoutPath);
}
}
Unfortunately variable layoutPath is base64 and location.replace leads me to nowhere.
How can I solve this location stack bypass with a base64 content?

Related

"react-pdf" displaying text found inside the PDF instead of the PDF itself?

https://www.npmjs.com/package/react-pdf
I'm using this package to try to display a PDF file.
This is the PDF i'm currently displaying:
https://i.imgur.com/YHKybGZ.png
and as you can see in this screenshot,
https://i.imgur.com/WAHvzHU.png
the text is extracted from the PDF and displayed under the image of the PDF. I don't want this. I just want the PDF.
This is how it looks in the dev inspector
https://i.imgur.com/QzaIrlo.png
there's a "react-pdf__Page__textContent" class that I don't want included. I already tried creating a CSS class and displaying none like this:
.react-pdf__Page__textContent{
display: none;
}
but that didn't work and the text is still appearing.
I don't know where the text is coming from because I don't include "textContent" anywhere in the code. This is the code I'm using to render the PDF.
Pdf.js
import SinglePagePDFViewer from "../components/pdf/single-page";
import samplePDF from "../components/pdf/diy-find_mac_address.pdf";
<>
<Navbar />
<div className="pdf-wrapper">
<div className="pdf-viewer">
<SinglePagePDFViewer pdf={samplePDF} />
</div>
</div>
</>
single-page.js
import React, { useState } from "react";
import { Document, Page } from "react-pdf";
export default function SinglePage(props) {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1); //setting 1 to show fisrt page
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
setPageNumber(1);
}
function changePage(offset) {
setPageNumber((prevPageNumber) => prevPageNumber + offset);
}
// function previousPage() {
// changePage(-1);
// }
// function nextPage() {
// changePage(1);
// }
const { pdf } = props;
return (
<>
<Document
file={pdf}
options={{ workerSrc: "/pdf.worker.js" }}
onLoadSuccess={onDocumentLoadSuccess}
>
<Page pageNumber={1} />
</Document>
</>
);
}
Has anybody used this package before? Any help? How do I display the PDF only, without the "text-content" below it?
EDIT:
Tried to create a Stackblitz example but IDK how to use PDF's with stackblitz. If you know how, this stackblitz will replicate the issue:
https://stackblitz.com/edit/react-qkbqgf?file=src/App.js
Had a similar issue. You can fix it by importing the textlayer css to the file:
import "react-pdf/dist/esm/Page/TextLayer.css";
This got rid of the text for me.
If that doesn't work, you can alternatively disable the textlayer prop in the <Page/> component by setting it to false although I wouldn't recommend that.
<Page pageNumber={1} renderTextLayer={false} />

Next Js rewrite not working when going back in browser

I'm rewriting /editorial/bowers-and-wilkins to /bowers-and-wilkins in my next.config.js:
async rewrites() {
const fallback = [
{
source: "/:path*",
destination: `/editorial/:path*`
}
];
const afterFiles = [
{
source: `/bowers-and-wilkins`,
destination: `/editorial/bowers-and-wilkins`
}
];
return {
fallback,
afterFiles
};
}
The redirect works, I'm using it in a Link component for example:
<Link href="/bowers-and-wilkins">
<a>
Bowers & Wilkins
</a>
</Link>
But when navigating to a different link then hitting back in the browser I get an incompatible href error:
The provided as value (/bowers-and-wilkins) is incompatible with the
href value (/editorial/[slug]).
Is there a workaround? I suspect may be modifying the Link or history state may be.
Updating from next 10 to 12.1.0 fixed my issue

How to conditionally render a component in Next.js without caching CSS styles?

I'm pretty new with Next.js and do not fully understand the cache functioning.
Given the following simplified example:
An index page that renders components Test1 or Test2, depending whether the current minute is even or odd:
import { Test2 } from '#src/components/test2'
import React from 'react'
const conditionallyChooseComponent = () => {
const d = new Date()
if (d.getMinutes() % 2 === 0) return <Test1 />
else return <Test2 />
}
export default function Home() {
return <div>{conditionallyChooseComponent()}</div>
}
And having the following components. Test1:
export const Test1 = () => {
const d = new Date()
return (
<div className={`${utilStyles.redContainer}`}>
<h1>It's {d.toISOString()} and I'm Test1 component. My background should be red</h1>
</div>
)
}
And Test2:
export const Test2 = () => {
const d = new Date()
return (
<div className={`${utilStyles.blueContainer}`}>
<h1>It's {d.toISOString()} and I'm Test2 component. My background should be blue</h1>
</div>
)
}
And this CSS:
.redContainer {
background-color: red;
}
.blueContainer {
background-color: blue;
}
The background color is being cached when the code is executed by building and serving from the transpiled code. When running with yarn dev it is working just fine.
Here is the unexpected result:
Screenshot with Test1 component being rendered with blue background
PS: I made this work with the workaround of using the getInitialProps to prevent Next.js from caching anything in that page but, for my real use case that option is not valid because I need the render condition to be calculated in the client side since it will depend on the local date of the browser.
Next will automatically cache all static pages that doesn't depends on external data, maybe you can implement useEffect to update your date variable or use a simple state, so it should work the way you expect
https://nextjs.org/docs/basic-features/pages#static-generation-without-data
To make that works, you will need to add some client-side code (via useEffect), so the React component updates every minute (or so). Funny enough, this is not as simple as it sounds, and even Dan Abramov has published a long post explaining why things such as setInterval may not work intuitively with React (specifically, with React Hooks).
Assuming you use the custom hook Dan explains in the article above, this should work:
export default function Home() {
const [date, setDate] = useState(new Date());
useInterval(() => {
// this will update the component's date every second
setDate(new Date());
}, 1000);
return <div>{date.getMinutes() % 2 === 0 ? <p>Test 1</p> : <p>Test 2</p>}</div>;
}
Observe that your code example only executes the conditionallyChooseComponent once, just when Next is trying to server-side rendering your page.

Angular 4 CSS DIV background-image binding doesn't work

template:
<div [style.background-image]="profileImage" ></div>
ts:
private profileImage: any;
private sanitizer:DomSanitizer
get photo from service:
this.profileImage = this.sanitizer.bypassSecurityTrustStyle("url("+ data.photo + ")");
The binding is not working. I checked Chrome Dev tools, photo is not downloaded. When refreshing the page, it is working fine, but it has to work when the value is set in service. Tried with ngStyle also, it is not working.
Plunkr link https://plnkr.co/edit/IhVGjiImyfk0F1u6cWtG?p=preview
I updated a bit your code in order to work:
#Component({
selector: 'my-app',
template: `
<div [style.background-image]="profileImage" style="width: 961px; height: 688px;"></div>
<h2> {{message}}</h2>
`,
})
export class App {
profileImage: string;
message: string;
constructor(private sanitizer: DomSanitizer) {
this.message = "Before async";
}
async ngOnInit() {
await delay(2000);
this.message = "Updating Photo";
const url = 'https://4.bp.blogspot.com/--RBVfn6FzOI/Tq5kOxFGKEI/AAAAAAAACnM/LaiZLD0sUKY/s1600/cats.jpg';
this.profileImage = this.sanitizer.bypassSecurityTrustStyle(`url(${url})`);
}
}
Changes
1st: Change:
constructor(sanitizer: DomSanitizer)
into this:
constructor(private sanitizer: DomSanitizer)
Thus having the sanitizer as a member of the class in order to be accessible in ngOnInit().
2nd: Set the dimension of the <div>. This will not adjust automatically to your background image size. So I set width and height. Other solutions exist, like using the aspect ratio of the image, or keeping an invisible <img>. These and more are described in this answer.
3nd: Set the image HTTP scheme from http to https because you are receiving a warning in Plunker which serves content over https.
Updated Plunk

How to add stylesheet to toolbar

Using the Firefox Addon SDK, I am creating a toolbar with several buttons and I want to create a mouseover effect for the buttons.
At first I thought to use a mouseover event, but then I would have to create a mouseout event to return it to normal, so I figured the best way would be to use css
In my old XUL version of my addon I was able to attach the stylesheet by linking to it in the XUL code and just add css for my #buttonID, which worked perfectly.
But how do I add the css stylesheet for my toolbar using the Addon SDK?
Here's what I've tried so far (which does not produce any errors), but I think this is just for content; if this is correct, then I'm not sure how to bind to the element:
const { browserWindows } = require("sdk/windows");
const { loadSheet } = require("sdk/stylesheet/utils");
//This is how to load an external stylesheet
for(let w of browserWindows){
loadSheet(viewFor(w), "./myStyleSheet.css","author" );
}
I've also tried this:
var Style = require("sdk/stylesheet/style").Style;
let myStyle = Style({source:'./myStyleSheet.css'});
for(let w of browserWindows){
attachTo(myStyle, viewFor(w))
};
And this:
var { attach, detach } = require('sdk/content/mod');
const { browserWindows } = require("sdk/windows");
var { Style } = require('sdk/stylesheet/style');
var stylesheet = Style({
uri: self.data.url('myStyleSheet.css')
});
for(let w of browserWindows){
attach(stylesheet, viewFor(w))
};
And here is my css:
#myButton:hover{list-style-image(url("./icon-16b.png")!important; }
Tested this in Browser Toolbox:
const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js"); // skip this in SDK
const { browserWindows: windows } = require("sdk/windows");
const { viewFor } = require("sdk/view/core");
const { attachTo } = require("sdk/content/mod");
const { Style } = require("sdk/stylesheet/style");
let style = Style({ source: "#my-button{ display: none!important; }" });
// let self = require("sdk/self");
// let style = Style({ uri: self.data.url("style.css") });
for (let w of windows)
attachTo(style, viewFor(w));
The commented part allows to load from a stylesheet file in the addon data directory.
Notice that you need to import SDK loader to use it in the toolbox.
When in an SDK addon, just use require directly.
NB: there is a difference in spelling: self.data.url vs { uri }
See self/data documentation.
NB2: SDK uses a custom widget ID scheme for toggle and action buttons so your button ID might not be what you expect:
const toWidgetId = id =>
('toggle-button--' + addonID.toLowerCase()+ '-' + id).replace(/[^a-z0-9_-]/g, '');
OR
const toWidgetId = id =>
('action-button--' + addonID.toLowerCase()+ '-' + id).replace(/[^a-z0-9_-]/g, '');
using this code, you should be able to use the mouse over or hover to change how it looks.
#buttonID {
//Normal state css here
}
#buttonID:hover {
//insert css stuff here
}
This goes in the javascript file:
const { browserWindows } = require("sdk/windows");
const { viewFor } = require("sdk/view/core");
const { loadSheet } = require("sdk/stylesheet/utils");
const { ActionButton } = require("sdk/ui/button/action");
var StyleUtils = require('sdk/stylesheet/utils');
var myButton = ActionButton({
id: "mybutton",
label: "My Button",
icon: { "16": "./icon-16.png", "32":"./icon-32.png", "64": "./icon-64.png" },
onClick: function(state) {
console.log("mybutton '" + state.label + "' was clicked");
}
});
//this is how you attach the stylesheet to the browser window
function styleWindow(aWindow) {
let domWin = viewFor(aWindow);
StyleUtils.loadSheet(domWin, "chrome://myaddonname/content/myCSSfile.css", "agent");
}
windows.on("open", function(aWindow) {
styleWindow(aWindow);
});
styleWindow(windows.activeWindow);
And here is the css for that
//don't forget to add the .toolbarbutton-icon class at the end
#action-button--mystrippedadonid-mybuttonid .toolbarbutton-icon,{
background-color: green;
}
There are several gotchas here.
First, as of this posting, you should not use capital letters in the id for the button because they get completely removed - only lowercase letters and hyphens are allowed.
The id of the element is not the same as the id you gave it in the button declaration. See below for how to come up with this identifier.
To specify content in the url for the stylesheet file (in the loadSheet function call) you will also need to create a chrome.manifest in the root of your addon folder, and put this in it: content spadmintoolbar data/ where "data" is the name of a real directory in the root folder. I needed a data/ folder so I could load icons for the button declarations, but you need to declare your virtual directories in chrome.manifest which jpm init does not do for you.
How to get the element id for your css file:
The easy way to get the id for your button element for use in an external style sheet is by testing your addon and then using the browser-toolbox's inspector to locate the element, whence you can fetch the id from the outputted code.
However, if you want to figure it yourself, try this formula.
[button-class] = the sdk class for the button. An Action Button becomes action-button
[mybuttonid] = the id you gave the button in the sdk button declaration
[myaddonname] = the name you gave the addon in it's package.json file.
[strippedaddonid] = take the id you assigned the addon in the package.json file, and remove any # symbol or dots and change it to all lowercase.
Now put it all together (don't include the square brackets):
`#[button-class]--[strippedaddonid]-[mybuttonid]]`
An example: action-button--myaddonsomewherecom-mybutton
Really simple isn't it?!
credit for the stylesheet attach code goes to mconley

Resources