In the previous articles, I have finished the TODO App with the most basic functions such as Add, Update, Delete, Done, … Today I will implement more realtime functions for those operations. As you know from Rails 5 and above, there is action cable support available to do realtime for your Rails project.
Implement server side
First, generate channel:
1 2 | rails generate channel Tasks |
It will create the following files:
1 2 3 4 | create app/channels/tasks_channel.rb identical app/assets/javascripts/cable.js create app/assets/javascripts/channels/tasks.coffee |
In the app/channels/tasks_channel.rb
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token comment"># app/channels/tasks_channel.rb</span> <span class="token keyword">class</span> <span class="token class-name">TasksChannel</span> <span class="token operator"><</span> <span class="token constant">ApplicationCable</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">Channel</span> <span class="token keyword">def</span> subscribed stream_from <span class="token string">"tasks_channel"</span> <span class="token keyword">end</span> <span class="token keyword">def</span> unsubscribed <span class="token comment"># Any cleanup needed when channel is unsubscribed</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
Here, I will be subscribed to the channel named tasks_channel. Data broadcast to this channel will receive it.
Next in the controller I will broadcast to the tasks_channel channel with the necessary data as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <span class="token keyword">def</span> create task <span class="token operator">=</span> <span class="token constant">Task</span> <span class="token punctuation">.</span> create <span class="token operator">!</span> task_params <span class="token constant">ActionCable</span> <span class="token punctuation">.</span> server <span class="token punctuation">.</span> <span class="token function">broadcast</span> <span class="token punctuation">(</span> <span class="token string">"tasks_channel"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> type <span class="token punctuation">:</span> <span class="token string">"add"</span> <span class="token punctuation">,</span> task <span class="token punctuation">:</span> <span class="token constant">Api</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">V1</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">TaskSerializer</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">(</span> task <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> render json <span class="token punctuation">:</span> <span class="token punctuation">{</span> success <span class="token punctuation">:</span> <span class="token keyword">true</span> <span class="token punctuation">,</span> data <span class="token punctuation">:</span> <span class="token constant">Api</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">V1</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">TaskSerializer</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">(</span> task <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">def</span> update task <span class="token operator">=</span> <span class="token constant">Task</span> <span class="token punctuation">.</span> find params <span class="token punctuation">[</span> <span class="token symbol">:id</span> <span class="token punctuation">]</span> task <span class="token punctuation">.</span> update_attributes <span class="token operator">!</span> task_params <span class="token constant">ActionCable</span> <span class="token punctuation">.</span> server <span class="token punctuation">.</span> <span class="token function">broadcast</span> <span class="token punctuation">(</span> <span class="token string">"tasks_channel"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> type <span class="token punctuation">:</span> <span class="token string">"update"</span> <span class="token punctuation">,</span> task <span class="token punctuation">:</span> <span class="token constant">Api</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">V1</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">TaskSerializer</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">(</span> task <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> render json <span class="token punctuation">:</span> <span class="token punctuation">{</span> success <span class="token punctuation">:</span> <span class="token keyword">true</span> <span class="token punctuation">,</span> data <span class="token punctuation">:</span> <span class="token constant">Api</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">V1</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">TaskSerializer</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">(</span> task <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">def</span> destroy task <span class="token operator">=</span> <span class="token constant">Task</span> <span class="token punctuation">.</span> find params <span class="token punctuation">[</span> <span class="token symbol">:id</span> <span class="token punctuation">]</span> <span class="token constant">ActionCable</span> <span class="token punctuation">.</span> server <span class="token punctuation">.</span> <span class="token function">broadcast</span> <span class="token punctuation">(</span> <span class="token string">"tasks_channel"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> type <span class="token punctuation">:</span> <span class="token string">"delete"</span> <span class="token punctuation">,</span> task <span class="token punctuation">:</span> <span class="token constant">Api</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">V1</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">TaskSerializer</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token punctuation">(</span> task <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> task <span class="token punctuation">.</span> destroy <span class="token operator">!</span> render json <span class="token punctuation">:</span> <span class="token punctuation">{</span> success <span class="token punctuation">:</span> <span class="token keyword">true</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> |
Each action has a type
used for the client to know from which actions the data received is.
Implement side
Actioncable not only supports server side, it also supports Reactjs.
1 2 | yarn add actioncable |
On the Client side, we will need 3 main steps as follows:
- Connecting to ‘/ cable’
- Subscribe to the ‘tasks_channel’ channel
- Listen to receive data
In the src / TodoList.js file, in componentDidMount
modify the code as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token function">componentDidMount</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token keyword">const</span> cable <span class="token operator">=</span> ActionCable <span class="token punctuation">.</span> <span class="token function">createConsumer</span> <span class="token punctuation">(</span> <span class="token string">'ws://192.168.2.103:2000/cable'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Kết nói</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> subscriptions <span class="token operator">=</span> cable <span class="token punctuation">.</span> subscriptions <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> <span class="token string">'TasksChannel'</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token comment">// subscribe</span> received <span class="token punctuation">:</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// data nhận được</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> data <span class="token punctuation">.</span> type <span class="token operator">===</span> <span class="token string">'add'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> items <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token operator">...</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">.</span> items <span class="token punctuation">,</span> data <span class="token punctuation">.</span> task <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> data <span class="token punctuation">.</span> type <span class="token operator">===</span> <span class="token string">'update'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> updatedData <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">.</span> items <span class="token punctuation">.</span> <span class="token function">map</span> <span class="token punctuation">(</span> item <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> item <span class="token punctuation">.</span> id <span class="token operator">===</span> data <span class="token punctuation">.</span> task <span class="token punctuation">.</span> id <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span> data <span class="token punctuation">.</span> task <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> item <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> items <span class="token punctuation">:</span> updatedData <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> data <span class="token punctuation">.</span> type <span class="token operator">===</span> <span class="token string">'delete'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> filteredItems <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> state <span class="token punctuation">.</span> items <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> item <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> item <span class="token punctuation">.</span> id <span class="token operator">!==</span> data <span class="token punctuation">.</span> task <span class="token punctuation">.</span> id <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> items <span class="token punctuation">:</span> filteredItems <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Come here is done. =))
Result:
To understand actioncable details, go to its document.