Sử dụngCoffeeScriptởphíamáykhách
Ứng dụng tìm kiếm được viết bằng CoffeeScript
Các chữ viết tắt thường dùng
JSON: JavaScript Object Notation (Ký pháp đối tượng JavaScript)
UI: User interface (Giao diện người dùng)
Ứng dụng mẫu mà bạn sẽ tạo ra cho phép người dùng nhập một thuật ngữ tìm kiếm, tìm kiếm cả
trên Google lẫn Twitter, rồi hiển thị các kết quả kết hợp. Phíamáykhách của ứng dụng nhận dữ
liệu do người dùng nhập vào và gửi nó đến máy chủ để tìm các kết quả. Khi máy chủ trả về các
kết quả, phíamáykhách tạo ra các phần tử giao diện người dùng (UI) cho các kết quả đó và hiển
thị chúng trên màn hình. Bây giờ, hãy đừng lo lắng gì về phíamáy chủ hoạt động như thế nào.
Bắt đầu bằng cách định nghĩa mô hình dữ liệu cho ứng dụng. Ứng dụng sẽ hiển thị các kết quả
tìm kiếm, vì vậy hãy định nghĩa một lớp SearchResult. Liệt kê 1 cho thấy định nghĩa này.
Liệt kê 1. Lớp SearchResult cơ sở
class SearchResult
constructor: (data) ->
@title = data.title
@link = data.link
@extras = data
toHtml: -> "<a href='#{@link}'>#{@title}</a>"
toJson: -> JSON.stringify @extras
Lớp SearchResult khá đơn giản. Nó:
Bắt đầu với một hàm tạo (constructor) định nghĩa hai biến thành viên: title (tiêu đề) và
link (liên kết).
Tìm kiếm hai giá trị này trong đối tượng dữ liệu được chuyển tới hàm tạo.
Lưu trữ phần còn lại của dữ liệu được chuyển tới một biến thành viên có tên là extras.
Điều này sẽ thuận tiện vì bạn biết bạn sẽ có hai loại kết quả tìm kiếm khác nhau trong
ứng dụng (các kết quả từ Google và các kết quả từ Twitter).
Định nghĩa hai phương thức cho lớp SearchResult:
o toHtml, tạo ra một chuỗi HTML đơn giản từ cá thể SearchResult, lợi dụng phép
nội suy chuỗi của CoffeeScript.
o toJson, dùng để chuyển đối tượng SearchResult thành một chuỗi JSON.
Lớp trong Liệt kê 1 cung cấp các tính năng cơ bản của một kết quả tìm kiếm. Bạn sẽ nhận được
nhiều dữ liệu hơn từ các kết quả tìm kiếm của cả Google lẫn Twitter. Mô hình hóa kết quả này
bằng cách tạo ra các lớp con cho từng kiểu kết quả tìm kiếm. Liệt kê 2 cho thấy kiểu kết quả tìm
kiếm của Google.
Liệt kê 2. Lớp GoogleSearchResult
class GoogleSearchResult extends SearchResult
constructor: (data) ->
super data
@content = @extras.content
toHtml: ->
"#{super} <div class='snippet'>#{@content}</div>"
Liệt kê 2 cho thấy việc lập trình hướng đối tượng bằng CoffeeScript dễ như thế nào. Lớp
GoogleSearchResult là lớp mở rộng (kế thừa) từ lớp cơ sở SearchResult ở Liệt kê 1. Hàm tạo
của nó gọi hàm tạo của lớp cha (superclass). Nếu bạn đã từng thực hiện sự thừa kế theo phong
cách-lớp bằng JavaScript, thì bạn biết việc này khó khăn đến mức nào. Liệt kê 3 cho thấy mã
JavaScript được tạo ra.
Liệt kê 3. Mã JavaScript của lớp GoogleSearchResult
GoogleSearchResult = (function() {
__extends(GoogleSearchResult, SearchResult);
function GoogleSearchResult(data) {
GoogleSearchResult.__super__.constructor.call(this, data);
this.content = this.extras.content;
}
GoogleSearchResult.prototype.toHtml = function() {
return "" + GoogleSearchResult.__super__.toHtml.apply(this, arguments)
+ " <div class='snippet'>" + this.content + "</div>";
};
return GoogleSearchResult;
})();
Để gọi hàm tạo của lớp cha, bạn phải giữ một cá thể của lớp cha trong biến __super__ (có thể
dùng bất kỳ tên nào) và sau đó gọi hàm tạo một cách tường minh. Quay lại Liệt kê 2, bạn có thể
thấy việc này dễ dàng hơn bao nhiêu trong CoffeeScript. Lưu ý rằng ví dụ này đã định nghĩa một
biến cá thể mới được gọi là content (nội dung) trong lớp GoogleSearchResult. Về cơ bản, đây
là một đoạn HTML từ trang web do kết quả tìm kiếm trỏ tới. Không có gì đáng ngạc nhiên là lớp
GoogleSearchResult có đoạn này, nhưng lớp cơ sở lại không có. Cuối cùng, lưu ý việc nạp
chồng (override) của phương thức toHtml. Ví dụ này sửdụng phương thức toHtml của lớp cha
nhưng còn gắn thêm một div bổ sung có đoạn nội dung đó. Xem lại Liệt kê 3 để thấy bước gọi
này đến phương thức toHtml của lớp cha được thực hiện như thế nào. Vì bạn có một lớp con
GoogleSearchResult , nên bạn cũng cần một lớp con TwitterSearchResult, như thể hiện
trong Liệt kê 4.
Liệt kê 4. Lớp TwitterSearchResult
class TwitterSearchResult extends SearchResult
constructor: (data) ->
super data
@source = @extras.from_user
@link = "http://twitter.com/#{@source}/status/#{@extras.id_str}"
@title = @extras.text
toHtml: ->
"<a href='http://twitter.com/#{@source}'>@#{@source}</a>: #{super}"
Lớp TwitterSearchResult cũng giống như lớp GoogleSearchResult trong Liệt kê 2. Hàm tạo
của nó cũng sửdụng hàm tạo của lớp cha. Cũng:
Định nghĩa biến thành viên riêng của mình có tên là source (nguồn).
Xây dựng biến thành viên link bằng cách sửdụng một khuôn mẫu chuỗi ký tự và các
biến thành viên của nó.
Đặt lại giá trị biến thành viên title đến trường khác từ dữ liệu đầu vào.
Ghi chồng phương thức toHtml của lớp cha, gắn thêm một liên kết đến người dùng đã
tạo ra tweet.
Một lần nữa, phép nội suy chuỗi của CoffeeScript làm cho việc sửdụng phương thức toHtml của
lớp cha trở nên dễ dàng khi tạo một phương thức mới. Để gọi phương thức toHtml của lớp cha,
bạn chỉ cần dùng từ khóa super. Bạn có thể dùng cách gọi super.toHtml, nhưng không cần
phải làm thế và thực ra nếu gọi như vậy sẽ báo lỗi. Chỉ cần dùng như trên thì CoffeeScript đã
hiểu rằng bạn muốn gọi chính phương thức tương tự từ lớp cha, điều này giúp bạn viết mã dễ
dàng hơn một chút mà thôi.
Bây giờ bạn có các cấu trúc dữ liệu mà ứng dụng sẽ cần và bạn có thể bắt đầu viết một số xử lý ở
phía máy khách. Sẽ dễ dàng hơn nhiều nếu thử nghiệm mã với một tầng làm việc bên dưới. Vì
không chỉ có một tầng làm việc bên dưới, chúng ta hãy tiếp tục tìm hiểu phần thú vị nhất: dữ liệu
giả định.
Về đầu trang
Sử dụng dữ liệu giả định
Khi xây dựng một ứng dụngmáy chủ - máykhách như một ứng dụng web hiện đại, điều luôn có
nghĩa là tạo giao diện dùng chung, nơi hai phần của ứng dụng gặp nhau và sau đó tạo ra dữ liệu
giả định. Điều này cho phép bạn phát triển các phần ứng dụngphíamáykhách và phíamáy chủ
song song với nhau. Cách tiếp cận này đặc biệt tốt với CoffeeScript vì bạn có thể sửdụng cùng
một ngôn ngữ lập trình trên cả phíamáykhách lẫn phíamáy chủ. Liệt kê 5 cho thấy các kết quả
tìm kiếm giả định từ Google.
Liệt kê 5. Các kết quả tìm kiếm giả định của Google
mockGoogleData = [
GsearchResultClass:"GwebSearch",
link:"http://jashkenas.github.com/coffee-script/",
url:"http://jashkenas.github.com/coffee-script/",
visibleUrl:"jashkenas.github.com",
cacheUrl:"http://www.google.com/search?q\u003dcache:nuWrlCK4-
v4J:jashkenas
.github.com",
title:"\u003cb\u003eCoffeeScript\u003c/b\u003e",
titleNoFormatting:"CoffeeScript",
content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a little language
that
compiles into JavaScript. Underneath all of those embarrassing braces and
semicolons, JavaScript has always had a \u003cb\u003e \u003c/b\u003e"
,
GsearchResultClass:"GwebSearch",
link:"http://en.wikipedia.org/wiki/CoffeeScript",
url:"http://en.wikipedia.org/wiki/CoffeeScript",
visibleUrl:"en.wikipedia.org",
cacheUrl:"http://www.google.com/search?q\u003dcache:wshlXQEIrhIJ
:en.wikipedia.org",
title:"\u003cb\u003eCoffeeScript\u003c/b\u003e - Wikipedia, the free
encyclopedia",
titleNoFormatting:"CoffeeScript - Wikipedia, the free encyclopedia",
content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a programming
language
that transcompiles to JavaScript. The language adds syntactic sugar
inspired by
Ruby, Python and Haskell to enhance \u003cb\u003e \u003c/b\u003e"
,
GsearchResultClass:"GwebSearch",
link:"http://codelikebozo.com/why-im-switching-to-coffeescript",
url:"http://codelikebozo.com/why-im-switching-to-coffeescript",
visibleUrl:"codelikebozo.com",
cacheUrl:"http://www.google.com/search?q\u003dcache:VDKirttkw30J:
codelikebozo.com",
title:"Why I\u0026#39;m (Finally) Switching to
\u003cb\u003eCoffeeScript
\u003c/b\u003e - Code Like Bozo",
titleNoFormatting:"Why I\u0026#39;m (Finally) Switching to
CoffeeScript -
Code Like Bozo",
content:"Sep 5, 2011 \u003cb\u003e \u003c/b\u003e You may have
already heard
about \u003cb\u003eCoffeeScript\u003c/b\u003e and some of the hype
surrounding it
but you still have found several reasons to not make the \u003cb\u003e
\u003c/b\u003e"
]
Điều hiển nhiên là việc tạo dữ liệu giả định với cú pháp đơn giản của CoffeeScript còn dễ dàng
hơn. Ví dụ này cho thấy các chữ hay số viết tắt (literals) của đối tượng trong CoffeeScript. Liệt
kê 5 là một mảng. Sửdụng thụt đầu dòng thêm để chỉ báo một đối tượng và mỗi đặc tính của đối
tượng lại được thụt vào nữa. Mã này rất tiện để so sánh với JSON. Khoảng trống thay cho các
dấu ngoặc nhọn. Chúng giống như các chữ hay số viết tắt của JavaScript, ở đây các thuộc tính
không nằm trong các dấu nháy. Với JSON các thuộc tính này cũng phải được đặt trong dấu nháy.
Liệt kê 6 cho thấy dữ liệu giả định tương tự với các kết quả tìm kiếm của Twitter.
Liệt kê 6. Các kết quả tìm kiếm giả định của Twitter
mockTwitterData = [
created_at:"Wed, 09 Nov 2011 04:18:49 +0000",
from_user:"jashkenas",
from_user_id:123323498,
from_user_id_str:"123323498",
geo:null,
id:134122748057370625,
id_str:"134122748057370625",
iso_language_code:"en",
metadata:
recent_retweets:4,
result_type:"popular"
profile_image_url:"http://a3.twimg.com/profile_images/1185870726/gravatar
_normal.jpg",
source:"<a
href="http://itunes.apple.com/us/app/twitter/id409789998?mt
=12" rel="nofollow">Twitter for Mac</a>",
text:""CoffeeScript [is] the closest I felt to the power I had twenty
years ago in Smalltalk" - Ward Cunningham (http://t.co/2Wve2V4l) Nice.",
to_user_id:null,
to_user_id_str:null
]
Dữ liệu giả định trong Liệt kê 6 giống như dữ liệu giả định trong Liệt kê 5, nhưng với các trường
đặc thù riêng của Twitter. Bây giờ bạn chỉ cần tạo ra một giao diện trả về dữ liệu giả định này.
Liệt kê 7 cho thấy một lớp khác thực hiện chính điều đó.
Liệt kê 7. Một lớp của máy tìm kiếm giả định
class MockSearch
search: (query, callback) ->
results =
google: (new GoogleSearchResult obj for obj in mockGoogleData)
twitter: (new TwitterSearchResult obj for obj in mockTwitterData)
callback results
Lớp MockSearch có một phương thức đơn lẻ được gọi là search (tìm kiếm). Nó có hai tham số:
query (truy vấn) để sửdụng cho việc tìm kiếm và một hàm callback (gọi lại). Lớp MockSearch
trả về các kết quả nhanh chóng, nhưng một việc tìm kiếm thực sự sẽ cần truyền qua mạng để nói
chuyện với các máy chủ. Để xử lý việc này trong JavaScript và để đảm bảo rằng bạn không làm
cho UI bị treo, bạn thường sửdụng một hàm callback.
Bước tiếp theo tạo ra một đối tượng có tên là results (kết quả). Bạn sẽ sửdụng lại cú pháp chữ
hay số viết tắt của đối tượng của CoffeeScript. Đối tượng results có hai trường: google và
twitter. Các giá trị của mỗi trường được biểu thị bằng cách sửdụng một phép gồm mảng. Biểu
thức sẽ tạo ra một mảng kiểu SearchResult thích hợp (GoogleSearchResult dùng cho Google
và TwitterSearchResult dùng cho Twitter). Cuối cùng, hàm callback được gọi với đối tượng
results được chuyển cho nó.
Việc tìm kiếm giả định sẽ làm việc, do đó, bạn cần sẵn sàng viết mã giao diện người dùng (UI) ở
phía máy khách.
Về đầu trang
Mang tất cả vào trong trình duyệt
Trước khi liên kết giao diện người dùng với mã ứng dụng, đầu tiên hãy xem xét giao diện người
dùng mà bạn sắp sử dụng. Liệt kê 8 cho thấy một trang web rất đơn giản.
Liệt kê 8. Trang web CoffeeSearch
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>CoffeeSearch</title>
<script type="text/javascript" src="search.js"></script>
</head>
<body>
<div>
<label for="searchQuery">Keyword:</label>
<input type="text" name="searchQuery" id="searchQuery"></input>
<input type="button" value="Search" onclick="doSearch()"/>
</div>
<div class="goog" id="gr"/>
<div class="twit" id="tr"/>
</body>
</html>
Trang web ở trên có một biểu mẫu cơ bản để nhập một từ khóa và chuyển nó đến một máy tìm
kiếm. Nó đã định nghĩa hai phần và sẵn sàng dùng để thêm các kết quả tìm kiếm vào chúng.
Trang web này đã không định nghĩa mã JavaScript nào trong đó. Thay vào đó, tất cả mã
JavaScript đều ở trong một tệp có tên là doSearch, mà tệp này sẽ là phiên bản biên dịch của
CoffeeScript. Lưu ý rằng khi nhấn vào nút tìm kiếm, một hàm có tên là doSearch được gọi ra.
Hàm này phải nằm trong tệp search.js; đó là thứ duy nhất trong tệp search.js mà bạn vẫn chưa
thấy. Liệt kê 9 cho thấy định nghĩa theo CoffeeScript.
Liệt kê 9. Hàm doSearch của trang web
@doSearch = ->
$ = (id) -> document.getElementById(id)
kw = $("searchQuery").value
appender = (id, data) ->
data.forEach (x) ->
$(id).innerHTML += "<p>#{x.toHtml()}</p>"
ms = new MockSearch
ms.search kw, (results) ->
appender("gr", results.google)
appender("tr", results.twitter)
Bạn có thể thấy rằng hàm này có một ký hiệu @ (một ký hiệu tắt của this) đặt trước nó. Khi
được định nghĩa ở mức cao nhất của một kịch bản lệnh, this trở thành đối tượng chung (global).
Trong trường hợp của một kịch bản lệnh trong một trang web, đối tượng chung là đối tượng cửa
sổ, sẽ cho phép nó được tham chiếu trong trang web mà bạn thấy trong Liệt kê 8.
Hàm doSearch làm rất nhiều việc chỉ trong một vài dòng mã. Mã này:
Định nghĩa một hàm cục bộ có tên là $, về cơ bản đó chính là đại diện cho hàm
document.getElementById. Sửdụng hàm này để nhận từ khóa đã nhập vào trong biểu
mẫu tìm kiếm trong Liệt kê 8.
Định nghĩa một hàm cục bộ khác tên là appender, sẽ nhận ID của một phần tử trong
DOM và một mảng. Sau đó nó sẽ duyệt qua mảng, tạo ra một chuỗi HTML và nối thêm
chính phần tử có ID đã cho.
Tạo một đối tượng MockSearch và gọi phương thức search của nó.
Chuyển từ khoá từ biểu mẫu và tạo ra một hàm gọi lại.
Hàm gọi lại sửdụng appender để nối thêm các kết quả tìm kiếm từ Google vào một div
và các kết quả tìm kiếm từ Twitter vào một div khác.
Bây giờ bạn có thể chỉ cần biên dịch tất cả mã và chạy nó. Hình 1 cho thấy trang web với dữ liệu
giả định.
Hình 1. Trang tìm kiếm với dữ liệu giả định
Ví dụ này có thể trông không ấn tượng lắm, nhưng nó trình diễn tất cả các hàm cần thiết ởphía
máy khách. Mặc dù bạn vẫn chưa viết mã phíamáy chủ, nhưng bạn có thể hoàn toàn tin tưởng
rằng ứng dụng này sẽ làm việc miễn là mã phíamáy chủ tạo ra dữ liệu có cấu trúc giống như dữ
liệu giả định.
. thiết ở phía
máy khách. Mặc dù bạn vẫn chưa viết mã phía máy chủ, nhưng bạn có thể hoàn toàn tin tưởng
rằng ứng dụng này sẽ làm việc miễn là mã phía máy. Sử dụng CoffeeScript ở phía máy khách
Ứng dụng tìm kiếm được viết bằng CoffeeScript
Các chữ viết tắt thường dùng