npm package: @mediacologne/ng-modal
Description: - Updated Module to 7.0.0 to support angular 7.x.x and improve documentation
Latest version: 13.1.0

# News

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

# Installation

## Install the NPM Module

```bash
$ npm install @mediacologne/ng-modal
```

## Using the Library

After the installation add the module to your application module

```typescript
// 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:

```typescript
// 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:

```typescript
{
    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>`

```xml
  ......
  <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
```typescript
ModalUtils.alert({
    title: "Alert Title",
    body: "Body Text"
}).then((result: any) => {
    console.log(result);
});
```


### Prompt
```typescript
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
```typescript
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
 ```typescript
 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
```typescript
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:
```typescript
// 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
```typescript
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.
```typescript
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_
```typescript
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:

```typescript
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.
```typescript
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
```typescript
// 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);
});
```

```html
// 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))
* 2) 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

```typescript
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

```typescript
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

```typescript
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/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.**