@mediacologne/ng-modal

- Updated Module to 7.0.0 to support angular 7.x.x and improve documentation

Stats

stars 🌟issues ⚠️updated 🛠created 🐣size 🏋️‍♀️
@mediacologne/ng-modal
Minified + gzip package size for @mediacologne/ng-modal in KB

Readme

News

  • Updated Module to 7.0.0 to support angular 7.x.x and improve documentation

Installation

Install the NPM Module

$ npm install @mediacologne/ng-modal

Using the Library

After the installation add the module to your application module

// Import your library
import { ModalModule } from '@mediacologne/ng-modal';

@NgModule({
  imports: [
    ModalModule
  ]
})
export class AppModule { }

Documentation

Import Module with default Configuration

If you wan't to provide some default configuration for all Modals in you application, you can import the ModalModule via the static forRoot() method and pass your defaultConfiguration as parameter.

You can define every possible config option here!
For example:

// Import your library
import { ModalModule } from '@mediacologne/ng-modal';

@NgModule({
  imports: [
    ModalModule.forRoot(<IModalConfig>{
        modal: {
            defaultConfig: {
                draggable: true,
                cssStyle: {
                    'width': '80vw',
                    
                    'modal-header': {
                        'height': '40px',
                        'border-color': '#2f2f2f'
                    },
                    'modal-title': {
                        'margin-top': '-10px',
                        'font-size': '1.1rem'
                    },
                    'modal-content': {
                        'background-color': '#535353'
                    }
                }
            }
        }
    }),
  ]
})
export class AppModule { }

All Possible configuration options:

{
    title: 'Modal Header',
    titleIcon: null,
    bodyIcon: null,
    body: '',
    width: '600px',
    template: null,
    draggable: false,
    backdropCloseable: false,
    cssStyle: {},
    component: <Component>,
    componentParams: {},
    onClose: null,
    btnOk: 'OK',
    btnCancel: 'Abbrechen',
    btnOkVisible: true,
    btnCancelVisible: true,
    buttons: [ // only allowed for Confirmation-Modals
        {result: 'yes', title: 'Yes', classes: ['btn-warning', 'btn-lg']},
    ],
    options: [ // only allowed for Choose-Modals
        {result: 'Sheldon Cooper', title: 'Sheldon Cooper'},
        {result: {firstname: 'Howard', name: 'Wolowitz'}, title: 'Howard Wolowitz'}
    ]
}

Concepts

Next step is to add the Component <modal-container></modal-container> before closing </body>

  ......
  <modal-container></modal-container>
</body>

The Modal Container Component is holding a list of opened Modals (nested or rather child Modals are supported).

Opening Modals

Alert

ModalUtils.alert({
    title: "Alert Title",
    body: "Body Text"
}).then((result: any) => {
    console.log(result);
});

Prompt

ModalUtils.prompt({
    title: "Prompt Title",
    body: "Body Text",
    promptValue: 'vordefinierter Wert',
}).then((result: any) => {
    console.log(result);
    if (result == false) {
        // Modal was canceled
    }
    else {
        // result => written text
    }
});

Confirm

ModalUtils.confirm({
    title: "Confirm Title",
    body: "Body Text"
}).then((result: any) => {
    console.log(result);
    if (result) {
        // Modal was closed positive
    }
    else {
        // Modal was closed negative
    }
});

Choose

ModalUtils.choose({
    title: "Choose Title",
    body: "Body Text",
    optionType: 'checkbox', // Supported: checkbox and dropdown; default: checkbox
    options: [
       {result: 'Sheldon Cooper', title: 'Sheldon Cooper'},
       {result: {firstname: 'Howard', name: 'Wolowitz'}, title: 'Howard Wolowitz', selected: true, disabled: false} // preselect an option via select or via checked
   ]
}).then((result: any) => {
    console.log(result); // Contains an Array with the selected results
    // for example: ['Sheldon Cooper', {firstname: 'Howard', name: 'Wolowitz'}]
   
});

Component

ModalUtils.component({
    title: "Component Title",
    component: ModalableTestComponent,
    componentParams: {
        email: "tom@local.int"
    }
}).then((result: any) => {
    console.log(result);
    if (result.closingResult) {
        // Modal was closed positive
        console.log(result.componentResult); // <-- this was returned by the Component
    }
    else {
        // Modal was closed negative
    }
});

Every Component which should be loadable inside a Modal must implements the Modalable Interface and it's onModalClose(closingResult: boolean) onModalOpen(params: any) Members. These Methods get's called on Modal opening and closing to pass data in or out.

Pass Component class as string

In some situations you may need to pass the component not as a class references but as a string name. To do so, you must register a dynamic component. See example below:

// Register your component
ModalUtils.registerDynamicComponent({'MyModalComponent': MyModalComponent});

// Open a dynamic registered modal component by string 
ModalUtils.component({
    title: "Component Title",
    component: 'MyModalComponent',
})

Icons

You can add Font Awesome Icons both to the Modal Header and the Modal Body.

Icon in Modal Header

To set an Icon inside the Modal Header, pass it as Font Awesome css class in titleIcon option

ModalUtils.alert({
    title: 'Attention!',
    titleIcon: 'fa fa-info'
})
Icon in Modal Body

If you pass a Font Awesome Icon as css class via bodyIcon option. The bodyIcon will be automatically in a flex row.

ModalUtils.alert({
    title: 'Attention!',
    bodyIcon: 'fa fa-info'
})

Custom CSS Style

If you have to change the modal CSS, you can provide custom styles via config.cssStyle.
For example change the default width_

ModalUtils.alert({
    cssStyle: {
        'width': '60vw'
    }
})

The config.cssStyle styles are applied to the div.modal-dialog. If you want to style other parts of the Modal, you can just add these parts as sub-objects like this:

ModalUtils.alert({
    cssStyle: {
        'modal-dialog': {
            'width': '60vw'            
        },
        'modal-header': {
            'background-color': 'red'
        }
    }
})

Draggable Modals

As default the draggable feature is disabled. If you want to make your Modals draggable, you have to install @angular/cdk library and import the DragDropModule (import {DragDropModule} from '@angular/cdk/drag-drop';). Enabling the draggable feature via config.draggable: true.

Additional Confirmation Buttons

If you need a Confirmation Modal but with more than an OK and Cancel button, you can pass a buttons: [] array in the following structure (see example below).
Attention: Per default the Confirmation Modal will return true or false as a boolean. If you use the buttons: [] array you have to set the result: any for each button which will the closingResult.

ModalUtils.confirm({
    title: "Confirm Title",
    body: "Do you wan't to delete?",
    buttons: [
        {result: 'yes', title: 'Yes', classes: ['btn-warning', 'btn-lg']},
        {result: 'no', title: 'No', classes: ['btn-primary', 'btn-lg']}, 
        {result: 'maybe', title: 'Maybe (disabled)', classes: ['btn-danger', 'btn-lg'], disabled: true},
        {result: 'yes_all', title: 'Yes for all'},
    ]   
}).then((result: any) => {
    console.log(result); // 'yes', 'no', 'yes_all', 'maybe' (is disabled and cannot occur)
});

Custom Templates

// xxx.component.ts file
@ViewChild('headerTemplate') headerTemplate: TemplateRef<any>;
@ViewChild('footerTemplate') footerTemplate: TemplateRef<any>;
@ViewChild('bodyTemplate') bodyTemplate: TemplateRef<any>;

ModalUtils.alert({
    template: {
        header: this.headerTemplate,
        footer: this.footerTemplate,
        body: this.bodyTemplate
    }
}).then((result: any) => {
    console.log(result);
});
// xxx.component.html file
<ng-template #headerTemplate let-context="context">            
    <h4 class="modal-title" style="color:red">Custom Modal Header!</h4>
</ng-template>

<ng-template #bodyTemplate>            
    <div class="alert alert-danger" role="alert">
        <strong>Oh snap!</strong> This is a Custom Modal Body
        <!-- How to access the Context? -->
        {{(context | async)?.yourProperty}}
    </div>
</ng-template>

<ng-template #footerTemplate let-modalCtrl="modalCtrl" let-context="context">
    <button type="button" class="btn btn-primary" (click)="modalCtrl.close(true)">Custom Footer Button OK</button>
    <button type="button" class="btn btn-primary" (click)="modalCtrl.close(false)">Custom Footer Button CANCEL</button>
</ng-template>

If you provide a modalFooter or a modalHeader (they must be ViewChilds), your custom Buttons are responsible for closing the Modal via modalCtrl (which is passed through ngTemplateOutletContext). Alternatively you can access your component e.g. public methods modalCtrl.component.customAction() which then is responsible for closing the Modal via the modalCtrl.

There are two ways to provide a custom footer or header template

    1. Provide a ViewChild reference as config.template.header or config.template.footer (the <ng-template> has to be placed into the parent component (in which you are open the modal))
    1. Don't provide a ViewChild via config.template.header or config.template.footer but define a @ViewChild('footerTemplate') footerTemplate: TemplateRef<any>; inside the the 'Modalable' component (the exact naming of the variable is important)
      The library scans your Modalable component for those variables and use them for custom footer/ header. If no such variables are found, the default footer/ header are shown

Completely Hide header/footer

If you want to completely hide the header/ footer, you have to pass false as config.template.header or config.template.footer

Access the Component context inside the custom header/ footer

In both, the custom header and footer, you can access the self-defined context of the component. Simply define the let-context="context" inside the <ng-template> (e.g. <ng-template #modalHeader let-context="context">) and you are able to access everything your own getContext() method returns (the method must be placed inside you component). Because the context is an Observable (BehaviourSubject), it must be piped through the async Pipe to access it's values inside your custom header/footer temaplte like this {{(context | async)?.yourProperty}}

Special case: Use the context inside a non component modal (like confirm or prompt)

If your modal is a prompt, alert or confirm, the let-context="context" gives you your config.context. This is because a non-confirm modal doesn't have a component which can implement the getContext() method.

Accessing Modal from Content Component via ModalCtrl

If the onModalOpen Method defines a modalCtrl argument, the holding Modal will pass it's ControlUnit (Ctrl) to it

onModalOpen(params: any, modalCtrl: any) {
    // Show Modal Config
    conosole.log("Active Modal Config", modalCtrl.config);

    // Modify Modal Config
    modalCtrl.config.btnOk = "Speichern";

    // Close the Modal with a positive result
    modalCtrl.close(true);
}

Close Modal on Event

If the closeOn is defined inside config and is a subscribable object, the Model get's closed on an Event reception

openAlertWithCloseOnEvent() {    
    // Define the event for closing the Modal
    let event: EventEmitter<any> = new EventEmitter();

    ModalUtils.alert({
      title: 'Ein Alert',
      body: "Alert Body Text",
      closeOn: event
    }).then(function (result) {
      console.log("Alert Result", result);
    });

    setTimeout(() => {
        // Trigger the event so the Modal will be closed immediatly
        event.next(true);
    }, 3000);
  }

OnOpen Event

If the OnOpen Callback is defined it will be called immediatly after the Modal will be shown. As argument the callback receives the ModalCtrl which can be used to control the Modal

openAlertWithOpenEvent() {
    ModalUtils.alert({
      title: 'Ein Alert',
      body: "Alert Body Text",
      onOpen: (modalCtrl) => {
        console.log("onOpen => modalCtrl", modalCtrl);
      }
    }).then(function (result) {
      console.log("Alert Result", result);
    });
  }

Developing the Library

Während der Entwicklung an der Library muss diese getestet werden, gebuilded werden sowie gepublished werden können. Hier steht wie's geht:


Testing

Simply run ng serve inside the root folder. The AppModule will be served and you're able to consume your library on the fly.


Building & Publishing

To build and publish the library you have to increase the version number inside the package.json (hint: you will find two package.json files. You have to increase the version number inside the ./projects/ng-modal-module/package.json and not the package.json inside the root folder!)
After pushing the commit to Gitlab, a Pipeline will start, build and publish your changes.

This automatic publishing will only start, if you increase the version number! If you push your changes without an increased version number nothing else than pushing will happen.

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.