question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ModalDialogParams - allow ability to be tokenized, provide differently or remove

See original GitHub issue

In an attempt to integrate modal handling seamlessly across web + {N} shared codebase, ModalDialogParams presents a problem in the way it is currently designed.

Instead of:

const modalParams = new ModalDialogParams(options.context, closeCallback);

        const providers = ReflectiveInjector.resolve([
            { provide: Page, useValue: page },
            { provide: ModalDialogParams, useValue: modalParams },  // let's remove this
        ]);

Perhaps the params could be simply lifted off the ModalDialogService itself and do away with ModalDialogParams altogether to do something like this:

constructor(private modal: ModalDialogService) {}

public close() {
  let optionalArgs: any = {
    anyData: 'test'
  };
  this.modal.close(optionalArgs);
}

Could look something like:

@Injectable()
export class ModalDialogService {
    public static closeCallback: Function;
    public context: any;
    private containerRef: ViewContainerRef;

   // etc.

  public close(args) {
    this.context = undefined; // clear any previously passed context
    ModalDialogService.closeCallback(...args);
  }

  public showModal(type: Type<any>, options: ModalDialogOptions): Promise<any> {
        let viewContainerRef = options.viewContainerRef || this.containerRef;

        if (!viewContainerRef) {
            throw new Error("No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions.");
        }

        const parentPage: Page = viewContainerRef.injector.get(Page);
        const resolver: ComponentFactoryResolver = viewContainerRef.injector.get(ComponentFactoryResolver);
        const pageFactory: PageFactory = viewContainerRef.injector.get(PAGE_FACTORY);

        this.context = options.context; // allow shown components to access context

        return new Promise((resolve, reject) => {
            setTimeout(() => ModalDialogService.showDialog(type, options, resolve, viewContainerRef, resolver, parentPage, pageFactory), 10);
        });
    }

  private static showDialog(
        type: Type<any>,
        options: ModalDialogOptions,
        doneCallback,
        containerRef: ViewContainerRef,
        resolver: ComponentFactoryResolver,
        parentPage: Page,
        pageFactory: PageFactory): void {

        const page = pageFactory({ isModal: true, componentType: type });

        let detachedLoaderRef: ComponentRef<DetachedLoader>;
        ModalDialogService.closeCallback = (...args) => {
            doneCallback.apply(undefined, args);
            page.closeModal();
            detachedLoaderRef.destroy();
        };

        // etc.

Simplifying setup by only needing to work with 1 service ModalDialogService instead of 2. It would also make tokenized setups for code sharing between web + {N} more achievable.

If this makes sense, I can offer a PR for this soon.

/cc @vakrilov

Issue Analytics

  • State:open
  • Created 7 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
codebackcommented, Sep 21, 2017

Hi @vakrilov,

Thank you for your response. I’ve just managed to make this work using another approach. I’ ve created two files (services) ‘dialog-params.service.tns.ts’ and ‘dialog-params.service.ts’:

// dialog-params.service.tns.ts
export { ModalDialogParams } from 'nativescript-angular/directives/dialogs';
// dialog-params.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class ModalDialogParams {

  constructor(
    public context: any = {},
    public closeCallback: (...args) => any) {
  }

}

Then, with a gulp task, if I am building {N} app, I rename ‘dialog-params.service.tns.ts’ to ‘dialog-params.service.ts’.

In my custom dialog component I am importing it as follows:

import { Component, OnInit } from '@angular/core';

import { ModalDialogParams } from '../../../shared/core/index';
// import { ModalDialogParams as DialogParams} from 'nativescript-angular/directives/dialogs';

@Component({
  selector: 'app-product-modal',
  moduleId: module.id,
  templateUrl: './product-modal.component.html'
})
export class ProductModal implements OnInit {
  constructor(
    private _params: ModalDialogParams
  ) {}
...

Do you see any drawback in this approach?

0reactions
bracco23commented, Mar 26, 2019

For future reference, I made it working with something similar to @codeback solution. What I did was basically:

  1. Set up a service to open the dialog. The service is duplicated in a web version and a {N} version.

modalForm.service.tns.ts

import { Injectable, ViewContainerRef } from "@angular/core";
import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/modal-dialog";
import { ModalFormComponent } from "./modalForm.component";
import { Measurement } from "../model";
import { Observable, from } from "rxjs";

@Injectable()
export class ModalFormProvider{
    
    constructor(private _modalService: ModalDialogService){
    }

    open(viewRef: ViewContainerRef): Observable<Measurement>{
        const options: ModalDialogOptions = {
            viewContainerRef: viewRef,
            fullscreen: false,
            context: {}
        };
        return from(this._modalService.showModal(ModalFormComponent, options))
    }
}

modalForm.service.ts

import { Injectable, ViewContainerRef } from "@angular/core";
import { MatDialog, MatDialogConfig, MatDialogRef } from "@angular/material";
import { ModalFormComponent } from "./modalForm.component";
import { Measurement } from "weight-stats-common";
import { Observable } from "rxjs";
import { DIALOG_ID } from "./modalForm.params";

@Injectable()
export class ModalFormProvider{
    constructor(private diag: MatDialog){

    }

    open(viewRef: ViewContainerRef): Observable<Measurement>{
        let temp = this.diag.open<ModalFormComponent, any, Measurement>(ModalFormComponent, {
            viewContainerRef: viewRef,
            id: DIALOG_ID
        })
        return temp.afterClosed();
    }
}

I make sure to set a known ID to the dialog when opened with MatDialog, this step is important.

  1. Create a stub ModalDialogParams for the web version. So I have:

modalForm.params.tns.ts

export { ModalDialogParams } from "nativescript-angular/modal-dialog";

modalForm.params.ts

import { Injectable } from '@angular/core'
import { MatDialogRef, MatDialog } from '@angular/material/dialog';

export const DIALOG_ID: string = 'form_dialog';

@Injectable()
export class ModalDialogParams{
    constructor(private dialogs: MatDialog){
    }

    closeCallback(data?:any){
        this.dialogs.getDialogById(DIALOG_ID).close(data);
    }
}

On {N} I export the already present ModalDialogParams, while on web I create a custom version, added to the providers list on my web module, which uses the MatDialog service and the known ID to retrieve the MatDialogRef and invoke close.

  1. On my component, which is not duplicated:

modalForm.component.ts

import { Component, OnInit, ViewChild } from "@angular/core";
import { ModalDialogParams } from "./modalForm.params";

@Component({
    moduleId: module.id,
    selector: "modalForm",
    templateUrl: 'modalForm.component.html',
    styleUrls: ['modalForm.component.css']
})
export class ModalFormComponent implements OnInit {

    @ViewChild('formView') form: any;
    measurement: Measurement;

    constructor(private _params: ModalDialogParams) { }

    ngOnInit() {
    }

    onClose() {
        this._params.closeCallback("prova");
    }
}

Now when I invoke closeCallback the right chain of events is started and things are done as they should. I am using it in my repo https://github.com/bracco23/weight-stats-frontend/commit/a58287bd450a02afdf6e350efb081770c210042d

Read more comments on GitHub >

github_iconTop Results From Across the Web

The tokenization of real estate: An introduction to fractional ...
Ability to create liquidity. Real estate tokens are easily and securely transferable by way of blockchain technology, allowing investors to ...
Read more >
Tokenization in NLP: Types, Challenges, Examples, Tools
It's a crucial step for building an amazing NLP application. There are different ways to preprocess text: stop word removal,; tokenization,; stemming.
Read more >
Overview | Device Tokenization Developer Site
To enable Google Pay device tokenization support for your cards, ... The ability to delete or uninstall the Google Wallet app depends on ......
Read more >
The tokenization of assets is disrupting the financial industry
be about to fundamentally change with the arrival of tokenization. ... ICOs, which can produce different tokens ... tokenization provides for both investors....
Read more >
Chapter 2 Tokenization
2.1 What is a token? In R, text is typically represented with the character data type, similar to strings in other languages. Let's...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found