npm package: @mediacologne/ng-utility
Latest version: 13.2.0

# @mediacologne/ng-utility


# News


- Achtung! Dies ist eine Angular >= 6 Library!


# Installation

## Install the NPM Module

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

## Using the Library

Nach der Installation muss die Library durch das Importieren des `UtilityModule` verfügbar gemacht werden.

```typescript
// Import your library
import { DataModule } from '@mediacologne/ng-utility';

@NgModule({
  imports: [
    UtilityModule.forRoot({
        logging: {
            enableConsoleRedirection: true,
            enableRemote: true,
            appId: "ApplicationName"
        }
    }),
  ]
})
export class AppModule { }
```

### Polyfills !

Wichtig ist das Hinzufügen folgender Zeile in die `./src/polyfills.ts` Datei:

```typescript
(window as any).global = window;
``` 


# Documentation


## Logging

tbw;


## LoadingIndicator

Show a *LoadingIndicator* with `UiUtil.showLoadingIndicator("Einen Moment bitte");` and hide it via `//UiUtil.hideLoadingIndicator();`
**Attention:** The old way of showing and hiding a LoadingIndicator via `UiUtil.toggleLoadingIndicator(true, "Einen Moment bitte");` is **deprecated!**


#### LoadingIndicator for asynchronous Operations in loops

If you have multiple (for example) save operations inside a loop, which are obviously asynchronous, it's difficult to show and hide the LoadingIndicator.  
With the `incrementIndicator` you can define the count of operations and then show the *LoadingIndicator*

```typescript
UiUtil.incrementIndicator(3).then(() => {
    console.log("All Operations are finished");
});
UiUtil.showLoadingIndicator("Einen Moment bitte");
```

Inside the (for example) `subscribe` method of every (save) operation you have to call the `decrementIndicator` method.
After the internal increment counter has reached 0, the *LoadingIndicator* will disappear and additionally/optionally the **Promise** of the `incrementIndicator` will be called.



## ContextMenu

This UtilityModule exports a simple to use ContextMenu based on MatMenu. The following is an example on how to use a simple ContextMenu.

```html
<ul>
    <li [contextmenu]="'mainMenu'" [contextmenuData]="{id: 1, name: 'Tom'}">
        Right click on me
    </li>
</ul>
```
This is the *TriggerElement* which opens the ContextMenu on right click. The `[contextmenu]="'mainMenu'"` specifies which ContextMenu should be opened.
The `[contextmenuData]="{id: 1, name: 'Tom'}"` gives you a way to pass data into the ContextMenu.


```typescript
<contextmenu [name]="'mainMenu'">
    <ng-template contextmenuContent let-user="contextmenuData">
        <button mat-menu-item (click)="console.log('Click (call the delete method)!', user);">
            <i class="fa fa-trash"></i>
            <span>Löschen</span>
        </button>
    </ng-template>
</contextmenu>
``` 

This is the ContextMenu. The `[name]="'mainMenu'"` specifies the *name* of the ContextMenu which is used by the *TriggerElement*.
Inside the `<ng-template contextmenuContent let-user="contextmenuData">` you can put you ContextMenu content.
It's important that the `<ng-template>` has the `contextmenuContent` diretive! Via `let-user="contextmenuData"` you can catch the provided `[contextmenuData]` from the *TriggerElement*.
The naming of `let-user` is yours!


#### Sub Menus
If you want a nested ContextMenu like MatMenu allready supports, I'm sad to tell you that it isn't possible with `MatMenu` (https://github.com/angular/material2/issues/11880).
But! You can open a second ContextMenu inside the ContextMenu with some hacks.


```html
<!-- This is the MainMenu -->
<contextmenu [name]="'mainMenu'">
    <ng-template contextmenuContent let-user="contextmenuData">
        <!-- This button is the TriggerElement* for a second ContextMenu named 'subMenu' -->
        <button mat-menu-item [contextmenu]="'subMenu'" [contextmenuData]="user" [contextmenuEvent]="'click'" [contextmenuStayOpen]="true">Open SubMenu...</button>

    </ng-template>
</contextmenu>

<!-- This is the SubMenu -->
<contextmenu [name]="'subMenu'">
    <ng-template contextmenuContent let-user="contextmenuData">
        <button mat-menu-item [contextmenuForceClose]="'mainMenu'"
                (click)="console.log('SubMenu Clicked')">
            <i class="fa fa-database"></i>
            <span> {{user.name}}</span>
        </button>
    </ng-template>
</contextmenu>
```

Notice the special directives which are necessary if you want a SubMenu!
The `[contextmenuEvent]="click"` and `[contextmenuStayOpen]="true"` at the *TriggerElement* and the `[contextmenuForceClose]="'mainMenu'"` on the ContextMenu.


## Socket Broker

This module comes with built in support for the Socket Broker ([mediacologne/internal/application-management-platform/socket-broker](mediacologne/internal/application-management-platform/socket-broker)).  
To enable the feature set the `enabled` Flag to `true`

```typescript
UtilityModule.forRoot({
    socket: {
        appId: 'appId', // replace with your AppId
        enabled: true
    }
})
```

You can inject the `brokerService: BrokerService` into your component and start listening for new events with `this.brokerService.subscribe('yourEventName', (data) => {})` or emit a new
Event with `this.brokerService.publish('yourEventName', {your: 'data'});`

### Associates
The default behaviour of the Socket Broker (on the server-side) is to broadcast all events to all clients with the same `appId`.
If your want to `subscribe` or `publish` events only from/ to specific clients, there are different reasons why this is not possible. For example: you will never
know the server-side Client-Socket-ID of your target, which should receive your event...

In a situation where you want to build something like a private chat between two specific users, the concept of `associates` becomes important.
Every event can be associated. These associates are something like **tagged events** which enables a more precise event targeting.
Both, the `subscribe` and `publish` methods accept a third parameter of a string array, containing the `associates` (imagine *tags*).

```typescript
/**
* Example Client 'Tom'
*/
const loggedInUser = 'tom';
// Associate the chatMessage event with 'sender=tom' and 'receiver=lisa'
brokerService.publish('chatMessage', {messag: 'Hello Tom'}, ['sender='+loggedInUser, 'receiver=lisa']);


/**
* Example Client 'Lisa'
*/
const loggedInUser = 'lisa';
brokerService.subscribe('chatMessage', (data) => {
    console.log('received a private chatMessage for Lisa => ' + data.message);
}, ['receiver='+loggedInUser]);
```

#### Default Associates
You can set an array of default `associates` which are always merged with your given `associates` and are used in all `.publish()` and `.subscribe()` usages.  
To set the default `associates` you can call the static `BrokerService.setDefaultAssociates([])`.

You can also use `BrokerService.setDefaultPublishingAssociates([])` and `BrokerService.setDefaultSubscribingAssociates([])` to differ between sending and receiving.