The way I’m doing compose is this. I have a tag on the view that I’m watching to make sure it’s ready and it is called body
on my viewModel
bodyChanged() {
if (this.model.type === DialogType.custom) {
let context = this.createContext((<CustomModel>this.model).bodyViewModel, this.body, (<CustomModel>this.model).model);
let _this = this;
this.compositionEngine.compose(context).then((view: any) => {
_this.viewRef = view.viewModel;
if ((<CustomModel>_this.model).footerViewModel) {
_this.composeFooter();
}
});
}
}
createContext(viewModel: object, host: Element, model: object): CompositionContext {
return {
container: this.container.createChild(),
viewModel: viewModel,
model: { dialogContainer: this, model: model },
host: host,
bindingContext: null,
viewResources: null,
viewSlot: new ViewSlot(host, true)
};
}
I am storing reference to the created viewModel because currently I need that for other work I’m doing but I’m trying to eliminate that need . I need to refactor my code so it’s more evident what (<CustomModel>this.model).model
means. this.model
is the object directly from the activate function and that object has it’s own model which is the model for my view to be composed.
My view looks something like this at the moment. I am working to eliminate the separate need for a footer view. My current WORKING implementation has the body as a separate view/viewModel and if a custom footer is desired that gets it’s own view/viewModel. The footer then gets the body viewmodel injected so i can handle logic with the buttons nicely. I could let this custom view contain the footer, but my goal is to not require a developer to have to create buttons each time.
If you see my most recent post about content projection, I am trying to make a wrapper for the custom case. The footer would be default buttons like I pasted above unless overridden, but I can handle closing the dialog with less code than I currently need.
<template>
<require from="./style.css"></require>
<require from="./customContainer"></require>
<ux-dialog ref="dialogRef" with.bind="model">
<ux-dialog-header style="padding:5px;" if.bind="title">
<div roles="alert" style="padding:5px; margin-bottom:2px; margin-top:2px;">
<div style="float: left;">
<span style="font-size: 24px; font-weight:bold; ">${title}</span>
</div>
<div style="float:right">
<loading-spinner loading.bind="isLoading"></loading-spinner>
<a if.bind="showCloseButton" style="cursor: pointer;" id="ux-dialog-header-close" title="Close" aria-hidden="true"
aria-label="Close" click.trigger="cancel()"><i class="fal fa-times"></i> </a>
</div>
<div style="clear:both;"></div>
</div>
</ux-dialog-header>
<!-- This is where the custom view gets injected -->
<div if.bind="type===0" element.ref="body"></div>
<template else>
<ux-dialog-body class="ux-dialog-body" id="ux-dialog-body" css="width: ${width}px; height: ${height}px;" style="overflow:auto; padding:5px;">
<div class="container-fluid" if.bind="type !== 0" style="min-width:415px;">
<div class="row" if.bind="iconName">
<div class="col col-11 align-self-center" innerhtml.bind="message | sanitizeHTML">
</div>
<div class="col col-1 align-self-center">
<i class="fal fa-${iconName} ${iconSizeString}"></i>
</div>
</div>
<div else innerhtml.bind="message | sanitizeHTML">
</div>
</div>
</ux-dialog-body>
<ux-dialog-footer>
<div element.ref="footer" if.bind="!hideFooter">
<template if.bind="!footerViewModel">
<button id="dgCancel" if.bind="type === 2 || (type === 0 && cancelButtonVisible)" disabled.bind="cancelDisabled"
click.trigger="cancel()" class="btn btn-default" type="button">${cancelText}
<b if.bind="autoCloseCancels && count">${countdown}</b>
</button>
<button id="dgOk" disabled.bind="okDisabled" click.trigger="ok()" type="submit" class="btn btn-primary">
${okText} <b if.bind="!autoCloseCancels && count">${countdown}</b>
</button>
</template>
</div>
</ux-dialog-footer>
</template>
</ux-dialog>
</template>
I can’t post the entire project unless I get permission. I am trying…
My alert signature looks like this
public alert(title: string, message: string, openDialogPromise: boolean, settings?: AlertSettings, immediate?: boolean): Promise<AlertDialog>;
public alert(title: string, message: string, settings?: AlertSettings, immediate?: boolean): Promise<void>;
public alert(title: string, message: string, arg2: any, arg3?: any, arg4?: any): Promise<any>
Here is my confirm
public confirm(title: string, message: string, openDialogPromise: boolean, settings?: ConfirmSettings, immediate?: boolean): Promise<ConfirmDialog>;
public confirm(title: string, message: string, settings?: ConfirmSettings, immediate?: boolean): Promise<boolean>;
public confirm(title: string, message: string, arg2: any, arg3?: any, arg4?: any): Promise<any> {
I have a variety of settings for each that control the header, icons for alert/confirm. The ability to get the dialogPromise back.
Other methods on my dialogFactory class include the ability to externally enable disable ok/cancel, close the open dialog and destroy and queued ones, show a loading/status icon and the ability to update a dialogs title
I wanted to work async await with my dialogs so I wrap the actual dialogservice.open in my own promise implementation
My custom dialogs works the same as the above 2 except it will return the object you are expecting back.
This lets me just await an alert for close if I want, or to await a true or false out of my confirm. I also have queuing logic in place that lets the dialogs wait so I don’t end up with dialogs on top of each other unless I want to (This is more about protection, myself and the rest of us better not need the stacking feature. I hate nested modals)