├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bower.json
├── package.json
├── signalr-hub.js
└── signalr-hub.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | #Changelog
2 |
3 | ##1.6.2 - Apr 19, 2016
4 |
5 | - Added `queryParams` to connect method [#70](https://github.com/JustMaier/angular-signalr-hub/pull/70) *Thanks @tribicito*
6 |
7 | ##1.6.1 - Mar 26, 2016
8 |
9 | - Added `withCredentials` option [#68](https://github.com/JustMaier/angular-signalr-hub/pull/68) *Thanks @redwyre*
10 |
11 | ##1.6.0 - Feb 23, 2016
12 |
13 | - Added `jsonp` option [#65](https://github.com/JustMaier/angular-signalr-hub/pull/65) *Thanks @sumant86*
14 | - Removed `hubDisconnected` use `stateChange` event hook instead
15 |
16 | ##1.5.0 - May 14, 2015
17 |
18 | - Added `stateChanged` event hook. [#45](https://github.com/JustMaier/angular-signalr-hub/pull/45) *Thanks @floo51*
19 | - Deprecated `hubDisconnected`
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | angular-signalr-hub
2 | =======================
3 | ![Bower version][bower-image]
4 | [![Nuget version][nuget-image]][nuget-url]
5 | [![NPM version][npm-image]][npm-url]
6 | [![TypeScript definitions on DefinitelyTyped][typescript-image]][typescript-url]
7 |
8 | A handy wrapper for SignalR Hubs. Just specify the hub name, listening functions, and methods that you're going to use.
9 |
10 | ## Installation
11 |
12 | #### Bower
13 | `bower install angular-signalr-hub`
14 |
15 | #### Nuget
16 | `install-package AngularJs.SignalR.Hub`
17 |
18 | #### NPM
19 | `npm install angular-signalr-hub`
20 |
21 | #### Manually
22 |
23 | ``
24 |
25 | ## Usage
26 |
27 | 1. Include the `signalr-hub.js` script provided by this component into your app
28 | 2. add `SignalR` as a module dependency to your app
29 | 3. Call new Hub with two parameters
30 |
31 | ```javascript
32 | var hub = new Hub('hubname',options);
33 | ```
34 |
35 | #### Javascript
36 |
37 | ```javascript
38 | angular.module('app',['SignalR'])
39 | .factory('Employees',['$rootScope','Hub', '$timeout', function($rootScope, Hub, $timeout){
40 |
41 | //declaring the hub connection
42 | var hub = new Hub('employee', {
43 |
44 | //client side methods
45 | listeners:{
46 | 'lockEmployee': function (id) {
47 | var employee = find(id);
48 | employee.Locked = true;
49 | $rootScope.$apply();
50 | },
51 | 'unlockEmployee': function (id) {
52 | var employee = find(id);
53 | employee.Locked = false;
54 | $rootScope.$apply();
55 | }
56 | },
57 |
58 | //server side methods
59 | methods: ['lock','unlock'],
60 |
61 | //query params sent on initial connection
62 | queryParams:{
63 | 'token': 'exampletoken'
64 | },
65 |
66 | //handle connection error
67 | errorHandler: function(error){
68 | console.error(error);
69 | },
70 |
71 | //specify a non default root
72 | //rootPath: '/api
73 |
74 | stateChanged: function(state){
75 | switch (state.newState) {
76 | case $.signalR.connectionState.connecting:
77 | //your code here
78 | break;
79 | case $.signalR.connectionState.connected:
80 | //your code here
81 | break;
82 | case $.signalR.connectionState.reconnecting:
83 | //your code here
84 | break;
85 | case $.signalR.connectionState.disconnected:
86 | //your code here
87 | break;
88 | }
89 | }
90 | });
91 |
92 | var edit = function (employee) {
93 | hub.lock(employee.Id); //Calling a server method
94 | };
95 | var done = function (employee) {
96 | hub.unlock(employee.Id); //Calling a server method
97 | }
98 |
99 | return {
100 | editEmployee: edit,
101 | doneWithEmployee: done
102 | };
103 | }]);
104 | ```
105 | ## Options
106 |
107 | * `listeners` client side callbacks*
108 | * `withCredentials` whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates, defaults to `true`
109 | * `methods` a string array of server side methods which the client can call
110 | * `rootPath` sets the root path for the signalR web service
111 | * `queryParams` object representing additional query params to be sent on connection, can also be specified in the connect method
112 | * `errorHandler` function(error) to handle hub connection errors
113 | * `logging` enable/disable logging
114 | * `useSharedConnection` use a shared global connection or create a new one just for this hub, defaults to `true`
115 | * `transport` sets transport method (e.g `'longPolling'` or `['webSockets', 'longPolling']`)
116 | * `jsonp` toggle JSONP for cross-domain support on older browsers or when you can't setup CORS
117 | * `stateChanged` function() to handle hub connection state changed event `{0: 'connecting', 1: 'connected', 2: 'reconnecting', 4: 'disconnected'}`
118 | * `autoConnect` prevents from connecting automatically. useful for authenticating and then connecting.
119 |
120 | **Note** `hubDisconnected` has been removed, instead use the following:
121 | ```
122 | 'stateChanged': function(state){
123 | var stateNames = {0: 'connecting', 1: 'connected', 2: 'reconnecting', 4: 'disconnected'};
124 | if(stateNames[state.newState] == 'disconnected'){
125 | //Hub Disconnect logic here...
126 | }
127 | }
128 | ```
129 |
130 | ## Demo
131 |
132 | [A simple demo using OData, Signalr, and Angular](https://github.com/JustMaier/signalrgrid)
133 |
134 | It's an adaption of [turanuk's great SignalR demo with Knockout](https://github.com/turanuk/signalrgrid).
135 |
136 | ## Simple Chat Demo
137 |
138 | This sample starts off with the [MVC-SignalR chat](http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc) sample by Tim Teebken and Patrick Fletcher.
139 |
140 | This sample is then reworked (in a quick and dirty way) to show how to go about using the chathub from angular by using the angular-signalr-hub.
141 |
142 | Some extra NuGet packages are added to the project. (check out the packages.config file)
143 | An app folder was added for the angular app, in which the following was added:
144 | * a module (signalRChatApp)
145 | * a factory (ChatService)
146 | * a controller (ChatController)
147 | * an html page
148 |
149 | Modifications were made to the following files:
150 | * BundleConfig.cs
151 | * RouteConfig.cs
152 | * HomeController.cs
153 | * Global.asax.cs
154 | * Startup.cs
155 | * Index.cshtml
156 |
157 | In the app folder for the angular app, there is a ChatService which uses the angular-signalr-hub.
158 | The hub in this case is the ChatHub in this project.
159 |
160 | Download the full sample [here](https://onedrive.live.com/redir?resid=A384F349DF30AC50!34027&authkey=!ABcHiikiie50aCM&ithint=file%2czip).
161 |
162 | The sample is provided as is.
163 | There are soms issues with the way it is set up, but it does the trick in showing in showing how to use the angular-signalr-hub in an easy to reproduce app.
164 |
165 | ## Multiple hubs
166 |
167 | There is something you have to take care about when using multiple hubs in an angular app :
168 |
169 | Angular services are singletons, so they won't be instantiated before you need it.
170 |
171 | If you use shared connection between your hubs (`useSharedConnection`), and if you have two services containing hubs, you can have a problem :
172 |
173 | The first service loaded will start the connection. Then when the second service will load, its hub won't be registered to the server SignalR (`OnConnected` method) if this service is instantiated after that the shared connection is `connected`.
174 | (SignalR trace : SignalR: Client subscribed to hub 'hubname'.)
175 | The hub of the second service will be able to invoke server methods, but the server won't be able to invoke the client methods for this hub.
176 |
177 | To avoid that, you can put `useSharedConnection` to `false`.
178 |
179 | ## Notes
180 |
181 | * I would recommend creating a factory or service around the Hub so that you have an easy to use "model handler" that can include SignalR and Web API calls and be easily pulled into any controller
182 | * For an example of Web API, SignalR, and Angular working together check out this [small demo](https://github.com/JustMaier/signalrgrid) I adapted from [turanuk's SignalR demo with Knockout](https://github.com/turanuk/signalrgrid)
183 |
184 |
185 | [npm-image]: https://img.shields.io/npm/v/angular-signalr-hub.svg?style=flat-square
186 | [npm-url]: https://www.npmjs.com/package/angular-signalr-hub
187 | [bower-image]: https://img.shields.io/bower/v/angular-signalr-hub.svg?style=flat-square
188 | [nuget-image]: https://img.shields.io/nuget/v/AngularJs.SignalR.Hub.svg?style=flat-square
189 | [nuget-url]: https://www.nuget.org/packages/AngularJs.SignalR.Hub/
190 | [typescript-image]: http://definitelytyped.org/badges/standard-flat.svg
191 | [typescript-url]: http://definitelytyped.org
192 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-signalr-hub",
3 | "version": "1.6.3",
4 | "repository":{
5 | "type": "git",
6 | "url": "git@github.com:JustMaier/angular-signalr-hub.git"
7 | },
8 | "main": "signalr-hub.js",
9 | "dependencies": {
10 | "angular": "*",
11 | "jquery": "*",
12 | "signalr": "*"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-signalr-hub",
3 | "version": "1.6.3",
4 | "description": "A handy wrapper for SignalR Hubs. Just specify the hub name, listening functions, and methods that you're going to use.",
5 | "main": "signalr-hub.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/justmaier/angular-signalr-hub.git"
9 | },
10 | "keywords": [
11 | "angular",
12 | "angularjs",
13 | "signalr",
14 | "hub",
15 | "service"
16 | ],
17 | "author": "Justin Maier (http://justinmaier.com)",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/justmaier/angular-signalr-hub/issues"
21 | },
22 | "homepage": "https://github.com/justmaier/angular-signalr-hub",
23 | "devDependencies": {},
24 | "dependencies": {}
25 | }
26 |
--------------------------------------------------------------------------------
/signalr-hub.js:
--------------------------------------------------------------------------------
1 | angular.module('SignalR', [])
2 | .constant('$', window.jQuery)
3 | .factory('Hub', ['$', function ($) {
4 | //This will allow same connection to be used for all Hubs
5 | //It also keeps connection as singleton.
6 | var globalConnections = [];
7 |
8 | function initNewConnection(options) {
9 | var connection = null;
10 | if (options && options.rootPath) {
11 | connection = $.hubConnection(options.rootPath, { useDefaultPath: false });
12 | } else {
13 | connection = $.hubConnection();
14 | }
15 |
16 | connection.logging = (options && options.logging ? true : false);
17 | return connection;
18 | }
19 |
20 | function getConnection(options) {
21 | var useSharedConnection = !(options && options.useSharedConnection === false);
22 | if (useSharedConnection) {
23 | return typeof globalConnections[options.rootPath] === 'undefined' ?
24 | globalConnections[options.rootPath] = initNewConnection(options) :
25 | globalConnections[options.rootPath];
26 | }
27 | else {
28 | return initNewConnection(options);
29 | }
30 | }
31 |
32 | return function (hubName, options) {
33 | var Hub = this;
34 |
35 | Hub.connection = getConnection(options);
36 | Hub.proxy = Hub.connection.createHubProxy(hubName);
37 |
38 | Hub.on = function (event, fn) {
39 | Hub.proxy.on(event, fn);
40 | };
41 | Hub.invoke = function (method, args) {
42 | return Hub.proxy.invoke.apply(Hub.proxy, arguments)
43 | };
44 | Hub.disconnect = function () {
45 | Hub.connection.stop();
46 | };
47 | Hub.connect = function (queryParams) {
48 | var startOptions = {};
49 | if (options.transport) startOptions.transport = options.transport;
50 | if (options.jsonp) startOptions.jsonp = options.jsonp;
51 | if (options.pingInterval !== undefined) startOptions.pingInterval = options.pingInterval;
52 |
53 | if (angular.isDefined(options.withCredentials)) startOptions.withCredentials = options.withCredentials;
54 | if(queryParams) Hub.connection.qs = queryParams;
55 | return Hub.connection.start(startOptions);
56 | };
57 |
58 | if (options && options.listeners) {
59 | Object.getOwnPropertyNames(options.listeners)
60 | .filter(function (propName) {
61 | return typeof options.listeners[propName] === 'function';})
62 | .forEach(function (propName) {
63 | Hub.on(propName, options.listeners[propName]);
64 | });
65 | }
66 | if (options && options.methods) {
67 | angular.forEach(options.methods, function (method) {
68 | Hub[method] = function () {
69 | var args = $.makeArray(arguments);
70 | args.unshift(method);
71 | return Hub.invoke.apply(Hub, args);
72 | };
73 | });
74 | }
75 | if (options && options.queryParams) {
76 | Hub.connection.qs = options.queryParams;
77 | }
78 | if (options && options.errorHandler) {
79 | Hub.connection.error(options.errorHandler);
80 | }
81 | if (options && options.stateChanged) {
82 | Hub.connection.stateChanged(options.stateChanged);
83 | }
84 |
85 | //Adding additional property of promise allows to access it in rest of the application.
86 | if(options.autoConnect === undefined || options.autoConnect){
87 | Hub.promise = Hub.connect();
88 | }
89 |
90 | return Hub;
91 | };
92 | }]);
93 |
94 | // Common.js package manager support (e.g. ComponentJS, WebPack)
95 | if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.exports === exports) {
96 | module.exports = 'SignalR';
97 | }
98 |
--------------------------------------------------------------------------------
/signalr-hub.min.js:
--------------------------------------------------------------------------------
1 | angular.module("SignalR",[]).constant("$",window.jQuery).factory("Hub",["$",function($){var globalConnections=[];function initNewConnection(options){var connection=null;if(options&&options.rootPath){connection=$.hubConnection(options.rootPath,{useDefaultPath:false})}else{connection=$.hubConnection()}connection.logging=options&&options.logging?true:false;return connection}function getConnection(options){var useSharedConnection=!(options&&options.useSharedConnection===false);if(useSharedConnection){return typeof globalConnections[options.rootPath]==="undefined"?globalConnections[options.rootPath]=initNewConnection(options):globalConnections[options.rootPath]}else{return initNewConnection(options)}}return function(hubName,options){var Hub=this;Hub.connection=getConnection(options);Hub.proxy=Hub.connection.createHubProxy(hubName);Hub.on=function(event,fn){Hub.proxy.on(event,fn)};Hub.invoke=function(method,args){return Hub.proxy.invoke.apply(Hub.proxy,arguments)};Hub.disconnect=function(){Hub.connection.stop()};Hub.connect=function(queryParams){var startOptions={};if(options.transport)startOptions.transport=options.transport;if(options.jsonp)startOptions.jsonp=options.jsonp;if(options.pingInterval!==undefined)startOptions.pingInterval=options.pingInterval;if(angular.isDefined(options.withCredentials))startOptions.withCredentials=options.withCredentials;if(queryParams)Hub.connection.qs=queryParams;return Hub.connection.start(startOptions)};if(options&&options.listeners){Object.getOwnPropertyNames(options.listeners).filter(function(propName){return typeof options.listeners[propName]==="function"}).forEach(function(propName){Hub.on(propName,options.listeners[propName])})}if(options&&options.methods){angular.forEach(options.methods,function(method){Hub[method]=function(){var args=$.makeArray(arguments);args.unshift(method);return Hub.invoke.apply(Hub,args)}})}if(options&&options.queryParams){Hub.connection.qs=options.queryParams}if(options&&options.errorHandler){Hub.connection.error(options.errorHandler)}if(options&&options.stateChanged){Hub.connection.stateChanged(options.stateChanged)}if(options.autoConnect===undefined||options.autoConnect){Hub.promise=Hub.connect()}return Hub}}]);if(typeof module!=="undefined"&&typeof exports!=="undefined"&&module.exports===exports){module.exports="SignalR"}
--------------------------------------------------------------------------------