[{"data":1,"prerenderedAt":833},["ShallowReactive",2],{"news-item-\u002Fvi\u002Fnews\u002Fxay-dung-ung-dung-chat-realtime-voi-framework-meteor-js":3},{"id":4,"title":5,"body":6,"category":820,"created by":821,"date":822,"description":823,"extension":824,"meta":825,"navigation":826,"path":827,"sections":828,"seo":829,"stem":830,"thumbnail":831,"__hash__":832},"content_vi\u002Fvi\u002Fnews\u002Fxay-dung-ung-dung-chat-realtime-voi-framework-meteor-js.md","Xây dựng ứng dụng Chat Realtime với framework Meteor.js",{"type":7,"value":8,"toc":808},"minimark",[9,17,21,24,30,33,39,45,51,57,63,69,75,82,89,94,97,108,119,125,129,136,141,156,162,165,173,180,186,196,200,204,208,214,242,248,268,274,281,287,293,297,300,305,312,318,328,334,339,346,357,360,366,372,390,400,406,412,423,429,436,442,445,451,457,464,470,473,484,488,493,499,505,513,519,524,530,533,537,542,557,563,573,579,586,592,598,604,609,615,628,634,647,653,656,660,665,669,674,679,683,688,701,705,717,721,733,737,749,753,765,769,772,777,783,786,790,796,802],[10,11,13],"h1",{"id":12},"giới-thiệu",[14,15,16],"strong",{},"Giới thiệu",[18,19,20],"p",{},"Meteor (Meteor.js) là một framework JavaScript Full-Stack, được xây dựng dựa trên Node.js, giúp phát triển các ứng dụng web và mobile một cách nhanh chóng và hiệu quả. Với thiết kế tối ưu hóa cho sự tích hợp mượt mà giữa client và server, Meteor không chỉ giúp đơn giản hóa quá trình chia sẻ code mà còn nâng cao trải nghiệm với dữ liệu thời gian thực.",[18,22,23],{},"Nếu bạn đang tìm kiếm một giải pháp phát triển ứng dụng nhanh chóng hoặc muốn mở rộng kiến thức về các công nghệ web hiện đại, bài viết này sẽ cung cấp cái nhìn tổng quan và sâu sắc về Meteor.",[10,25,27],{"id":26},"đặc-điểm-nổi-bật-của-meteor",[14,28,29],{},"Đặc điểm nổi bật của Meteor",[18,31,32],{},"Trong phần này, chúng ta sẽ khám phá sâu hơn về các đặc điểm nổi bật của Meteor, làm nên sự khác biệt và độc đáo của framework này trong lĩnh vực phát triển web và mobile:",[18,34,35,38],{},[14,36,37],{},"Tích Hợp Mượt Mà giữa Client và Server",": Meteor cung cấp một mô hình dữ liệu đồng bộ tự động, cho phép cập nhật dữ liệu tức thời trên cả client và server mà không cần phải viết code đồng bộ dữ liệu phức tạp.",[18,40,41,44],{},[14,42,43],{},"Chia Sẻ Code giữa Client và Server",": Với Meteor, việc chia sẻ code trở nên dễ dàng, giúp tối ưu hóa quy trình phát triển và duy trì ứng dụng.",[18,46,47,50],{},[14,48,49],{},"Cập Nhật Dữ liệu Thời Gian Thực",": Meteor sử dụng DDP (Distributed Data Protocol) để đồng bộ dữ liệu giữa client và server, giúp ứng dụng cập nhật dữ liệu một cách nhanh chóng và mượt mà.",[18,52,53,56],{},[14,54,55],{},"Hệ Sinh Thái Mạnh Mẽ",": Meteor có một hệ sinh thái phong phú với hàng ngàn gói và công cụ hỗ trợ, từ việc tích hợp với MongoDB cho đến việc triển khai ứng dụng với Galaxy, dịch vụ hosting của chính Meteor.",[18,58,59,62],{},[14,60,61],{},"Tính Năng Hot Code Push",": Tính năng này cho phép các nhà phát triển cập nhật ứng dụng của họ mà không làm gián đoạn trải nghiệm người dùng, một lợi ích lớn trong việc duy trì và cập nhật ứng dụng.",[18,64,65,68],{},[14,66,67],{},"Đa Nền Tảng:"," Chỉ cần code một lần, bạn có thể deploy nó thành một web app, hoặc build nó thành một mobile app trên Android, IOS.",[10,70,72],{"id":71},"bắt-đầu-với-meteorjs",[14,73,74],{},"Bắt đầu với Meteor.js",[18,76,77,78,81],{},"Để bắt đầu với Meteor, bạn cần đảm bảo rằng Node.js đã được cài đặt trên máy của bạn. Hiện tại, Meteor hỗ trợ Node.js từ phiên bản ",[14,79,80],{},"10 đến 14",", với Meteor 3.0 đang trong quá trình phát triển để hỗ trợ phiên bản Node.js mới nhất.",[83,84,86],"h2",{"id":85},"cài-đặt-meteor",[14,87,88],{},"Cài đặt Meteor",[18,90,91],{},[14,92,93],{},"Windows, Linux, và OS X:",[18,95,96],{},"Bạn có thể cài đặt Meteor trên tất cả các nền tảng này bằng lệnh sau trong terminal:",[98,99,104],"pre",{"className":100,"code":102,"language":103},[101],"language-text","npm install -g meteor\n","text",[105,106,102],"code",{"__ignoreMap":107},"",[18,109,110,111,114,115,118],{},"Ngoài ra, trên ",[14,112,113],{},"Linux và OS X,"," Meteor cũng có thể được cài đặt qua ",[105,116,117],{},"curl"," bằng cách sử dụng lệnh sau:",[98,120,123],{"className":121,"code":122,"language":103},[101],"curl https:\u002F\u002Finstall.meteor.com\u002F | sh\n",[105,124,122],{"__ignoreMap":107},[83,126,128],{"id":127},"tạo-ứng-dụng-chat-realtime-đơn-giản","Tạo Ứng Dụng Chat Realtime Đơn Giản",[18,130,131,132,135],{},"Chúng ta sẽ cùng tìm hiểu thêm về ",[14,133,134],{},"Meteor.js"," bằng cách thực hiện 1 project chat realtime đơn giản nhé.",[137,138,140],"h4",{"id":139},"_1-khởi-tạo-dự-án","1. Khởi Tạo Dự Án:",[18,142,143,144,147,148,151,152,155],{},"Để thiết lập nhanh chóng, sử dụng lệnh ",[105,145,146],{},"meteor create"," kèm theo tùy chọn ",[105,149,150],{},"--blaze"," và đặt tên cho dự án của bạn. Trong trường hợp này, chúng ta sẽ gọi dự án là “",[14,153,154],{},"simple-chat-meteor","”:",[98,157,160],{"className":158,"code":159,"language":103},[101],"meteor create --blaze simple-chat-meteor\n",[105,161,159],{"__ignoreMap":107},[18,163,164],{},"Sau khi khởi tạo thành công, bạn sẽ nhận được thông báo sau trong terminal:",[166,167],"img",{"className":168,"alt":107,"src":171,"style":172},[169,170],"block","mx-auto","https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19133739\u002Fimg01-e1708324777843.png","width: 100%;",[18,174,175,176,179],{},"Để chạy ứng dụng, di chuyển vào thư mục dự án và sử dụng lệnh ",[105,177,178],{},"meteor run",":",[98,181,184],{"className":182,"code":183,"language":103},[101],"cd simple-chat-meteor\nmeteor run\n",[105,185,183],{"__ignoreMap":107},[18,187,188,189,195],{},"Mở trình duyệt và truy cập ",[190,191,192],"a",{"href":192,"rel":193},"http:\u002F\u002Flocalhost:3000",[194],"nofollow"," để xem ứng dụng của bạn.",[166,197],{"className":198,"alt":107,"src":199,"style":172},[169,170],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19135639\u002Fimg02.png",[137,201,203],{"id":202},"_2-cấu-trúc-dự-án","2. Cấu trúc dự án:",[166,205],{"className":206,"alt":107,"src":207,"style":172},[169,170],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F20092348\u002Fimg09.png",[18,209,210,213],{},[14,211,212],{},".meteor",": Là thư mục quan trọng nhất trong bất kỳ ứng dụng Meteor nào. Nó chứa các tệp cấu hình cốt lõi của ứng dụng Meteor và các thư mục con như:",[215,216,217,224,230,236],"ul",{},[218,219,220,223],"li",{},[14,221,222],{},"local",": Chứa dữ liệu cục bộ và tệp nhật ký cho ứng dụng của bạn.",[218,225,226,229],{},[14,227,228],{},"packages",": Danh sách các thư viện mà ứng dụng của bạn sử dụng.",[218,231,232,235],{},[14,233,234],{},"platforms",": Các nền tảng mà ứng dụng của bạn hỗ trợ.",[218,237,238,241],{},[14,239,240],{},"versions",": Chứa phiên bản của các thư viện mà ứng dụng của bạn đang sử dụng.",[18,243,244,247],{},[14,245,246],{},"client",": Thư mục này dùng để chứa mã nguồn phía client của ứng dụng và thường bao gồm:",[215,249,250,256,262],{},[218,251,252,255],{},[14,253,254],{},"main.css",": Tệp CSS chính cho styles của client.",[218,257,258,261],{},[14,259,260],{},"main.html",": Tệp HTML chính, thường là điểm khởi đầu của ứng dụng với các định nghĩa layout và template.",[218,263,264,267],{},[14,265,266],{},"main.js",": Tệp JavaScript chính cho client, nơi bạn định nghĩa các sự kiện và helpers cho templates của Blaze.",[18,269,270,273],{},[14,271,272],{},"server",": Thư mục này dùng để chứa mã nguồn phía server của ứng dụng và thường bao gồm:",[215,275,276],{},[218,277,278,280],{},[14,279,266],{},": Tệp JavaScript chính cho server, nơi bạn thiết lập các publication và methods cũng như các cấu hình khởi tạo server.",[18,282,283,286],{},[14,284,285],{},"tests",": Thư mục này dùng để chứa các bài kiểm thử cho ứng dụng của bạn.",[18,288,289,292],{},[14,290,291],{},"node_modules",": Thư mục này dùng để chứa các gói NPM mà dự án của bạn phụ thuộc vào.",[137,294,296],{"id":295},"_3-thực-hiện-các-chức-năng","3. Thực hiện các chức năng:",[18,298,299],{},"Trong phần này, chúng ta sẽ đi qua các bước cần thiết để thêm chức năng vào ứng dụng chat Meteor của chúng ta, bao gồm cấu hình giao diện người dùng, xử lý sự kiện, và xây dựng chức năng đăng nhập, đăng xuất.",[18,301,302],{},[14,303,304],{},"Cấu Hình Giao Diện Người Dùng",[18,306,307,308,311],{},"Ở file “",[14,309,310],{},"client\u002Fmain.html","”: Chúng ta sẽ định nghĩa cấu trúc HTML cơ bản cho ứng dụng chat của bạn, bao gồm tiêu đề và các meta-tag để tối ưu hóa hiển thị trên thiết bị di động.",[98,313,316],{"className":314,"code":315,"language":103},[101],"\u003Chead>\n  \u003Ctitle>Simple Chat Meteor\u003C\u002Ftitle>\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \u002F>\n  \u003Cmeta charset=\"utf-8\"\u002F>\n  \u003Cmeta http-equiv=\"x-ua-compatible\" content=\"ie=edge\"\u002F>\n  \u003Cmeta\n      name=\"viewport\"\n      content=\"width=device-width, height=device-height, viewport-fit=cover, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no\"\n  \u002F>\n  \u003Cmeta name=\"mobile-web-app-capable\" content=\"yes\"\u002F>\n  \u003Cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\"\u002F>\n\u003C\u002Fhead>\n",[105,317,315],{"__ignoreMap":107},[18,319,307,320,323,324,327],{},[14,321,322],{},"client\u002Fmain.js","\", chúng ta import file \"",[14,325,326],{},"Chat.js","\" để khởi tạo UI chat.",[98,329,332],{"className":330,"code":331,"language":103},[101],"import '..\u002Fimports\u002Fui\u002FChat\u002FChat.js';\n\n",[105,333,331],{"__ignoreMap":107},[18,335,336],{},[14,337,338],{},"Xử Lý Sự Kiện và Dữ Liệu",[18,340,341,342,345],{},"Tạo thư mục “",[14,343,344],{},"imports","” để chứa mã nguồn phân chia theo UI và logic.",[18,347,348,349,352,353,356],{},"Trong “",[14,350,351],{},"imports\u002Fui\u002FChat","”, tạo file “",[14,354,355],{},"Chat.html","” để định nghĩa giao diện của ứng dụng chat.",[18,358,359],{},"Chúng ta sẽ sử dụng Blaze templates để tổ chức nội dung và logic hiển thị:",[98,361,364],{"className":362,"code":363,"language":103},[101],"\u003Cbody>\n    {{> chatContainer}}\n\u003C\u002Fbody>\n\n\u003Ctemplate name=\"chatContainer\">\n    \u003Cdiv class=\"chat-container\">\n        \u003Cdiv class=\"chat-header\">\n            \u003Ch2>Simple Chat Meteor\u003C\u002Fh2>\n        \u003C\u002Fdiv>\n\n        {{> chatContent}}\n    {{> chatInput}}\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Ctemplate name=\"chatContent\">\n    \u003Cdiv class=\"chat-content\">\n        {{#each chats}}\n        \u003Cdiv class=\"message\">\n            \u003Cdiv class=\"username\">{{ username }}\u003C\u002Fdiv>\n            \u003Cdiv class=\"message-content\">{{ messageText }}\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        {{\u002Feach}}\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Ctemplate name=\"chatInput\">\n    \u003Cdiv class=\"chat-input\">\n        \u003Cinput id=\"message\" type=\"text\" placeholder=\"Input your message...\" \u002F>\n        \u003Cbutton id=\"sendMessage\">Send\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[105,365,363],{"__ignoreMap":107},[18,367,368,371],{},[14,369,370],{},"chatContainer",": Đây là template chính chứa toàn bộ giao diện của ứng dụng chat, bao gồm tiêu đề của ứng dụng (chat-header) và phần nội dung chat.",[18,373,374,377,378,381,382,385,386,389],{},[14,375,376],{},"chatContent",": Một template nhỏ hơn, được sử dụng để hiển thị nội dung chat. Nó lặp qua mỗi “",[14,379,380],{},"chats","” (các tin nhắn), hiển thị “",[14,383,384],{},"username","” (tên người gửi) và “",[14,387,388],{},"messageText","” (nội dung tin nhắn).",[18,391,392,395,396,399],{},[14,393,394],{},"chatInput",": Một template khác cho phép người dùng gửi tin nhắn mới. Bao gồm một trường input để nhập tin nhắn và một nút “",[14,397,398],{},"Send","” để gửi tin nhắn.",[18,401,402,403,155],{},"Thêm css tại file “",[14,404,405],{},"client\u002Fmain.css",[98,407,410],{"className":408,"code":409,"language":103},[101],":root {\n  --main-bg-color: #f0f2f5;\n  --chat-bg-color: #fff;\n  --highlight-color: #f69914;\n  --text-color-white: #fff;\n  --border-color: #ddd;\n  --input-border-color: #ccc;\n  --hover-bg-color: #0056b3;\n}\n\nbody {\n  font-family: Arial, sans-serif;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  height: 100vh;\n  overflow: hidden;\n  margin: 0;\n  background-color: var(--main-bg-color);\n}\n\n.chat-container {\n  height: 100%;\n  width: 600px;\n  border: 1px solid var(--border-color);\n  background-color: var(--chat-bg-color);\n  display: flex;\n  flex-direction: column;\n}\n\n.chat-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  background-color: var(--highlight-color);\n  color: var(--text-color-white);\n  height: 68px;\n  padding: 0px 10px;\n}\n\n.chat-header button#logout {\n  height: 30px;\n  background-color: transparent;\n  cursor: pointer;\n  color: var(--text-color-white);\n  border: 1px solid var(--chat-bg-color);\n  border-radius: 10px;\n}\n\n.chat-content {\n  padding: 10px;\n  height: calc(100vh - 90px - 57px);\n  overflow-y: auto;\n  gap: 10px;\n}\n\n.chat-content .message {\n  background-color: #f1f1f1;\n  padding: 10px;\n  border-radius: 10px;\n  display: flex;\n  flex-direction: column;\n}\n\n.chat-content .message:not(:first-child) {\n  margin-top: 10px;\n}\n\n.chat-content .message .username {\n  font-weight: bold;\n  color: var(--highlight-color);\n}\n\n.chat-content .message .message-content {\n  margin-top: 5px;\n}\n\n.chat-input {\n  display: flex;\n  padding: 10px;\n  height: 57px;\n  box-sizing: border-box;\n}\n\n.chat-input input {\n  flex: 1;\n  padding: 10px;\n  margin-right: 10px;\n  border: 1px solid var(--input-border-color);\n  border-radius: 4px;\n}\n\n.chat-input button {\n  padding: 10px;\n  background-color: var(--highlight-color);\n  color: white;\n  border: none;\n  border-radius: 4px;\n  cursor: pointer;\n}\n\n.chat-input button:hover {\n  background-color: var(--hover-bg-color);\n}\n\n.auth-container {\n  background-color: white;\n  padding: 40px;\n  border-radius: 5px;\n  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);\n  width: 300px;\n}\n\n.auth-container h2 {\n  text-align: center;\n  margin-bottom: 20px;\n}\n\n.auth-container p span {\n  text-decoration: underline;\n  color: var(--hover-bg-color);\n  cursor: pointer;\n}\n\n#loginForm .input-group,\n#signupForm .input-group {\n  margin-bottom: 15px;\n  display: flex;\n  flex-direction: column;\n}\n\n#loginForm .input-group label,\n#signupForm .input-group label {\n  margin-bottom: 5px;\n}\n\n#loginForm .input-group input,\n#signupForm .input-group input {\n  padding: 10px;\n  border: 1px solid var(--border-color);\n  border-radius: 4px;\n}\n\n#loginForm button,\n#signupForm button {\n  background-color: orange;\n  color: white;\n  padding: 10px;\n  border: none;\n  border-radius: 4px;\n  cursor: pointer;\n  width: 100%;\n}\n\n#loginForm button:hover,\n#signupForm button:hover {\n  background-color: darkorange;\n}\n",[105,411,409],{"__ignoreMap":107},[18,413,414,415,418,419,422],{},"Trong thư mục “",[14,416,417],{},"imports\u002Fapi","” tạo file “",[14,420,421],{},"ChatsCollection.js","”",[98,424,427],{"className":425,"code":426,"language":103},[101],"import { Mongo } from 'meteor\u002Fmongo';\n\nexport const ChatsCollection = new Mongo.Collection('chats');\n",[105,428,426],{"__ignoreMap":107},[18,430,414,431,418,433,422],{},[14,432,417],{},[14,434,435],{},"ChatsPublications.js",[98,437,440],{"className":438,"code":439,"language":103},[101],"import { Meteor } from \"meteor\u002Fmeteor\";\n\nimport { ChatsCollection } from \".\u002FChatsCollection\";\n\nMeteor.publish(\"chats\", function () {\n  return ChatsCollection.find({}, { sort: { createdAt: -1 } });\n});\n",[105,441,439],{"__ignoreMap":107},[18,443,444],{},"Tiếp theo, chúng ta sẽ kết nối với cơ sở dữ liệu chat trong Meteor, đăng ký dữ liệu chat từ server và sử dụng nó để hiển thị các tin nhắn theo thứ tự thời gian trong giao diện người dùng.",[18,446,447,448,155],{},"Thêm đoạn Code sau vào file “",[14,449,450],{},"imports\u002Fui\u002FChat\u002FChat.js",[98,452,455],{"className":453,"code":454,"language":103},[101],"import { Template } from \"meteor\u002Ftemplating\";\nimport { ChatsCollection } from \"\u002Fimports\u002Fapi\u002FChatsCollection\";\nimport \".\u002FChat.html\";\n\nMeteor.subscribe(\"chats\");\n\nTemplate.chatContent.helpers({\n  chats() {\n    return ChatsCollection.find({}, { sort: { createdAt: 1 } });\n  },\n});\n",[105,456,454],{"__ignoreMap":107},[18,458,459,460,463],{},"Thay thế đoạn code của file “",[14,461,462],{},"server\u002Fmain.js","” như sau:",[98,465,468],{"className":466,"code":467,"language":103},[101],"import { Meteor } from \"meteor\u002Fmeteor\";\nimport { ChatsCollection } from \"\u002Fimports\u002Fapi\u002FChatsCollection\";\nimport '\u002Fimports\u002Fapi\u002FChatsPublications';\n\nMeteor.startup(() => {\n  if (ChatsCollection.find().count() === 0) {\n    ChatsCollection.insert({\n      messageText: 'Welcome to the chat app!',\n      createdAt: new Date(),\n      username: \"Admin\",\n    });\n  }\n});\n",[105,469,467],{"__ignoreMap":107},[18,471,472],{},"Mục đích là để khi khởi động server, thì sẽ kiểm tra nếu như chưa có message nào thì sẽ thêm vào message mặc định.",[18,474,475,476,478,479,483],{},"→ Khởi động ứng dụng Meteor.js bằng lệnh ",[105,477,178],{}," và truy cập vào địa chỉ ",[190,480,192],{"href":481,"rel":482},"http:\u002F\u002Flocalhost:1337\u002Farticle",[194]," để xem kết quả.",[166,485],{"className":486,"alt":107,"src":487,"style":172},[169,170],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19160033\u002Fimg04.png",[18,489,490],{},[14,491,492],{},"Thực hiện chức năng gửi tin nhắn:",[18,494,495,496,498],{},"Trong file “",[14,497,450],{},"”, thêm đoạn code sau:",[98,500,503],{"className":501,"code":502,"language":103},[101],"...\n\nTemplate.body.events({\n  \"click #sendMessage\": function () {\n    const messageElement = document.querySelector(\"#message\");\n    if (messageElement.value.trim()) {\n      Meteor.call(\"chats.sendMessage\", messageElement.value.trim());\n      messageElement.value = \"\";\n    }\n  },\n});\n",[105,504,502],{"__ignoreMap":107},[18,506,414,507,352,509,512],{},[14,508,417],{},[14,510,511],{},"ChatsMethods.js","” và thêm đoạn code sau:",[98,514,517],{"className":515,"code":516,"language":103},[101],"import { Meteor } from 'meteor\u002Fmeteor';\nimport { ChatsCollection } from \".\u002FChatsCollection\";\n\nMeteor.methods({\n    'chats.sendMessage'(message) {\n      ChatsCollection.insert({\n        messageText: message,\n        createdAt: new Date(),\n        username: \"Admin\"\n      });\n    }\n});\n",[105,518,516],{"__ignoreMap":107},[18,520,521,522,155],{},"Tiếp theo, chúng ta sẽ import method vừa mới tạo vào file “",[14,523,462],{},[98,525,528],{"className":526,"code":527,"language":103},[101],"...\nimport '\u002Fimports\u002Fapi\u002FChatsMethods';\n...\n",[105,529,527],{"__ignoreMap":107},[18,531,532],{},"Hãy thử gửi 1 tin nhắn bất kì nhé:",[166,534],{"className":535,"alt":107,"src":536,"style":172},[169,170],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19161822\u002Fimg05.png",[18,538,539],{},[14,540,541],{},"Thực hiện chức năng Login, Register và Logout:",[18,543,544,545,551,552,155],{},"Chúng ta sẽ cài đặt packages “",[14,546,547],{},[548,549,550],"em",{},"accounts-password","” và “",[14,553,554],{},[548,555,556],{},"bcrypt",[98,558,561],{"className":559,"code":560,"language":103},[101],"meteor add accounts-password\nmeteor npm install --save bcrypt\n",[105,562,560],{"__ignoreMap":107},[18,564,565,566,569,570,512],{},"Tại thư mục “",[14,567,568],{},"imports\u002Fui\u002FAuth","” tạo file ",[14,571,572],{},"“Auth.html",[98,574,577],{"className":575,"code":576,"language":103},[101],"\u003Ctemplate name=\"authContainer\">\n    {{> Template.dynamic template=currentView}}\n\u003C\u002Ftemplate>\n\n\u003Ctemplate name=\"loginContainer\">\n    \u003Cdiv class=\"auth-container\" >\n        \u003Ch2>Login\u003C\u002Fh2>\n        \u003Cform id=\"loginForm\">\n            \u003Cdiv class=\"input-group\">\n                \u003Clabel for=\"username\">Username\u003C\u002Flabel>\n                \u003Cinput type=\"text\" id=\"username\" name=\"username\" required>\n            \u003C\u002Fdiv>\n            \u003Cdiv class=\"input-group\">\n                \u003Clabel for=\"password\">Password\u003C\u002Flabel>\n                \u003Cinput type=\"password\" id=\"password\" name=\"password\" required>\n            \u003C\u002Fdiv>\n            \u003Cbutton type=\"submit\">Login\u003C\u002Fbutton>\n        \u003C\u002Fform>\n\n        \u003Cp>Don’t have an account yet?\n            \u003Cspan>Sign up\u003C\u002Fspan>\n        \u003C\u002Fp>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Ctemplate name=\"signupContainer\">\n    \u003Cdiv class=\"auth-container\" >\n        \u003Ch2>Sign up\u003C\u002Fh2>\n        \u003Cform id=\"signupForm\">\n            \u003Cdiv class=\"input-group\">\n                \u003Clabel for=\"username\">Username\u003C\u002Flabel>\n                \u003Cinput type=\"text\" id=\"username\" name=\"username\" required>\n            \u003C\u002Fdiv>\n            \u003Cdiv class=\"input-group\">\n                \u003Clabel for=\"password\">Password\u003C\u002Flabel>\n                \u003Cinput type=\"password\" id=\"password\" name=\"password\" required>\n            \u003C\u002Fdiv>\n            \u003Cdiv class=\"input-group\">\n                \u003Clabel for=\"confirmPassword\">Confirm Password\u003C\u002Flabel>\n                \u003Cinput type=\"password\" id=\"rePassword\" name=\"confirmPassword\" required>\n            \u003C\u002Fdiv>\n            \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n        \u003C\u002Fform>\n\n        \u003Cp>Already have an account?\n            \u003Cspan>Login here\u003C\u002Fspan>\n        \u003C\u002Fp>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[105,578,576],{"__ignoreMap":107},[18,580,565,581,418,583,512],{},[14,582,568],{},[14,584,585],{},"Auth.js",[98,587,590],{"className":588,"code":589,"language":103},[101],"import { Template } from \"meteor\u002Ftemplating\";\nimport \".\u002FAuth.html\";\n\nimport { ReactiveVar } from \"meteor\u002Freactive-var\";\n\ncurrentView = new ReactiveVar(\"loginContainer\");\n\nTemplate.authContainer.helpers({\n  currentView: function () {\n    return currentView.get();\n  },\n});\n\nTemplate.loginContainer.events({\n  \"click span\": function (e) {\n    e.preventDefault();\n    currentView.set(\"signupContainer\");\n  },\n  \"submit #loginForm\"(e) {\n    e.preventDefault();\n\n    const target = e.target;\n\n    const username = target.username.value;\n    const password = target.password.value;\n\n    Meteor.loginWithPassword(username, password, (err) => {\n      if (err) {\n        alert(err);\n      }\n    });\n  },\n});\n\nTemplate.signupContainer.events({\n  \"click span\": function (e) {\n    e.preventDefault();\n    currentView.set(\"loginContainer\");\n  },\n  \"submit #signupForm\"(e) {\n    e.preventDefault();\n\n    const target = e.target;\n\n    const username = target.username.value;\n    const password = target.password.value;\n    const confirmPassword = target.confirmPassword.value;\n\n    if (password !== confirmPassword) {\n      alert(\"Password do not match\");\n      return;\n    }\n\n    Accounts.createUser(\n      {\n        username,\n        password,\n      },\n      (err) => {\n        if (err) {\n          alert(err);\n        }\n      }\n    );\n  },\n});\n",[105,591,589],{"__ignoreMap":107},[18,593,307,594,597],{},[14,595,596],{},"imports\u002Fui\u002FChat\u002FChat.html","” chúng ta sẽ tiến hành kiểm tra xem user đã login hay chưa, và thêm nút logout:",[98,599,602],{"className":600,"code":601,"language":103},[101],"\u003Cbody>\n    {{#if isUserLogged}} {{> chatContainer}} {{else}} {{> authContainer}} {{\u002Fif}}\n\u003C\u002Fbody>\n\n...\n    \u003Cdiv class=\"chat-header\">\n        \u003Ch2>Simple Chat Meteor\u003C\u002Fh2>\n        \u003Cbutton id=\"logout\">Logout\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n...\n",[105,603,601],{"__ignoreMap":107},[18,605,307,606,608],{},[14,607,450],{},"”, chúng ta sẽ kiểm tra xem user đã login hay chưa, và xử lý logout:",[98,610,613],{"className":611,"code":612,"language":103},[101],"import \"..\u002FAuth\u002FAuth.js\"\n...\nTemplate.body.helpers({\n  isUserLogged() {\n    return !!Meteor.userId() && !Meteor.loggingIn();\n  },\n});\n\nTemplate.body.events({\n  \"click #sendMessage\": function () {\n    const messageElement = document.querySelector(\"#message\");\n    if (messageElement.value.trim()) {\n      Meteor.call(\"chats.sendMessage\", messageElement.value.trim());\n      messageElement.value = \"\";\n    }\n  },\n  \"click #logout\": function () {\n    Meteor.logout();\n  },\n});\n",[105,614,612],{"__ignoreMap":107},[18,616,307,617,619,620,623,624,627],{},[14,618,462],{},"”,  kiểm tra và thêm account “admin” nếu nó không tồn tại, và thêm “",[548,621,622],{},"username”"," cho “",[548,625,626],{},"message”"," mặc định:",[98,629,632],{"className":630,"code":631,"language":103},[101],"import { Meteor } from \"meteor\u002Fmeteor\";\nimport '\u002Fimports\u002Fapi\u002FChatsMethods';\nimport '\u002Fimports\u002Fapi\u002FChatsPublications';\nimport { ChatsCollection } from \"\u002Fimports\u002Fapi\u002FChatsCollection\";\nimport { Accounts } from 'meteor\u002Faccounts-base';\n\nconst SEED_USERNAME = \"admin\";\nconst SEED_PASSWORD = \"admin\";\n\nMeteor.startup(() => {\n  if (!Accounts.findUserByUsername(SEED_USERNAME)) {\n    Accounts.createUser({\n      username: SEED_USERNAME,\n      password: SEED_PASSWORD,\n    });\n  }\n\n  const user = Accounts.findUserByUsername(SEED_USERNAME);\n\n  if (ChatsCollection.find().count() === 0) {\n    ChatsCollection.insert({\n      messageText: 'Welcome to the chat app!',\n      createdAt: new Date(),\n      username: user.username,\n    });\n  }\n});\n",[105,633,631],{"__ignoreMap":107},[18,635,636,637,639,640,643,644,155],{},"Tương tự, chúng ta cũng thêm “",[548,638,622],{}," cho methods “",[548,641,642],{},"sendMessage”"," ở file “",[14,645,646],{},"imports\u002Fapi\u002FChatsMethods.js",[98,648,651],{"className":649,"code":650,"language":103},[101],"import { Meteor } from 'meteor\u002Fmeteor';\nimport { ChatsCollection } from \".\u002FChatsCollection\";\n\nMeteor.methods({\n  'chats.sendMessage'(message) {\n    const username = Meteor.user()?.username;\n\n    ChatsCollection.insert({\n      messageText: message,\n      createdAt: new Date(),\n      username: username\n    });\n  }\n});\n",[105,652,650],{"__ignoreMap":107},[18,654,655],{},"Cùng xem thành quả nhé:",[166,657],{"className":658,"alt":107,"src":659,"style":172},[169,170],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19172343\u002Fimg06.png",[18,661,662],{},[548,663,664],{},"Ở trạng thái chưa đăng nhập, hiển thị màn hình Login.",[166,666],{"className":667,"alt":107,"src":668,"style":172},[169,170],"https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19172346\u002Fimg07.png",[18,670,671],{},[548,672,673],{},"Màn hình Sign up",[166,675],{"className":676,"alt":107,"src":678,"style":172},[169,170,677],"mb-4","https:\u002F\u002Fs3-ap-southeast-1.amazonaws.com\u002Fhomepage-media\u002Fwp-content\u002Fuploads\u002F2024\u002F02\u002F19172349\u002Fimg08.png",[10,680,682],{"id":681},"so-sánh-với-expressjs","So sánh với Express.js",[684,685,687],"h3",{"id":686},"khả-năng-mở-rộng","Khả Năng Mở Rộng",[215,689,690,695],{},[218,691,692,694],{},[14,693,134],{},": Cung cấp một “bộ công cụ phát triển” với các tính năng và bộ phận đã được xây dựng sẵn, Meteor.js giúp quá trình phát triển nhanh chóng và dễ dàng hơn. Tuy nhiên, điều này có thể giới hạn khả năng tùy chỉnh và mở rộng ở một số dự án lớn.",[218,696,697,700],{},[14,698,699],{},"Express.js",": Là một framework linh hoạt, Express.js cung cấp môi trường cho phép các nhà phát triển tự do xây dựng cấu trúc ứng dụng theo ý muốn, làm cho nó trở nên lý tưởng cho các dự án lớn và phức tạp với yêu cầu mở rộng cao.",[684,702,704],{"id":703},"giao-tiếp-thời-gian-thực","Giao Tiếp Thời Gian Thực",[215,706,707,712],{},[218,708,709,711],{},[14,710,134],{},": Được trang bị sẵn khả năng giao tiếp thời gian thực giữa máy khách và máy chủ, giúp dữ liệu được cập nhật ngay lập tức mà không cần tải lại trang.",[218,713,714,716],{},[14,715,699],{},": Để đạt được tính năng tương tự, cần phải tích hợp thêm các thư viện bên thứ ba, tăng thêm công số phát triển.",[684,718,720],{"id":719},"tích-hợp-cơ-sở-dữ-liệu","Tích Hợp Cơ Sở Dữ Liệu",[215,722,723,728],{},[218,724,725,727],{},[14,726,134],{},": Tích hợp chặt chẽ với MongoDB và tối ưu cho việc sử dụng nó, giúp quản lý dữ liệu dễ dàng nhưng giới hạn lựa chọn.",[218,729,730,732],{},[14,731,699],{},": Hỗ trợ kết nối với đa dạng cơ sở dữ liệu, từ SQL đến NoSQL, cho phép linh hoạt chọn lựa dựa trên nhu cầu dự án.",[684,734,736],{"id":735},"cộng-đồng-và-hệ-sinh-thái","Cộng Đồng và Hệ Sinh Thái",[215,738,739,744],{},[218,740,741,743],{},[14,742,134],{},": Dù cộng đồng nhỏ hơn, nhưng vẫn cung cấp đủ các gói và công cụ để hỗ trợ nhu cầu phát triển.",[218,745,746,748],{},[14,747,699],{},": Có một cộng đồng lớn và đa dạng với hàng ngàn thư viện và công cụ bổ sung, cung cấp nhiều lựa chọn để mở rộng và cải thiện ứng dụng.",[684,750,752],{"id":751},"tốc-độ-phát-triển-và-độ-khó-học","Tốc Độ Phát Triển và Độ Khó Học",[215,754,755,760],{},[218,756,757,759],{},[14,758,134],{},": Tăng tốc độ phát triển ứng dụng bằng cách tích hợp sẵn nhiều chức năng, giảm thiểu công việc cấu hình và quản lý. Lộ trình học tương đối dễ tiếp cận, thích hợp cho người mới bắt đầu.",[218,761,762,764],{},[14,763,699],{},": Cung cấp cách tiếp cận linh hoạt và tùy chỉnh cao, có thể mất thêm thời gian để thiết lập nhưng mang lại lợi ích về lâu dài khi phát triển dự án. Đòi hỏi sự hiểu biết về Node.js và cách thức tích hợp các middleware, phù hợp với những nhà phát triển có kinh nghiệm.",[10,766,768],{"id":767},"kết-luận","Kết luận",[18,770,771],{},"Từ những phân tích trên, có thể thấy rằng Express.js và Meteor đều có những ưu và nhược điểm riêng biệt, phù hợp với các loại dự án và nhu cầu phát triển khác nhau:",[18,773,774,776],{},[14,775,699],{}," là lựa chọn tốt cho những dự án cần mức độ tùy chỉnh cao và khả năng mở rộng lớn. Nó đặc biệt phù hợp với những dự án phức tạp, đa dạng về cơ sở dữ liệu và yêu cầu một hệ sinh thái lớn với nhiều lựa chọn thư viện và công cụ.",[18,778,779,780,782],{},"Ngược lại, ",[14,781,134],{}," là sự chọn lựa hoàn hảo cho việc phát triển nhanh chóng và dễ dàng, đặc biệt là với các ứng dụng thời gian thực và dự án nhỏ đến trung bình cần đến MongoDB. Nó phù hợp với các nhà phát triển muốn tập trung vào việc triển khai ý tưởng nhanh chóng mà không mất quá nhiều thời gian vào cấu hình và quản lý hạ tầng.",[18,784,785],{},"Tóm lại, lựa chọn giữa Express.js và Meteor phụ thuộc vào yêu cầu cụ thể của dự án, kỹ năng và kinh nghiệm của đội ngũ phát triển, cũng như mục tiêu và khung thời gian dự án. Cả hai công nghệ đều có những điểm mạnh riêng biệt, và việc lựa chọn công nghệ phù hợp sẽ góp phần quan trọng vào sự thành công của dự án.",[10,787,789],{"id":788},"tài-liệu-tham-khảo","Tài liệu tham khảo",[18,791,792],{},[190,793,794],{"href":794,"rel":795},"https:\u002F\u002Fdocs.meteor.com\u002F",[194],[18,797,798],{},[190,799,800],{"href":800,"rel":801},"https:\u002F\u002Fwww.blazejs.org\u002F",[194],[18,803,804],{},[190,805,806],{"href":806,"rel":807},"https:\u002F\u002Fexpressjs.com\u002F",[194],{"title":107,"searchDepth":809,"depth":809,"links":810},2,[811,812],{"id":85,"depth":809,"text":88},{"id":127,"depth":809,"text":128,"children":813},[814,816,817,818,819],{"id":686,"depth":815,"text":687},3,{"id":703,"depth":815,"text":704},{"id":719,"depth":815,"text":720},{"id":735,"depth":815,"text":736},{"id":751,"depth":815,"text":752},"tech talk","HOANG PHAN THANH","2025-03-31","Meteor (Meteor.js) là một framework JavaScript Full-Stack, được xây dựng dựa trên Node.js, giúp phát triển các ứng dụng web và mobile một cách nhanh chóng và hiệu quả. Với thiết kế tối ưu hóa cho sự tích hợp mượt mà giữa client và server, Meteor không chỉ giúp đơn giản hóa quá trình chia sẻ code mà còn nâng cao trải nghiệm với dữ liệu thời gian thực. Nếu bạn đang tìm kiếm một giải pháp phát triển ứng dụng nhanh chóng hoặc muốn mở rộng kiến thức về các công nghệ web hiện đại, bài viết này sẽ cung cấp cái nhìn tổng quan và sâu sắc về Meteor.","md",{},true,"\u002Fvi\u002Fnews\u002Fxay-dung-ung-dung-chat-realtime-voi-framework-meteor-js",null,{"title":5,"description":823},"vi\u002Fnews\u002Fxay-dung-ung-dung-chat-realtime-voi-framework-meteor-js","https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2026\u002F06\u002F05080300\u002Fmeteor-logo-2.png","lI1BPLeRnL1L92-5w4emlC3RvxoZQ-m9OW7QV-2zhvo",1782205038753]