8

我在構建一個Rails應用程序,該應用程序使用Pusher使用Web套接字將更新直接推送到客戶端。在javascript中:如何在使用Javascript和ERB模板時保持DRY(Rails)

channel.bind('tweet-create', function(tweet){ //when a tweet is created, execute the following code: 
    $('#timeline').append("<div class='tweet'><div class='tweeter'>"+tweet.username+"</div>"+tweet.status+"</div>"); 
}); 

這是令人討厭的代碼和演示混合。所以自然的解決方案是使用JavaScript模板。或許生態或鬍子:

//store this somewhere convenient, perhaps in the view folder: 
tweet_view = "<div class='tweet'><div class='tweeter'>{{tweet.username}}</div>{{tweet.status}}</div>" 

channel.bind('tweet-create', function(tweet){ //when a tweet is created, execute the following code: 
    $('#timeline').append(Mustache.to_html(tweet_view, tweet)); //much cleaner 
}); 

這是很好的和所有,但,我重複自己。小鬍子模板與我已經編寫用於從服務器呈現HTML的ERB模板99%相同。鬍子和ERB模板的預期輸出/目的是100%相同的:將tweet對象變成tweet html。

消除這種重複的最佳方法是什麼?

更新:儘管我回答了我自己的問題,但我真的很想看到其他人的想法/解決方案 - 因此是賞金!

+1

供將來參考:Mustache(您已經提到過)也允許進行服務器端渲染(請參閱http://mustache.github.com/中的實現)。流程現在變爲:1使用mustache-template進行初始服務器端渲染+使用完全相同的mustache-template連續N次客戶端渲染 – 2011-07-26 09:31:53

+0

非常感謝。我會看看那些。 – user94154 2011-07-26 17:35:46

+0

fyi:剛纔我問了一個類似的問題(尋找可以替代限制小鬍子的選項),儘管流程會保持不變。也許這也有幫助。請參閱:http://stackoverflow.com/questions/6831718/client-side-templating-language-with-java-compiler-as-well-dry-templating – 2011-07-26 18:29:21

回答

5

即時通訊最簡單的方法是在創建新推文時使用AJAX更新頁面。這需要創建兩個文件,第一個是標準的html.erb文件,第二個是js.erb文件。 html.erb將成爲標準格式,可以迭代並顯示從數據庫中提取的所有推文。該js.erb文件將是你簡單的JavaScript創建時附加新的鳴叫,即:

$('#timeline').append("<div class='tweet'><div class='tweeter'><%= tweet.username %></div><%= tweet.status %></div>") 

在您的形式爲新的鳴叫,你將需要添加:

:remote => true 

這將使AJAX。然後在創建操作,你需要添加如下代碼:

def create 
...Processing logic... 
    respond_to do |format| 
    format.html { redirect_to tweets_path } 
    format.js 
    end 
end 

在這種情況下,如果您發佈與支持AJAX形式的鳴叫,這將通過運行任何代碼是在create.js響應號召.erb(這將是$('#timeline')。append代碼從上面)。否則,它將重定向到任何你想發送它(在這種情況下,'索引'的鳴叫)。這就是DRYest和最明確的方式來完成你正在嘗試做的事情。

+0

感謝您的答覆,但如何解決重複問題? js.erb文件中的div將以html.erb部分完全相同的方式寫入。 – user94154 2011-03-01 18:48:47

+2

以及你可以做的是有兩個(共享/ _tweet_item.html.erb)之間的第三個共享部分。所以例如在你的HTML文件中,你可以做一個渲染'<%= render:partial =>'shared/tweet_item',:collection => @tweets_array%>',它將遍歷數組中的所有推文。類似地在你的create.js.erb文件中,你可以做'$(「timeline」)。append(「<%= escape_javascript(render('shared/tweet_item',@tweet))%>」)'一個新的部分,因爲你傳遞給它一個@tweet實例變量 – 2011-03-01 19:11:02

+0

沒有提到它,但我在這裏意味着你所要做的就是在共享部分中定義div的格式和所有內容。那麼它只需要遍歷html.erb文件中的集合併爲集合中的每個項目呈現部分內容,或者將實例變量傳遞給partial,並簡單地通過AJAX調用附加所有內容。沒有重複和良好的內容分離。 – 2011-03-01 19:14:53

3

到目前爲止,我找到的最佳解決方案是Isotope

它可以讓你用Javascript編寫模板,可以由客戶端和服務器渲染。

2

我沒有試過,但是這只是發生在我作爲一個可能的解決方案:

在您的視圖中創建一個隱藏的div包含一個示例模板(我在這裏使用HAML爲了簡潔):

#tweet-prototype{:style => "display:none"} 
    = render :partial => Tweet.prototype 

您的推文部分可以像現在一樣呈現推文。

.tweet 
    .tweeter 
     = tweet.username 
    .status 
     = tweet.status 

當創建您設置要與JS-更換模板語法領域的鳴叫原型,你肯定可以幹這件事,但我在這裏,包括它完全作爲一個例子。

# tweet.rb 
def self.prototype 
    Tweet.new{:username => "${tweet.username}", :status => "${tweet.status}"} 
end 

在你會做一些事情,就像客戶端:

var template = new Template($('#tweet-prototype').html()); 
template.evaluate(.. your tweet json..); 

最後一部分將取決於你如何做你的模板,但它會是類似的東西。

如前所述,我還沒有嘗試過這種技術,它不會讓你直接在模板中做循環或條件格式化的東西,但你可以用一些創造性來解決這個問題。

這與您希望使用Isotope做的並不遙遠,在很多方面都很差,但這絕對是一個更簡單的解決方案。就我個人而言,我喜歡haml,並儘可能地寫出儘可能多的標記,所以這對我個人而言是更好的解決方案。

我希望這有助於!

3

我會用Javascript呈現所有推文。不要在服務器上呈現HTML,而應將初始數據設置爲頁面頂部的JS。加載頁面時,使用JS呈現Tweets。

在你的腦袋:

%head 
    :javascript 
    window.existingTweets = [{'status' : 'my tweet', 'username' : 'glasner'}]; 

在JS文件:

$.fn.timeline = function() { 
    this.extend({ 
    template: "<div class='tweet'><div class='tweeter'>{{tweet.username}}</div>{{tweet.status}}</div>", 
    push: function(hash){ 
     // have to refer to timeline with global variable 
     var tweet = Mustache.to_html(timeline.template, hash)  
     timeline.append(tweet); 
    } 
    }); 

    window.timeline = this; 

    channel.bind('tweet-create', this.push); 

    // I use Underscore, but you can loop through however you want 
    _.each(existingTweets,function(hash) { 
    timeline.push(hash); 
    }); 

    return this 
}; 


$(document).ready(function() { 
    $('#timeline').timeline(); 
}); 
+0

這是另一個很好的答案,但問題是您不再對搜索引擎友好。 – jonnii 2011-03-02 15:28:24

+0

好點。此技術僅適用於您未創建長期內容的應用程序。 – Jordan 2011-03-02 15:31:01