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