Angular2Angular2 V4 Publish/Subscribe Tutorial for Realtime Apps

PubNub utilizes a Publish/Subscribe model for real-time data streaming and device signaling which lets you establish and maintain persistent socket connections to any device and push data to global audiences in less than ¼ of a second.
The atomic components that make up a data stream are API Keys, Messages, and Channels.
To build an application that leverages the PubNub Network for Data Streams with Publish and Subscribe, you will need PubNub API Keys which we provide when you Sign-Up.

You will need at the minimum a subscribeKey and publishKey. If a client will only subscribe, and not publish, then the client only need to initialize with the subscribeKey. For clients who will be publishing only, or publishing and subscribing (a client can both publish and subscribe), it will need to initialize with both the subscribeKey and the publishKey.

You only need to supply the publishKey to clients that will publish (send) data to your application over the PubNub network. A read-only client for example would not need to initialize with this key.

 
Although a secretKey is also provided to you along with your publish and subscribe keys in the admin portal, it is not required for plain-old publish and subscribe. You'll only need the secretKey if you are using PAM functionality, which we discuss more in the PAM Feature Tutorial.
A message consists of a channel, and its associated data payload. A publishing client publishes messages to a given channel, and a subscribing client receives only the messages associated with the channels its subscribed to.

PubNub Message payloads can contain any JSON data including Booleans, Strings, Numbers, Arrays, and Objects. Simply publish the native type per your platform, and the clients will JSON serialize the data for you. Subscribers will automatically deserialize the JSON for you into your platform's associated native type.
 
When creating a message, keep these limits in mind:
  • Maximum message size is 32KB
  • The message size includes the channel name
  • The message size is calculated after all URL encoding and JSON serialization has occured. Depending on your data, this could add > 4X the original message size.

Keeping your messages < 1.5KB in size will allow them to fit into a single TCP packet!

For further details please check: https://support.pubnub.com/support/discussions/topics/14000006322

Channels are created on-the-fly, and do not incur any additional charges to use one or many in your application. When you create a PubNub application, all messages will be associated with a channel.

In a unicast (AKA 1:1) design pattern, the channels can be unique for each client in one-to-one communication. For example, user1 subscribes to user1-private, and user2 subscribes to user2-private. Using this pattern, each client listens on a channel which only relevant data to that client is sent. It has the advantage of minimal network usage (each client receives only the data it needs) and minimal processing (no need for filtering unneeded data).

PubNub Galaxy                                                                                                                            
In a multicast (AKA 1:Many) design pattern, a public (AKA system, global, or admin) channel is used for global communications amongst all clients. For example, building off our previous example, while a user can speak to any other user on their private channel, since each client in the application is listening on their private channel AND the public channel, they can receive on either. When receiving a message on the public channel, it may or may not be relevant for that particular receiving client -- to get around this, the client can filter on some sort of key, allowing them to selectively process messages with specific interest to them.

PubNub Pulse
In many cases, based on the use case of your application, the pattern you choose may be unicast, multicast, or a combination. There is no right or wrong pattern to implement, but based on your use case, there may be an optimal, most efficient pattern.
Since the text length of the channel name is counted as part of the entire message, and as such, as part of the maximum message length, it is best to keep the channel name as short as efficiency and utility allows.

Channel names are UTF-8 compatible. Prohibited chars in a channel name are:
  • comma: ,
  • slash: /
  • backslash: \
  • period: .
  • asterisks: *
  • colon: :
The procedure to publish and subscribe is really quite simple:

  • Include the PubNub Javascript and Angular2 libraries
  • Register PubNub-Angular2
  • Inject PubNub-Angular2
  • new PubNubAngular() - instantiate a PubNub instance.
  • subscribe() - subscribe to a specific channel.
  • publish() - send a message on a specific channel.
  • unsubscribe() - additively unsubscribe to a specific channel.

PubNub Angular2 is wrapper for PubNub Javascript SDK v4 that adds a few extra features to simplify the integration to Angular v2 and v4.

  • Support: Available to use with Typescript or Javascript plain.
  • Events: Delegate methods accept the triggerEvents option which will broadcast certain callback and binding these directly to the HTML.
  • Autoload: An easy and fast way to recovery the history messages of your channel.
  • Multiple instance behavior: All instance are accessible throughout application via PubNub service.

You can still use the native Pubnub JavaScript SDK if you feel this will be more suitable for your situation.

npm install --save pubnub pubnub-angular2

You have to two ways for registering PubNubAngular in your list of providers in your Angular App and these are in: ngModules or ngComponents.

Angular Module lets you to use the same instance of PubNub in all Angular Components declared in this. Then you will not need to register PubNub in any Angular Component hosted by the Angular Module.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { PubNubAngular } from 'pubnub-angular2';
import { AppComponent } from './appComponent';
@NgModule({
    imports:[ BrowserModule ],
    declarations:[ AppComponent ],
    providers:[ PubNubAngular ],
    bootstrap:[ AppComponent ]
})
export class AppModule {
    constructor() {}
}

Angular Component only lets to use the same instance of PubNub inside itself and its Angular Components children. For more information about this we recommend to visit Depedency Injection and Hierarchical Dependency Injectors.

import { Component } from '@angular/core';
import { PubNubAngular } from 'pubnub-angular2';
@Component({
    selector: 'appComponent',
    template: '<H1>PubNub Angular2 SDK Demo</H1>',
    providers:[ PubNubAngular ]
})
export class AppComponent {
    constructor() {}
}

Now you can inject PubNubAngular in your ngComponents

import { Component } from '@angular/core';
import { PubNubAngular } from 'pubnub-angular2';
@Component({
    selector: 'appComponent',
    template: '<H1>PubNub Angular2 SDK Demo</H1>'
})
export class AppComponent {
    constructor(pubnub: PubNubAngular) {
    pubnub.init({
        publishKey: 'YOUR PUB_KEY',
        subscribeKey: 'YOUR SUB_KEY'
        });
    }
}

For Angular2 you have to add some extra steps in order to setup the environment, Your HTML page will have to include the next libraries.

  • Global dependencies for Angular2
  • Angular2
  • PubNub Javascript SDK
  • PubNub Angular2 SDK

With Javascript is possible to use the libraries from CDN, NPM and Bower.

  1. Include global dependencies for Angular2:
     
                <script src="node_modules/core-js/client/shim.min.js"></script>
                <script src="node_modules/zone.js/dist/zone.js"></script>
                <script src="node_modules/reflect-metadata/Reflect.js"></script>
                <script src="node_modules/rxjs/bundles/Rx.js"></script>
            
  2. Include Angular2
                <script src="node_modules/@angular/core/bundles/core.umd.js"></script>
                <script src="node_modules/@angular/common/bundles/common.umd.js"></script>
                <script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
                <script src="node_modules/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
                <script src="node_modules/@angular/forms/bundles/forms.umd.js"></script>
                <script src="node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
            
  3. Include the lastest version of PubNub's Javascript SDK:
     
                    <script src="node_modules/pubnub/dist/web/pubnub.js"></script>
            
  4. Include PubNub's Angular2 SDK:
     
                    <script src="node_modules/pubnub-angular2/dist/pubnub-angular2.js"></script>
            

Your HTML page will have to include the same libraries described above but you have to load the Angular2 and PubNub Angular2 SDK from NPM modules.

  1. Include global dependencies for Angular2:
     
                <script src="node_modules/core-js/client/shim.min.js"></script>
                <script src="node_modules/zone.js/dist/zone.js"></script>
                <script src="node_modules/reflect-metadata/Reflect.js"></script>
                <script src="node_modules/rxjs/bundles/Rx.js"></script>
            
  2. Include the lastest version of PubNub's Javascript SDK:
     
                    <script src="node_modules/pubnub/dist/web/pubnub.js"></script>
            
  3. Include and load libraries from systemjs:
     
                <script src="node_modules/systemjs/dist/system.src.js"></script>
                <script src="systemjs.config.js"></script>
                <script>
                    System.import('app').catch(function(err){ 
                        console.error(err);
                    });
                </script>
            

Angular2 uses systemjs.config.js and this will have to include next libraries inside the map attribute.

  • Rxjs
  • Angular2
  • PubNub Angular2 SDK
    map: {
        'rxjs': 'npm:rxjs',
        '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
        '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
        '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
        '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
        '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
        '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
        '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
        '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
        'pubnub-angular2': 'npm:pubnub-angular2/dist/pubnub-angular2.js'
    }
    

You have to two ways for registering PubNub Angular2 SDK in your list of providers in your Angular App and these are in: Angular Modules or Components.

Angular Module lets you to use the same instance of PubNub in all Angular Components declared in this. Then you will not need to register PubNub in any Angular Component hosted by the Angular Module.

    'use strict';

    (function (app) {
        app.appModule = ng.core.NgModule({
            imports: [ng.platformBrowser.BrowserModule],
            declarations: [app.appComponent],
            providers: [ PubNubAngular ],
            bootstrap: [app.appComponent]
        }).Class({
            constructor: function(){}
        });

        document.addEventListener('DOMContentLoaded', function(){
            ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(app.appModule);
        });
    })(window.app || (window.app = {}));
    
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { PubNubAngular } from 'pubnub-angular2';
    import { AppComponent } from './appComponent';
    @NgModule({
        imports:[ BrowserModule ],
        declarations:[ AppComponent ],
        providers:[ PubNubAngular ],
        bootstrap:[ AppComponent ]
    })
    export class AppModule {
        constructor() {}
    }
    

Angular Component only lets to use the same instance of PubNub inside itself and its Angular Components children. For more information about this we recommend to visit Dependency Injection and Hierarchical Dependency Injectors

    (function (app) {
        app.appComponent = ng.core.Component({
            selector: 'appComponent',
            template: '<H1>PubNub Angular2 SDK Demo</H1>'
            providers: [ PubNubAngular ]
        }).Class({
            constructor: function () {}
        });
    })(window.app || (window.app = {}));
    
    import { Component } from '@angular/core';
    import { PubNubAngular } from 'pubnub-angular2';
    @Component({
        selector: 'appComponent',
        template: '<H1>PubNub Angular2 SDK Demo</H1>',
        providers: [PubNubAngular]
    })
    export class AppComponent {
        constructor() {}
    }
    

Registering PubNubAngular within the provider list in an Angular Module or Component lets to inject an instance the PubNub in an Angular Component according to the Hierarchical Dependency, then this will be a shared instance.

After injecting the PubNub instance in the Angular Component's constructor will have to initialize the service including the PUB_KEY and SUB_KEY. It is important to know that this process has to be done once because this instance will be shared by all Angular Components within same scope according to its dependency model choose.

    (function (app) {
        app.appComponent = ng.core.Component({
            selector: 'appComponent',
            template: '<H1>PubNub Angular2 SDK Demo</H1>'
        }).Class({
            constructor: [PubNubAngular, function(pubnub){
                pubnub.init({
                    publishKey: 'YOUR PUB_KEY',
                    subscribeKey: 'YOUR SUB_KEY'
                });
            }]
        });
    })(window.app || (window.app = {}));
    
    import { Component } from '@angular/core';
    import { PubNubAngular } from 'pubnub-angular2';
    @Component({
        selector: 'appComponent',
        template: '<H1>PubNub Angular2 SDK Demo</H1>'
    })
    export class AppComponent {
        constructor(pubnub: PubNubAngular) {
            pubnub.init({
                publishKey: 'YOUR PUB_KEY',
                subscribeKey: 'YOUR SUB_KEY'
            });
        }
    }
    

To learn about PubNub JavaScript features refer to native PubNub JavaScript SDK manual. All methods of this SDK are wrapped with PubNubAngular.

Native PubNub JavaScript SDK provides instance creation using Pubnub.init(), which returns new instance with given credentials. In PubNub Angular2 SDK instances are hidden inside service and are accessible via instance getter. Methods of default instance are mapped directly to PubNub service just like Pubnub.publish({...}). In most use cases usage of the only default PubNub instance will be enough, but if you need multiple instances with different credentials, you should use Pubnub.getInstance(instanceName) getter. In this case publish method will looks like Pubnub.getInstance(instanceName).publish({}).

    var defaultInstance = new PubNub({
        publishKey: 'YOUR PUB_KEY',
        subscribeKey: 'YOUR SUB_KEY'
    });

    var anotherInstance = new PubNub({
        publishKey: 'ANOTHER PUB_KEY',
        subscribeKey: 'ANOTHER SUB_KEY'
    });

    defaultInstance.publish(
        {
            message: {such: 'Hello!'},
            channel: 'my_channel'
        },
        function (status, response) {
            if (status.error) {
                console.log(status);
            } else {
                console.log("message Published w/ timetoken", response.timetoken);
            }
        }
    );

    anotherInstance.grant(
        {
            channels: ['my_channel'],
            authKeys: ['my_authkey'],
            read: true,
            write: false
        },
        function (status) {
            console.log(status);
        }
    );
    
    declare var PubNub: any;

    var defaultInstance = new PubNub({
        publishKey: 'YOUR PUB_KEY',
        subscribeKey: 'YOUR SUB_KEY'
    });

    var anotherInstance = new PubNub({
        publishKey: 'ANOTHER PUB_KEY',
        subscribeKey: 'ANOTHER SUB_KEY'
    });

    defaultInstance.publish(
        {
            message: {such: 'Hello!'},
            channel: 'my_channel'
        },
        (status, response) => {
            if (status.error) {
                console.log(status);
            } else {
                console.log('message Published w/ timetoken', response.timetoken);
            }
        }
    );

    anotherInstance.grant(
        {
            channels: ['my_channel'],
            authKeys: ['my_authkey'],
            read: true,
            write: false
        },
        (status) => {
            console.log(status);
        }
    );
    
    (function (app) {
        app.appComponent = ng.core.Component({
            selector: 'appComponent',
            template: '<H1>PubNub Angular2 SDK Demo</H1>'
        }).Class({
            constructor: [PubNubAngular, function(pubnub){
                pubnub.init({
                    publishKey: 'YOUR PUB_KEY',
                    subscribeKey: 'YOUR SUB_KEY'
                });

                pubnub.getInstance("another").init({
                    publishKey: 'ANOTHER PUB_KEY',
                    subscribeKey: 'ANOTHER SUB_KEY'
                });

                pubnub.publish(
                    {
                        message: {
                            such: 'Hello!'
                        },
                        channel: 'my_channel'
                    }, 
                    function (status, response) {
                        if (status.error) {
                            console.log(status);
                        } else {
                            console.log('message Published w/ timetoken', response.timetoken);
                        }
                    }
                );

                pubnub.getInstance('another').grant(
                    {
                        channels: ['my_channel'],
                        authKeys: ['my_authkey'],
                        read: true,
                        write: false
                    }, 
                    function (status) {
                        console.log(status);
                    }
                );
            }]
        });
    })(window.app || (window.app = {}));
    
    import { Component } from '@angular/core';
    import { PubNubAngular } from 'pubnub-angular2';

    @Component({
        selector: 'appComponent',
        template: '<H1>PubNub Angular2 SDK Demo</H1>'
    })

    export class AppComponent {
        constructor(pubnub: PubNubAngular) {
            pubnub.init({
                publishKey: 'YOUR PUB_KEY',
                subscribeKey: 'YOUR SUB_KEY'
            });

            pubnub.getInstance('another').init({
                publishKey: 'ANOTHER PUB_KEY',
                subscribeKey: 'ANOTHER SUB_KEY'
            });

            pubnub.publish(
                {
                    message: {such: 'Hello!'},
                    channel: 'my_channel'
                },
                (status, response) => {
                    if (status.error) {
                        console.log(status);
                    } else {
                        console.log('message Published w/ timetoken', response.timetoken);
                    }
                }
            );

            pubnub.getInstance("another").grant(
                {
                    channels: ['my_channel'],
                    authKeys: ['my_authkey'],
                    read: true,
                    write: false
                }, (status) => {
                    console.log(status);
                }
            );
        }
    }
    

That's it, you are ready to start using PubNubAngular SDK.

If this PubNub instance will only be subscribing, you only need to pass the subscribeKey to initialize the instance. If this instance will be subscribing and publishing, you must also include the publishKey parameter.
pubnub.init({
    subscribeKey: 'YOUR SUB_KEY',
    publishKey: 'YOUR PUB_KEY',
    ssl: true
});
The channel the messages will be published over is called my_channel. For this example, we will use the same instance to both publish and subscribe. To do this, we'll publish a message to the channel, but only after we're sure we've first successfully subscribed to the channel.

The publish() and subscribe() methods are pretty simple to use. For both publish() and subscribe(), the channel attribute defines the channel in use.

When using the subscribe() method, an event is fired which is handled by adding listeners:
pubnub.subscribe({
    channels: ['my_channel'],
    withPresence: true
});
 
During your application's lifecycle, you can call subscribe() repeatedly to additively subscribe to additional channels.
For publish(), the message attribute contains the data you are sending.
pubnub.publish(
    {
        message:{ 
            such: 'Hello from the PubNub Angular2 SDK!'
        },
        channel: 'my_channel'
    },
    function (status, response) {
        if (status.error) {
            console.log(status);
        } else {
            console.log("message Published w/ timetoken", response.timetoken);
        }
    }
);
The above code demonstrates how to subscribe, and how to publish. But what if your use-case requires that client instance not only subscribes and publishes, but also that its guaranteed to start publishing only AFTER it's successfully subscribed? -- In other words, you want to guarantee it receives all of its own publishes?
The Angular2 V4 client SDK, like many of the PubNub SDKs, is asynchronous -- publish() can, and most likely will, fire before the previously executed subscribe() call completes. The result is, for a single-client instance, you would never receive (via subscribing) the message you just published, because the subscribe operation did not complete before the message was published.
To get around this common case, we can take advantage of the optional connect callback in the subscribe method.
pubnub.addListener({
    status: function(st) {
        if (st.category === "PNConnectedCategory") {
            pubnub.publish({
                message: 'Hello from the PubNub Angular2 SDK!',
                channel: 'my_channel'
            });
        }
    },
    message: function(message) {
        console.log(message);
    }
});

pubnub.subscribe({
    channels: ['my_channel']
});
By following this pattern on a client that both subscribes and publishes when you want to be sure to subscribe to your own publishes, you'll never miss receiving a message.
While you are subscribed to a channel, you will continue to receive messages published to that channel. To stop receiving messages on a given channel, you must unsubscribe() from the channel.
pubnub.unsubscribe({
    channels : ['my_channel']
});
Like subscribe(), unsubscribe() can be called multiple times to successively remove different channels from the active subscription list.
 Unsubscribing from all the channel(s) and then subscribing to a new channel Y is not the same than Subscribing to the channel Y and then unsubscribing from the previously subscribe channel(s). Unsubscribing from all the channels resets the timetoken and thus, there could be some gaps in the subscription that may lead to a message loss.