An Angular 1.x service for seamlessly integrating Rails 5 (ActionCable) into frontend Angular code. This service opens and maintains a websocket connection between Angular and ActionCable, reconnecting & resubscribing when the connection has been lost, and desynchronising the clients from one another to ease server-side events like code deploys or server restarts.
- Use bower and run
bower install angular-actioncable --save
(preferred) - Use npm and run
npm install angular-actioncable --save
- Download it manually
- CDN for development
https://rawgit.com/angular-actioncable/angular-actioncable/1.3.0/dist/angular-actioncable.js
- CDN for production
https://cdn.rawgit.com/angular-actioncable/angular-actioncable/1.3.0/dist/angular-actioncable.min.js
<%= action_cable_meta_tag %>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="bower_components/angular-websocket/dist/angular-websocket.min.js"></script>
<script src="bower_components/angular-actioncable/dist/angular-actioncable.js"></script>
<section ng-controller="SomeController">
<ul>
<li ng-repeat="datum in myData">
{{ datum }}
</li>
</ul>
</section>
<script>
angular.module('YOUR_APP', [
'ngActionCable'
])
.controller('SomeController', function ($scope, ActionCableChannel){
$scope.myData = [];
// connect to ActionCable
(new ActionCableChannel("MyChannel")).subscribe(function(message){ $scope.myData.push(message) });
});
</script>
<meta name="action-cable-url" content="ws://localhost:3000/cable"/>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="bower_components/angular-websocket/dist/angular-websocket.min.js"></script>
<script src="bower_components/angular-actioncable/dist/angular-actioncable.js"></script>
<section ng-controller="SomeController">
<ul>
<li ng-repeat="datum in myData">
{{ datum }}
</li>
</ul>
<input ng-model="inputText" /><button ng-click="sendToMyChannel(inputText)">Send</button>
</section>
<script>
angular.module('YOUR_APP', [
'ngActionCable'
])
.controller('SomeController', function ($scope, ActionCableChannel){
$scope.inputText = "";
$scope.myData = [];
// connect to ActionCable
var consumer = new ActionCableChannel("MyChannel", {user: 42, chat: 37});
var callback = function(message){ $scope.myData.push(message); };
consumer.subscribe(callback).then(function(){
$scope.sendToMyChannel = function(message){ consumer.send(message, 'send_a_message'); };
$scope.$on("$destroy", function(){
consumer.unsubscribe().then(function(){ $scope.sendToMyChannel = undefined; });
});
});
});
</script>
class MyChannel < ApplicationCable::Channel
# ...
def send_a_message(message)
# ...
end
end
Supports:
- Rails 5.0
constructor function
name | arguments | description |
---|---|---|
new | channelName:String channelParams:Hash:optional returns instance |
Creates and opens an ActionCableChannel instance.var consumer = new ActionCableChannel('MyChannel', {widget_id: 17}); |
subscribe | callback:Function returns promise |
Subscribes a callback function to the channel.consumer.subscribe(function(message){ $scope.thing = message }); |
unsubscribe | returns promise |
Unsubscribes the callback function from the channel.consumer.unsubscribe(); |
send | message:String action:String:optional returns promise |
Send a message to an action in Rails. The action is the method name in Ruby.consumer.send('message'); |
onConfirmSubscription | callback:Function | Call each time server registers a subscription.consumer.onConfirmSubscription(function(){ console.log('subscribed'); }); |
singleton
name | arguments | description |
---|---|---|
start | Starts ngActionCable services. ActionCableSocketWrangler.start(); This will start by default unless disabled. |
|
stop | Stops ngActionCable services. ActionCableSocketWrangler.stop(); |
|
preConnectionCallbacks | Allows registration of functions which return promises which much be resolved before attempting to establish a connection. ActionCableSocketWrangler.preConnectionCallbacks().push(myFunctionThatReturnsAPromise); |
Exactly one will be true at all times.
name | type | description |
---|---|---|
connected | Property:Boolean | ngActionCable is started and connected live.ActionCableSocketWrangler.connected; |
connecting | Property:Boolean | ngActionCable is started and trying to establish a connection.ActionCableSocketWrangler.connecting; |
disconnected | Property:Boolean | ngActionCable is stopped and not connected.ActionCableSocketWrangler.disconnected; |
value
You can override the defaults.
name | type | description |
---|---|---|
wsUri | String | URI to connect ngActionCable to ActionCable. If this is inside a Rails view, it will be read from the action_cable_meta_tag but can still be overridden. |
protocols | Array | Specify protocol headers for the websocket connection. Empty by default. |
autoStart | Boolean | Connect automatically? Default is true.ActionCableConfig.autoStart= false; |
debug | Boolean | Show verbose logs. Default is false.ActionCableConfig.debug= true; |
my_app.run(function (ActionCableConfig){
ActionCableConfig.wsUri= "wss://example.com/cable";
ActionCableConfig.protocols = ['soap', 'wamp'];
ActionCableConfig.autoStart= false;
});
- Q.: What if the browser doesn't support WebSockets?
- A.: This module depends on angular-websocket which will not help; it does not have a fallback story for browsers that do not support WebSockets. Please check your browser target support.
This package is not managing the consumer unsubscribing when you're destroying an Angular object (controller, component, ...) so you must do it.
A simple way to do so is to use the minimal version of the "better way" example:
function SharedMsStockResponse($q, $scope, x2js, stockResponseService, Supplier, ActionCableChannel) {
var ctrl = this
var consumer = new ActionCableChannel("MyChannel", {});
var callback = function(result) {
console.log("result", result);
# ctrl is accessible here
}
consumer.subscribe(callback).then(function() {
$scope.$on("$destroy", function() {
consumer.unsubscribe();
});
});
});
angular.module('MY_APP')
.component('sharedMsStockResponse', {
templateUrl: ...,
controller: ['$q', '$scope', 'x2js', 'stockResponseService', 'Supplier', 'ActionCableChannel', SharedMsStockResponse],
bindings: {
...
saleRow: '<'
}
});
Please have a look at this issue in order to get more information.