Chia sẻ thư viện javascript mở rộng khi kết nối tới Hub sử dụng SignalR

Bài viết được sự cho phép của tác giả Phạm Công Sơn

Tôi chia sẻ kinh nghiệm và thư viện mở rộng mà tôi viết để kết nối tới Hub sử dụng SignalR. Thư viện do tôi mở rộng thêm với mục đích

  1. 1. Có thể kết nối được tới nhiều Hub
  2. 2. Duy trì được kết nối ổn định: Khi disconnected thì sẽ tự động kết nối trở lại
  3. 3. Có thể đăng ký nhiều sự kiện để nhận data được push từ server gửi về
  4. 4. Khi gửi request lên server Hub thì chỉ gửi được khi đang ở trạng thái kết nối tốt.
  10 câu hỏi javascript để nâng cao trình độ
  10 tip tối ưu code trên JavaScript mà web developer nào cũng nên biết

Để tải thư viện mở rộng do tôi viết các bạn có thể tải tại đây.

function Hub(hubUrl, hubName) {     this.onOk = function (func) { if (state == 2) func(); };     this.onBeforeStart = function () { };     this.start = function() {/* Code coe */ }; }

Phần mở rộng tôi viết gồm hubUrlhubName là trỏ đến địa chỉ Hub cần kết nối tới. this.onOk là phương thức đảm bảo đang ở trạng thái kết nối mới thực hiện func (một hành động nào đó). Với class Hub tôi đã xử lý hết các sự kiện khi mất kết nối, đang kết nối và kết nối

// Nếu bị mất kết nối thì 2s sau sẽ kết nối lại
$this.hub.connection.disconnected(function ()
{
console.log("disconnected");
state = 1;
var t = setInterval(function () { $this.doHubStart(true); clearInterval(t); }, $this.timeout);
});
$this.hub.connection.reconnecting(function ()
{
state = 1;
console.log("reconnecting");
});

// Đã được kết nối lại
$this.hub.connection.reconnected(function ()
{
console.log("reconnected");
var t = setInterval(function () { $this.doHubStart(true); clearInterval(t); }, $this.timeout);
});

Nếu bị mất kết nối, thì sau khoảng this.timeout (mặc định 2000ms) sẽ thực hiện kết nối lại.

Hub.ActionRegistry = function ()
{
this.registry = function (func) { /* Code code */ }
/* Code Code */
}

Với Hub.ActionRegistry được dùng để đăng ký các hành động sau khi nhận dữ liệu được push từ server gửi về. Tại sao tôi lại viết class này, vì tôi hướng tới có thể nhiều nơi, nhiều module mà cũng muốn nhận dữ liệu push về mà không phải lúc nào cũng phải sửa lại phương thức nhận dữ liệu tại client của Hub. Điều này cũng đồng nghĩa với việc lúc nào module nào dùng thì có thể đăng ký nhận dữ liệu.

Sau đây là code mẫu tôi viết cho chương trình chat đơn giản về sử dụng thư viện mở rộng trên

Tán gẫu nào
Trước tiên bạn cũng cài đặt SignalR và gắn thẻ script ở client
<script src='/signalr/hubs'></script> <!-- file js mà hub sinh ra --> <script src='Hub.js'></script>        <!-- Thư viện mở rộng của tôi -->
Code server để định nghĩa Hub
public class ExampleHub : Hub
{
public void ClientSendMessage(string userName, string msg, string color)
{
userName = userName.Trim();
if (userName.IsNull()) return;

msg = msg.Trim();
if (msg.IsNull()) return;

var data = new { userName, avatar = userName.First(), msg, time = DateTime.Now.ToString("HH:mm dd/MM/yyyy"), color };

Clients.AllExcept(Context.ConnectionId).receiveMessage(data, false);
Clients.Client(Context.ConnectionId).receiveMessage(data, true);
}
}
Code javascript tại client để kết nối tới Hub
function ExampleHub()
{
$.extend(this, new Hub("", "exampleHub"));

this.actionReceiveMessage = new Hub.ActionRegistry();

this.onBeforeStart = function ()
{
var $this = this;

// Client
this.hub.client.receiveMessage = function (data, right)
{
$this.actionReceiveMessage.do(function (func) { func(data, right); });
};

// Server
this.startDone = function () { };
this.clientSendMessage = function (userName, message, color)
{
this.onOk(function ()
{
$this.hub.server.clientSendMessage(userName, message, color);
});
}
};
}

Ở đây các bạn có thể thấy ExampleHub kế thừa tới Hub và gán 2 giá trị: hubUrl = “” (Do server của hub nằm ngay tại server web của tôi) và hubName = “exampleHub”.

Ở phía xử lý client có this.actionReceiveMessage là đối tượng đăng ký nhận hành động muốn xử lý data gửi về từ server.  Client muốn giao tiếp gửi request tới server thì thông qua this.onOk để luôn đảm bảo chỉ thực hiện request khi ở trạng thái kết nối ổn định.

Và cuối cùng là giao diện hiển thị tôi định nghĩa một BoxChat như sau
function BoxChat()
{
this.ws = null; // là một instance của ExampleHub
this.area = null;
var template = null;

var colors = { 0: "bg-danger text-white", 1: "bg-success text-white", 2: "bg-primary text-white", 3: "bg-warning text-white", 4: "bg-secondary text-white", 5: "bg-dark text-white", 6: "bg-orangered text-white" };
var names = { 0: "Sơn20", 1: "Dũng", 2: "Việt Anh", 3: "TuhiKing", 4: "Tàn Long", 5: "ButaKing", 6: "Osoft" };

var color = null;

this.start = function ()
{
color = colors[Core.random(6)];

var $this = this;
template = this.area.find("[data-form=template] .direct-chat-msg");
var userNameInput = this.area.find("[name=userName]");
var messageInput = this.area.find("[name=message]").focus();
var butonSend = this.area.find("[data-form=button]");

userNameInput.val(names[Core.random(6)] + "." + Core.random(20)); // Tạo một tên ngẫu nhiên
butonSend.click(function ()
{
var userName = userNameInput.val();
if (userName == "") { Core.alert("Vui lòng nhập tên tài khoản để tán gẫu"); return; };

var message = messageInput.val();
if (message == "") { Core.alert("Vui lòng nhập nội dung để tán gẫu"); return; };

$this.ws.clientSendMessage(userName, message, color);
});
messageInput.keypress(function (event)
{
if (event.keyCode == "13")
{
butonSend.click();
return false;
}
});

var formChat = this.area.find("[data-form=chat]");
this.ws.actionReceiveMessage.registry(function (data, right)
{
var chatItem = template.clone();
chatItem.find("[data-chat]").each(function () { $(this).html(data[$(this).attr("data-chat")]); });
chatItem.find("[data-chat=avatar]").addClass(data.color);
if (right) chatItem.addClass("right");
formChat.append(chatItem);

formChat.scrollTop(formChat[0].scrollHeight);
messageInput.val("").focus();
});
}
}

Tại hàm start của BoxChat tôi đăng ký hành động khi có message gửi về thì hiển thị nội dung tin nhắn lên. Như đã nói ở trên, actionRegistry giúp đăng ký hành động nhận push từ server ở mọi module mà mình muốn mà không cần phải sửa lại hàm ở client trong hub.

Cuối cùng là khai báo và sử dụng
var ws = new ExampleHub();
ws.timeout = 2000; // Mặc định
ws.start();

var boxChat = new BoxChat();
boxChat.ws = ws;
boxChat.area = $("article");
boxChat.start();

// Ví dụ viết code đăng ký thêm một hành động nhận dữ liệu từ server push về
// Thì hiển thị một notify bên góc phải trên cùng màn hình
// Để cho thấy là có thể đăng ký sự kiện nhận dữ liệu ở mọi nơi.
ws.actionReceiveMessage.registry(function (data, right)
{
new PNotify({ target: $("article"), text: data.userName + ":" + data.msg, type: "success", delay: 1000 });
});

Tại bài viết này tôi vẫn đang sử dụng asp.net SignalR mà chưa chuyển sang asp.net Core SignalR. Do chưa đủ điều kiện về vật chất, hạ tầng và nhất là hệ thống phần mềm hiện tại của tôi vẫn đang viết trên asp.net. Tuy nhiên với cách làm này của tôi thì chuyển sang asp.net Core tôi cũng sẽ làm tương tự. Và đây là chia sẻ nhỏ mà tôi đã làm trong thực tế.

Chia sẻ thư viện javascript mở rộng khi kết nối tới Hub sử dụng SignalR

Gửi cảnh báo khi nhân viên thêm phiếu chi với một mục đích chi nào đó bị quá giới hạn cho phép mà quản lý đã cấu hình

Chia sẻ thư viện javascript mở rộng khi kết nối tới Hub sử dụng SignalR

Click vào cảnh báo ở góc phải bên dưới màn hình xem ngay được nội dung cảnh báo

Chia sẻ thư viện javascript mở rộng khi kết nối tới Hub sử dụng SignalR

Tại nội dung cảnh báo. Click xem chi tiết phiếu để xem nhân viên đã thêm chi cho những mục đích gì

Chúc các bạn lập trình ngày càng tốt hơn. Nếu có thắc mắc các bạn hãy để lại bình luận nhé. Xin cảm ơn smiley

Sơn 20

Bài viết gốc được đăng tải tại sonpc20.com

Có thể bạn quan tâm:

Tham khảo tìm việc Javascript hấp dẫn trên TopDev