So here is the explanation of the problem I am facing. It might look very similar other already asked questions, but none of them answered my problem.
I want to open an angular template reference in a new browser window (with all the styles) and use that window to print the contents using system print dialog.
By template reference I mean a whole component or may be just a fraction of template of given component.
Please note that I do not want to do it by below methods:
Opening a new route in a new browser window. (Why? this will cause all other things like common toolbar or help open up with the component. Also a new route will be required which is undesired.)
Opening the content in a closable modal.
Ok, This is how I did it using ComponentFactoryResolver and Injector. Just inject these two dependencies in your component which wants to open other component (ReportComponent in this case) in new browser window.
The constructor looks something like this in snippet below.
constructor(
private resolver: ComponentFactoryResolver,
private injector: Injector
) {}
.
.
.
This method opens up the component in new browser window.
public openPrintableReport(): void {
// resolve and instantiate the component which you want to open in new window.
const componentFactory = this.resolver.resolveComponentFactory(ReportComponent);
let component = componentFactory.create(this.injector);
component.changeDetectorRef.detectChanges();
// define external window
this.externalWindow = window.open('', '', 'width=1000,height=1000,left=150,top=200');
// copy all the styles to external window.
document.querySelectorAll('style').forEach(htmlElement => {
this.externalWindow.document.head.appendChild(htmlElement.cloneNode(true));
});
this.externalWindow.document.title = 'Printer Friendly Report';
// attach the component to external window
this.externalWindow.document.body.appendChild(component.location.nativeElement);
}
Related
I'm using Angular 10, on click the following function is executed to preform direction change:
private changeHtmlDirection(direction: 'rtl' | 'ltr') {
document.getElementsByTagName("html")[0].dir = direction;
}
It works well, only that the Angular CDK does not update.
I tried to find an API to change Angular CDK's direction at runtime, but couldn't find any.
I saw that there's a BidiModule but it uses only to get the current direction rather than set it.
Is there any solution?
According to the material documentation, you can't change 'dir' on the "html" tag so that affects bidi API. You can see the document at the following link:
bi-directionality document
But if you want to use material bi-directionality you can add the 'dir' directive to a container element in the root component like bellow:
<div [dir]="documentDirection"> </div>
and whenever the 'documentDirection' variable changes, the bidi "change emitter" will be emit.
like following code you can subscribe to it:
constructor(
private dir: Directionality ) {
this.isRtl = dir.value === 'rtl';
this.dir.change.subscribe(() => {
this.isRtl = !this.isRtl;
});
}
Is it possible to customize the back behavior of the Angular router? Specifically, I'd like to add a URL fragment based on the resource the browser is navigating from.
For example, if the browser is on http://example.com/purchases/42, navigating back would take the user to http://example.com/purchases#42 instead of http://example.com/purchases. This is desirable because the /purchases page could be very long, and the URL fragment could position the browser in the context of the previous page.
Is such a thing even possible? Is the only way this could be accomplished is by using the History API, or is there some other API that Angular users for managing navigation state?
Well, lucky for you, in the new Angular 6.1 there is a new feature for routers that you can enable and your router will "remember" your last scroll position when you hit back.
You have to set the routers module like that:
RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled'
})
There problem right now is that its a very new feature, and its will only work for static pages. Thats mean that if you fetching content from service or something, the restoration will try to set it self before you actually have the data, so the position will fail. (Currently, it will also fail even if you are using a resolver)
There is a workaround that we can use right now via the new service called viewportScroller from the #angular/router package, but you will have to do it manully. (currently, it will probably get fixed in the near future).
export class PendingRacesComponent {
scrollPosition: [number, number];
races: Array<RaceModel>;
constructor(route: ActivatedRoute, private router: Router, private viewportScroller: ViewportScroller) {
this.races = route.snapshot.data['races'];
this.router.events.pipe(
filter(e => e instanceof Scroll)
).subscribe(e => {
if ((e as Scroll).position) {
this.scrollPosition = (e as Scroll).position;
} else {
this.scrollPosition = [0, 0];
}
});
}
ngAfterViewInit() {
this.viewportScroller.scrollToPosition(this.scrollPosition);
}
}
This is an example of how you can use it right now, for full explanation you should visit the post
I am working on my first bigger Polymer application and currently have around 30 components. Most of the components need to be able to display (modal) message boxes. For this I implemented a message box component wrapping paper-dialog (similar to other message box components available).
What I don't like is that in every component which wants to display message boxes I need to define an element
<my-message-box id="message-box"></my-message-box>
and then call it like this
this.$["message-box"].information("Something happened...");
This works but my gut feeling is that a message box should be more like a global service, a singleton maybe. In C# f.e. there exists a static method on the MessageBox class.
Is the above mechanism really the recommended way to do it or are there better solutions to it?
My current approach is to create error-dialog and add it as a sibling to my main-app in index.html:
<body>
<main-app></main-app>
<error-dialog></error-dialog>
<noscript>
Please enable JavaScript to view this website.
</noscript>
</body>
error-dialog's ready() method adds a custom event:
ready() {
super.ready();
this.addEventListener('o_error', e => this._errorListener(e));
}
_errorListener(e) {
this.o_error = e.detail;
this.$.errorDlog.open();
}
Now I can open error-dialog from anywhere with
let msg = ...
const dlog = document.querySelector('error-dialog');
dlog.dispatchEvent(new CustomEvent('o_error', {detail: msg, bubbles: true, composed: true}));
Using Polymer Starter Kit as an example, I would like to have different <app-toolbar> in <my-app> (using property headerType) based on different <my-view#>, i.e.
<my-view1> => headerType = 'my-view1-header'
<my-view2> => headerType = 'my-view2-header'
In my <my-app>, I have created a property headerType and use <dom-if> to show/hide different <app-toolbar>.
My question is how would I always fire an event to <my-app> and set headerType = my-view#-header whenever <my-view#> is active (i.e. comes into view).
I have tried the polymer lifecycle, such as ready(), attached(), etc, and I understand they are only trigger during dom-related events.
I eventually use the _pageChanged observer to call a function on <my-view#>. Below are the snippet of the code.
_pageChanged: function(page) {
let onLoad = function () {
let selected = this.$.ironpages.children[page];
if (Object.getPrototypeOf(selected).hasOwnProperty('viewSelected')) {
selected.viewSelected();
}
}
// Load page import on demand. Show 404 page if fails
var resolvedPageUrl = this.resolveUrl('my-' + page + '.html');
this.importHref(resolvedPageUrl, onLoad, this._showPage404, true);
},
There is some example in Polymer shop template where you can execute something when the visibility of your view change with iron-pages.
you just need to add a property for example visible in each of your view element with Boolean type and observe that property to check whatever the view is visible or not, and then in your iron-pages you need to add selected-attribute property and the value is visible. check Polymer Shop Template.
I`m trying to insert a custom link to a special page in VisualEditor toolbar. See the image below.
See Image
I googled a lot but without success. Someone please give a path...
My answer is based on the following resources:
MediaWiki core JS doc (ooui-js)
VisualEditor JS doc (+ reading code of both repositories used for VE, mediawiki/extension/VisualEditor and VisualEditor)
Also, I'm pretty sure, that there is no documented way of adding a tool to the toolbar in VE, as far as I know. Although it's possible to add a tool to a group, which is already added, mostly used for the "Insert" tool group, like in Syntaxhighlight_GeSHi). There is, probably, a much easier or "better" way of doing this :)
First, VisualEditor provides a way to load additional modules (called plugins) when the main part of VE loads (mostly, when you click the "Edit" button). The modules needs to be registered via the global variable $wgVisualEditorPluginModules (or the equivalent in extension.json, if you're using the new extension registration). In your extension registration file, you should initialize a module (with your required script files to add the tool) and add it as a VE plugin.
Example PHP (old extension registration via PHP files):
// other setup...
$wgResourceModules['ext.extName.visualeditor'] = array(
'localBasePath' => __DIR__,
'remoteExtPath' => 'extName'
'dependencies' => array(
'ext.visualEditor.mwcore',
),
'scripts' => array(
'javascripts/ve.ui.ExtNameTool.js',
),
'messages' => array(
'extname-ve-toolname',
),
);
$wgVisualEditorPluginModules[] = 'ext.extName.visualeditor';
// other setup...
extension.json (new JSON-based extension registration):
// other setup...
"ResourceModules": {
"ext.geshi.visualEditor": {
"scripts": [
"javascripts/ve.ui.ExtNameTool.js"
],
"dependencies": [
"ext.visualEditor.mwcore"
],
"messages": [
"extname-ve-toolname"
]
}
},
"VisualEditorPluginModules": [
"ext.extName.visualeditor"
],
// other setup...
Now, if VE starts, it will load your module, named ext.extName.visualeditor in this example, with the script ve.ui.ExtNameTool.js. In this script, you can now do, what ever you want. As an example, this is a way to add a new module to the end of the toolgroup list in the toolbar:
Example of ve.ui.ExtNameTool.js:
( function () {
// create a new class, which will inherit ve.ui.Tool,
// which represents one tool
ve.ui.extNameTool = function extNameTool( toolGroup, config ) {
// parent constructor
ve.ui.extNameTool.super.apply( this, arguments );
// the tool should be enabled by default, enable it
this.setDisabled( false );
}
// inherit ve.ui.Tool
OO.inheritClass( ve.ui.extNameTool, ve.ui.Tool );
// every tool needs at least a name, or an icon
// (with the static property icon)
ve.ui.extNameTool.static.name = 'extname';
// don't add the tool to a named group automatically
ve.ui.extNameTool.static.autoAddToGroup = false;
// prevent this tool to be added to a catch-all group (*),
although this tool isn't added to a group
ve.ui.extNameTool.static.autoAddToCatchall = false;
// the title of the group (it's a message key,
// which should be added to the extensions i18n
// en.json file to be translateable)
// can be a string, too
ve.ui.extNameTool.static.title =
OO.ui.deferMsg( 'extname-ve-toolname' );
// onSelect is the handler for a click on the tool
ve.ui.extNameTool.prototype.onSelect = function () {
// show an alert box only, but you can do anything
alert( 'Hello' );
this.setActive( false );
}
// needs to be overwritten, but does nothing so far
ve.ui.extNameTool.prototype.onUpdateState = function () {
ve.ui.extNameTool.super.prototype.onUpdateState.apply( this, arguments );
}
// the tool needs to be registered to the toolFactory
// of the toolbar to be reachable with the given name
ve.ui.toolFactory.register( ve.ui.extNameTool );
// add this tool to the toolbar
ve.init.mw.Target.static.toolbarGroups.push( {
// this will create a new toolgroup with the tools
// named in this include directive. The naem is the name given
// in the static property of the tool
include: [ 'extname' ]
} );
} )();
After installing the extension in your LocalSettings.php and starting VE, you should see a new tool in the toolbar with the given name. Clicking it will show an alert box with content "Hello". Like written in the inline comments: In the click handler (onSelect) you can do whatever you want, e.g. open a link in a new tab, e.g. to a Special page. To get the link to a special page I would suggest to use mw.Title to get a localized namespace. For example:
var relativeUrl = mw.Title.newFromText( 'RecentChanges', -1 ).getUrl();
The first parameter of mw.Title.newFromText() is the name of the page, the second parameter is the ID of the namespace (-1 is the default for special pages and should always work).
I hope that helps!
I am not sure I understand your question entirely. It is as simple as selecting some text, clicking the chain icon, then clicking the External Link tab and pasting your link there.