How to properly set print orientation in Angular application? - html

I need to create a printing service, that can be called from different components throughout my application. The service method accepts two parameters, the component that needs to be compiled and printed, and the printing orientation (Portrait or Landscape).
Everything works fine, except the orientation. Either it is not set, or it sticks with the secondly applied orientation. What I mean by secondly applied orientation is, I have two buttons for testing purposes. Each button calls different component and sets different orientation. If I click the buttons in the following order: Portrait orientation and then Landscape orientation, no matter what I press after that, the page is printed in Landscape, and vice versa.
So far, I have tried the following:
defined two different div elements conditionally generated (with ngIf directive). Each div contains <style></style> elements with appropriate page orientations. The style elements are not rendering, hence orientation is not changed
page orientation is defined within the component's style/stuleUrls metadata. This produces the problem with the secondly applied orientation as described above
add styling to the dynamically created components with ComponentFactoryResolver. The styling to the page is not applied
The component is dynamically generated using the ComponentFactoryResolver. For simplicity, I won't post that code. I will post it if it's needed.
This html is added in the app.component.html:
<div class="print-section">
<div *ngIf="printOrientation === orientationEnum.Landscape">
<style>
#media print {
#page {
orientation: landscape !important;
}
}
</style>
</div>
<div *ngIf="printOrientation === orientationEnum.Portrait">
<style>
#media print {
#page {
orientation: portrait !important;
}
}
</style>
</div>
</div>
The same code is added within the component style/styleUrls metadata.
I have also tried adding classes to the generated components dynamically, after the component is created:
let element: HTMLElement = <HTMLElement>componentRef.location.nativeElement;
this.printTemp.layout === PrintLayoutEnum.Landscape ? element.classList.add('landscape-print') : element.classList.add('portrait-print');
This doesn't affect the orientation at all.
Expected outcome is to change the orientation appropriately. Either by setting the style in app.component.html, or by applying the dynamically when the component is rendered with the ComponentFactoryResolver. I prefer the second options, but either one should be fine.

I got tunnel vision and I tried to over-complicate the solution. I created a style element with id. During the component creation with the ComponentFactoryResolver, I simply appended the desired style:
let styleElement = document.getElementById('print-style');
styleElement.append('#media print { #page { size: A4 landscape; } }')

Related

Vaadin - print grid with CSS

Inside vaadin-grid.css I managed to get the #table element which contains the grid's rows
and with for example
#table th {
backround-color:green;
}
I can change its color.
Now I need to do that only when the page is printed.I tried adding inside vaadin-grid.css
#media print {
#table th {
backround-color:green;
}
}
but that has no effect.Note that I print the page using javascript print().
I added an id="viewfgrid" (as seen in the screenshot) to the enclosing grid and with that now when I add
inside shared-style.css
#media print {
#viewfgrid {
outline:green;
}
}
I can access and change the grid when printing.
However I can't access the inside table with the rows.I tried various variations like
#media print {
#viewfgrid #table {
background-color:green;
}
}
#media print {
#viewfgrid :host > table {
background-color:green;
}
}
but no effect.
How can I access that table ?
Also as a secondary question why can I access #table from within vaadin-grid.css without prepending :host ? when I do that , it has no effect
thanks
I’m not sure why #media print would not work from within the Grid’s shadow DOM styles. Did you try in different browsers? I wonder if there’s some browser bug/limitation here, similar to the fact that you can’t define a #font-face inside shadow DOM.
Also as a secondary question why can I access #table from within vaadin-grid.css without prepending :host ? when I do that , it has no effect
The host selector targets the same element as the #viewfgrid ID. To select a host element with a specific ID, you can use :host(#viewfgrid) inside the shadow DOM styles.
Notice, that you should not rely on any ID, class name, or raw tag name selectors when styling Vaadin components (for example #table or th. Those are considered internal implementation details / private API, and can change in any release. You should only rely on the :host(...) and [part~="..."] selectors and state attribute selectors (for example, [focused]).
If there really is a limitation in using #media print inside shadow DOM styles, I think your best option is to use the ::part() selector, which allow you to style named parts inside the shadow DOM from the outside/light DOM styles. That is actually a more robust method than relying on injecting styles into the shadow DOM (via the frontend/mytheme/components/vaadin-grid.css file).
styles.css:
#media print {
#viewfgrid::part(cell) {
background-color: green;
}
}
The API docs show all available parts of the Grid (look for the "Styling" section): https://cdn.vaadin.com/vaadin-web-components/23.2.0-alpha3/#/elements/vaadin-grid
Grid is by design not very printing friendly as it has been designed for "infinite vertical scrolling". You wont for example have headers and footers per page. If you want to include "report" functionality to your application, it is better approach to create separate report view that is designed printing friendly instead of screen use. This will allow you to use different layouting and components in it. You can for example generate multiple Grid's for each page. Or use BeanTable component from Directory, which generaters HTML Table without shadow DOM.
Because apparently you can't access the shadow dom from CSS when there's no 'part' tag on the element,as is the case with this table,I got it by using Javascript as in :
UI.getCurrent().getPage().executeJs("const tabledom = document.querySelector(\"#viewfgrid\").shadowRoot.querySelector(\"#table\");
tabledom.style.cssText+='.....' "
So now this snippet is called when the user clicks on a Print button and you can do whatever with the element's style.In my case I flatten the table so that it can be printed without the scrollbar intervening.

How can I override A-Frame CSS in an app built using create-react-app? [duplicate]

This question already has answers here:
How are the points in CSS specificity calculated
(7 answers)
Closed 12 months ago.
I'm building a mobile app with 2D & 3D elements.
2D elements use React, and I'm using create-react-app for the build/bundling etc.
3D elements use A-Frame.
For iOS, I need to obtain device orientation permissions, and I'm doing this using the standard A-Frame component: https://aframe.io/docs/1.3.0/components/device-orientation-permission-ui.html
However I want to change the appearance of the modal, by specifying my own CSS (as suggested on that page).
I have a problem that appears only in my production build.
I've written the CSS overrides that I want in my App.css. I build my production JS and CSS files.
They are included in index.html like this (inside the <head>)
<script defer="defer" src="/static/js/main.6b739253.js"></script>
<link href="/static/css/main.8198a130.css" rel="stylesheet">
However, on initialization, A-Frame adds its own CSS files to the end of the HTML header:
<style type="text/css" data-href="src/style/aframe.css">
...CSS here...
</style>
Because the A-Frame CSS appears at the end of the HTML header, it takes priority over my custom CSS, and I end up with the A-Frame styling for the modal, rather than my custom styling.
I've come up with a solution for this, but I think there's got to be a better one...
My solution: the following code at the top of my top-level React component, which explicitly removes the aframe.css styles from the HTML (I've also added the A-Frame styles that I don't want to override directly in my App.css, where I can have full control over what I do & don't include).
useEffect(() => {
var head = document.getElementsByTagName('HEAD')[0];
for (var ii = 0; ii < head.children.length; ii++) {
const child = head.children[ii];
if (child.attributes['data-href'] &&
child.attributes['data-href'].nodeValue === "src/style/aframe.css") {
head.removeChild(child);
}
}
}, [])
I'm looking for a better solution, where I don't have to duplicate large chunks of aframe.css in my own CSS file, and I can simply have the two CSS files loaded, but prioritized in a different order.
Ideally that solution would not require me to eject from create-react-app, which I have managed to avoid needing to do so far.
There is in fact a simple solution here.
A CSS specifier that specifies an element selector + a class selector is more specific than a specifier that specifies a class selector only.
https://www.w3schools.com/css/css_specificity.asp
So the following CSS...
button.a-dialog-allow-button {
background-color: red;
}
will be more specific (and hence take precedence over) the A-Frame defaults which look like this:
.a-dialog-allow-button {
background-color: #00ceff;
}
Similarly you can override .a-modal with div.a-modal, .a-dialog with div.a-dialog etc.
One thing to watch out for (this threw me when testing this)... Not all A-Frame modals use the a-modal and a-dialog classes.
There are various other classes, used for specific modals - e.g. a-enter-vr-modal and a-orientation-modal (used for portrait / landscape orientation, rather than for device orientation permisions).
So just if you see an A-Frame modal, and your changes don't seem to have worked, it may be because it is using a different set of modal classes from the ones you changed. Check the class names carefully!

Print site logo just on first page (#media print )

I need to create print version of website, and as I mention in title I need to display site logo just on first page. For example, if I print home page, and I get 5 pages, logo should be displayed just on first page.
is it possible with #media print ?
What I've tried so far but does not work
#media print {
#top-menu,
#main-navigation-sticky-wrapper,
#action-bar,
.teaser-cda,
.pre-footer,
.footer,
.post-footer,
.header .logo {
display: none;
}
#page:first {
.header .logo { display:block }
}
The correct syntax (according to MDN) for first page is:
#page :first {
/* .... */
}
You don't have a space between the two components. Be wary, however, as compatibility for #page :first is not well-defined.
It might not even be necessary though. I don't think block-level elements get repeated on every page, so you might just need to ensure that the logo is displayed in #media print { ... }.
You will also want to check the element and it's container elements to ensure that none of them have position: fixed as that may also cause the element to repeat on each printed page.
#page rule is a CSS at-rule used to modify different aspects of a printed page property. It targets and modifies only the page's dimensions, page orientation, and margins.
It can't have css class inside.
#page :first {...} it just allows you to add these previous styles on the first page but you can't also add a class inside.

Ionic - CSS is not applied when refreshing on another view

I'm currently working on my first Ionic App and working with Angular for the first time.
I am using the pie-chart library to display charts on the dashboard of the app. This works nicely if I refresh while I am on the dashboard and looks like this:
https://imgur.com/YUCAO6i,oakGp8c#1
But if I navigate to another tab, lets say the server tab, and refresh there, the width and height is not applied to the charts on the dashboard. Instead they are rendered using the standard width and height (500h x 900w instead of 100h x 100w). (See second picture on imgur). If I refresh on the dashboard again, they will render normally.
I went through the source code of the library and saw that when refreshing on the dashboard, the element[0].parentElement.offsetWidth equals 100, but if I refresh when on another view, it is 0, so the default values are used. It looks like the pie-chart directive can't access the parent when on another view.
Here is the HTML and CSS used:
HTML
<div class="pieChart">
<pie-chart data="server.chartData.cpu" options="chartOptions"></pie-chart>
</div>
CSS
.pieChart {
height: 100px;
width: 100px;
float: left;
}
I tried to find an answer for hours, but I am not even sure what exactly I need to search for. The only solution I came up with is to change the default value in the pie-chart.js, which I would prefer not to do.
EDIT
The app is open source, you can find the full code in my repository: https://github.com/AndreasGassmann/cloudatcostapp
After hours of researching I finally found the cause of the problem.
There are 2 separate behaviours causing the issue:
Ionic caches up to 10 views to increase performance. This means that when you switch from one tab to another, the first one remains in the DOM so it can be loaded quicker. However, it seems that the CSS styles are not applied to the view that is not visible. This means that the actual height and width of the div with the .pieChart class is 0 at this point. I think it's similar to setting the display: none property of an element. (A blogpost explaining the issue with jquery)
Whenever the pie-chart updates, it will set its size to the width and height of the parent element. I guess this is made so the chart will resize after you resize your window and refresh the data.
Those 2 things obviously don't go well together. Since Ionic caches the dashboard view, the <pie-chart></pie-chart> element is still in the DOM, so it will try to re-render immediately. But because the styles are not applied to the the parent div, it will just get width and height 0 and fall back to using the default values.
In normal websites the views usually don't get cached. This means that when you refresh, the <pie-chart></pie-chart> element isn't present in the DOM, so it won't try to render at all. Only after you navigate back to the view and the element is loaded again will it try to render and read the dimensions of its parent. (Which will work, since all styles are applied).
I haven't found a way how you can tell an element to "stay rendered", even if it's not the active view. This means that there are 2 options to solve this (besides changing the way the pie-chart library works):
Hardcode the height and width as the default value inside the pie-chart.js
Disable caching for that view in ionic or clear the view cache every time you refresh. You can do this by calling $ionicHistory.clearCache()
Force the style I assume with :
.pieChart {
height: 100px !important;
width: 100px !important;
float: left !important;
}

How to give some space to every page in printing only using css

I want to give some space to top of every page which is applied in only in printing mode.
is it possible or not..?
I am using:
#page { margin-top : 30px; }
But it doesn't get applied..
Are there any other methods available in css..?
You can do the following way.
#media print
{
body {margin-top:30px;}
}
This will select and target only the print related CSS changes. Hope this helps.
*PS: I have taken Body element, but if you want, you can target specific wrapper that is part of your HTML and you can target it specifically only if you want that wrapper to start from top with certain spacing. You have the solution with logic. Use it to match your scenarios.*