I have a running Angular 9 application and I have created custom dialog box. I have also used ComponentFactoryResolver to dynamically load the component.
My custom dialog box looks like:
So, when I click on close button, the dialog box closes.
As per the current implementation, if I open multiple dialog box on the screen, then I am able to close only last opened dialog box by clicking on close button.
My expected behavior is to close all the dialog box. Please help me on this
Stackblitz demo: https://stackblitz.com/edit/dialog-box-overlay
Note: In this stackblitz demo, one modal opens on the top of another modal as I have not modified the css. So, please focus on Modal name to get to know which modal is opened
Instead of assigning the created modal component to the services dcRef property you need to manage all of your modal components, i.e. in a list. Your service's open() method
open(component: Type<any>, modalName: string) {
const factory = this.componentFactoryResolver.resolveComponentFactory(DialogComponent);
this.dcRef = factory.create(this.injector);
...
return this.dcRef;
}
returns the component reference. You could manage this reference from the caller and pass it as an argument to your close() method. When all of the component refs are managed by the service you can also "batch close" all modals (see closeAll()):
#Injectable()
export class DialogService {
refs: ComponentRef<DialogComponent>[] = [];
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private applicationRef: ApplicationRef,
private injector: Injector
) { }
open(component: Type<any>, modalName: string) {
const factory = this.componentFactoryResolver.resolveComponentFactory(DialogComponent);
var ref = factory.create(this.injector);
this.applicationRef.attachView(ref.hostView);
const domElement = (ref.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
document.body.appendChild(domElement);
ref.changeDetectorRef.detectChanges();
ref.instance.open(component, modalName);
this.refs.push(ref);
return ref;
}
close(ref) {
this.applicationRef.detachView(ref.hostView);
ref.instance.close();
ref.destroy();
// Remove ref from a list managed by the service
var i = this.refs.indexOf(ref);
this.refs.splice(i, 1);
}
closeAll()
{
this.refs.forEach(r => this.close(r));
}
}
This is not tested and might need to be tuned, but you should get the idea. Instead of using the ComponentRef as a handle you can also create some custom object to prevent the caller of the modal to interact with the component directly.
Related
I need to create something similar to what is shown in this image:
I don't think it's a modal. To me it looks more like a tooltip because it is displayed next to the control that is used to open it(calendar icon), but the difference is that I need to place other controls inside that sort of tooltip.
Can you think of any angular component (npm package) similar to what is display in this image?
Thanks.
We can create like this one using MatDialog. you can use your custom component inside MatDialog also.
constructor(
private dialog: MatDialog) {}
showDialog() {
const dialogConfig = new MatDialogConfig();
const dialogRef = this.dialog.open(YOURCUSTOMCOMPONENT, dialogConfig);
this.dialogRef.afterClosed().subscribe(
val => {
// Read values
});
}
Using a clarity datagrid version 2.3
Seeing an issue where if the user starts typing into the input field of datagrid column filter, the filter input focuses out automatically as soon as a key is pressed.
Since the datagrid is paginated and server driven, this causes the API to get fired as soon as a
key is pressed after the debounce time.
The automatic focus out of the input field cause the filter to only have a single character and the API gets triggered since the debouce is only 800.
Have looked at clarity github for any reported issues, doesn't look like its reported or anyone having similar issue.
Expected behavior should be the input focus out should not happend until the user moves the cursor away or presses enter, which is when the debounce should kickin after which the api should be called.
HTML:
<clr-datagrid
(clrDgRefresh)= refreshDataGrid($event)>
...
</clr-datagrid>
TS Component:
debouncer = new Subject<any>();
ngOnInit() {
this.debouncer.asObservable().pipe(
debounceTime(800)
).subscribe(state => {
// do something here.. like call an API to filter the grid.
})
}
refreshDataGrid(state) {
this.debouncer.next(state);
}
Any help is appreciated.
Currently I'm hacking my component, to make sure the focus is not lost on the input field until done so by the user.
refreshDataGrid(state) {
const isClrFilterInputField = document.querySelector('.datagrid-filter .clr-input');
if (isClrFilterInputField instanceof HTMLElement) {
isClrFilterInputField.focus();
}
this.debouncer.next(state);
}
This is still not a clean answer, but as far as I have searched, this seems like an issue with clarity datagrid itself, until I hear from someone with a cleaner answer.
Most likely the upgrade version might have this fixed.
Yet to check that.
Unfortunately I think we designed the datagrid to emit the changes on each filter value change with debouncing intended to be done on the app side as consumers see fit.
That said, it is possible to accomplish what you describe. I've implmented a quick and dirty guard based on events but there may be better ways. I'll add code snippets here and a link to the working stackblitz at the end.
You are on the right track with the debouncer. But we don't need to debounce with time, we only need to 'debounce' on certain events.
Instead of debouncing with time, what if we debounce with an #HostListener for clicks on the filter input? (I'll leave it as an exercise for you to implement a HostListener for the focusin event since focusin bubble's up and blur does not). To do that we need:
A Hostlistener that can hear keydown.enter event on the filter input
A guard to prevent requests
A property to store the datagrid state as user enters text
In general the code needs to:
Fetch data when component inits but not after unless directed
Keep track of state events that get emitted from the datagrid
listen to keydown.enter events (and any other events like the filter input focusout - becuase it bubbles up, unlike blur)
Check that the event was generated on a datagrid filter input
dismiss the guard
make the request
re-enlist the guard
Here is a rough attempt that does that:
export class DatagridFullDemo {
refreshGuard = true; // init to true to get first run data
debouncer = new Subject<any>(); // this is now an enter key debouncer
datagridState: ClrDatagridStateInterface; // a place to store datagrid state as it is emitted
ngOnInit() {
// subscribe to the debouncer and pass the state to the doRefresh function
this.debouncer.asObservable().subscribe(state => {
this.doRefresh(state);
});
}
// a private function that takes a datagrid state
private doRefresh(state: ClrDatagridStateInterface) {
// Guard against refreshes ad only run them when true
if (this.refreshGuard) {
this.loading = true;
const filters: { [prop: string]: any[] } = {};
console.log("refresh called");
if (state.filters) {
for (const filter of state.filters) {
const { property, value } = <{ property: string; value: string }>(
filter
);
filters[property] = [value];
}
}
this.inventory
.filter(filters)
.sort(<{ by: string; reverse: boolean }>state.sort)
.fetch(state.page.from, state.page.size)
.then((result: FetchResult) => {
this.users = result.users;
this.total = result.length;
this.loading = false;
this.selectedUser = this.users[1];
// Set the guard back to false to prevent requests
this.refreshGuard = false;
});
}
}
// Listen to keydown.enter events
#HostListener("document:keydown.enter", ["$event"]) enterKeydownHandler(
event: KeyboardEvent
) {
// Use a host listener that checks the event element parent to make sure its a datagrid filter
const eventSource: HTMLElement = event.srcElement as HTMLElement;
const parentElement = eventSource.parentElement as HTMLElement;
if (parentElement.classList.contains("datagrid-filter")) {
// tell our guard its ok to refresh
this.refreshGuard = true;
// pass the latest state to the debouncer to make the request
this.debouncer.next(this.datagridState);
}
}
refresh(state: ClrDatagridStateInterface) {
this.datagridState = state;
this.debouncer.next(state);
}
}
Here is a working stackblitz: https://stackblitz.com/edit/so-60980488
I am creating a custom confirmation dialog in Google App Maker and would like the Confirm button to call a passed-in function. I don't see an "onclick" event in the button widget. Any suggestions on how to do this?
function confirmationDialog(msg, confirmFunction)
{
var desc = app.pageFragments.ConfirmationDialog.descendants;
var label = desc.Label;
var confirmButton = desc.Confirm;
label.text = msg;
confirmButton.onClick = confirmFunction; // does not work
app.showDialog(app.pageFragments.ConfirmationDialog);
}
Thanks
It'd be great if this was a bit easier, but the best bet is to use Custom Properties (https://developers.google.com/appmaker/ui/viewfragments).
You can set up a custom property of type "Dynamic" and call it anything, take "onConfirmCallback", for example. Then you can set the function on that custom property:
Code to invoke dialog:
app.pageFragments.ConfirmationDialog.properties.onConfirmCallback = function(param) {
alert(param);
};
app.showDialog(app.pageFragments.ConfirmationDialog);
And then in the onClick for the close button:
app.pageFragments.ConfirmationDialog.properties.onConfirmCallback("hi");
app.closeDialog();
Also note that there are slightly better ways to set up labels than in your example, also using custom properties.
Create custom properties for any widget properties you want to customize, and then bind those custom properties (#properties.propertyName) to the widget property. For example you might have a confirmText property, with the confirm buttons text property boudn to #properties.confirmText.
Then when you invoke your dialog, you can just set those custom properties. Quick modification of your example code using properties for everything:
function confirmationDialog(msg, confirmFunction)
{
var properties = app.pageFragments.ConfirmationDialog.properties;
properties.text = msg;
properties.confirmCallback = confirmFunction;
app.showDialog(app.pageFragments.ConfirmationDialog);
}
For my confirmation dialogs, I just set the onclick of the OK button before I show the dialog (everything is in one place, which is easier for the dummy (me) who will have to maintain it in six months:
var dialog=app.pages.ConfirmationDialog;
dialog.descendants.message.text='Are you sure...?'
dialog.descendants.btnOk.getElement().onclick=function(){
//do something here
app.closeDialog();
});
};
app.showDialog(dialog);
}
I am customizing the ribbon tool bar by adding a button to it in TRIDION 2011 SP1 version.
When I click on the button it will open an aspx page.Inside that aspx page I need to access the name of the schema used to create that component before creating the component itself(I mean to say while creating the component itself).
Please provide me a way to solve this issue. Thanks in advance.
You should pass it to your popup. The URI of the Schema is available on the Component model object within the CME - so your button command can access it and pass it to the popup (in the URL, for example).
var schemaId = $display.getView().getItem().getSchemaId();
If you have the component (as an object), you can get it's schema id as Peter indicated. If you only have the component id, you can load the component and through that get to the schema.
When you need to load any item, you have to be aware that it's not a synchronous call in the UI API, so you should use delegate methods for that. For example something like this:
Example.prototype._loadItemInformation = function Example$_loadItemInformation(itemId, reload) {
var item = $models.getItem(itemId);
if (item) {
var self = this;
function Example$_loadItemInformation$_onItemLoaded() {
$evt.removeEventHandler(item, "load", Example$_loadItemInformation$_onItemLoaded);
// proceed with the actions on the loaded item here
};
if (item.isLoaded(true) && !reload) {
Example$_loadItemInformation$_onItemLoaded();
}
else {
$evt.addEventHandler(item, "load", Example$_loadItemInformation$_onItemLoaded);
//$evt.addEventHandler(item, "loadfailed", Example$_loadItemInformation$_onItemLoadFailed);
item.load(reload, $const.OpenMode.VIEW);
}
}
};
Also be aware the item could fail loading, you should actually also register an event handler for loadfailed (as my example code neglects to do).
I have the following AS code.I have noticed that if an Application i s using the webcamera then it cannot be used by any secondary applications until unless the primary application is closed.
My question is that from the following code 1.can we capture that condition
2.If no camera is detected how to give the alert since it is an AS code
EDIT:
Filename is cldAS.as
Now how to call cldAS() from any.mxml file .Some example would be appreciated
package org.com
{
import flash.display.Sprite;
import flash.media.*;
import flash.net.*;
public class cldAS extends Sprite
{
public function cldAS()
{
var cam:Camera = Camera.getCamera();
if(cam != null)
{
cam.setMode(640, 480, 30);
var video:Video = new Video(300, 450);
video.attachCamera(cam);
addChild(video);
}
else
{
trace("No Camera Detected");
//How to give an alert here
}
}
}
}
Alert.show("You don't seem to have a webcam.");
instead of
trace(...) ?
Alert is available in Flex only , in AS3 you should really implement your own solution, on the other hand , since Alert is a Javascript function , you could also use ExternalInterface to call it.
As far as implementing your own solution is concerned, at the minimum you need a TextField to display your message, which text you could provide by sending a CustomEvent with a message property that will simply take a String. It wouldn't take too much work to create your own Alert class.It would sit on top of your App , you could toggle visibility when receiving a CustomEvent and have a Close button to hide it.
You should be able to call your AS3 class within script tags , other than that I'll leave a more detailed answer to Flex experts. I'm not sure if you can add a Sprite directly into Flex , for all I remember an object in Flex must inherit from UIComponent in order to be added to the stage but check with the other guys here, I haven't used Flex in quite some time...
<mx:Script>
import org.com.cldAS;
public cld:cldAS = new cldAS();
</mx:Script>