简体   繁体   English

如何在使用Javascript和ERB模板(Rails)时保持DRY

[英]How to stay DRY when using both Javascript and ERB templates (Rails)

I'm building a Rails app that uses Pusher to use web sockets to push updates to directly to the client. 我正在构建一个Rails应用程序,它使用Pusher来使用Web套接字将更新直接推送到客户端。 In javascript: 在javascript中:

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>");
});

This is nasty mixing of code and presentation. 这是令人讨厌的代码和演示混合。 So the natural solution would be to use a javascript template. 所以自然的解决方案是使用javascript模板。 Perhaps eco or mustache: 也许生态或胡子:

//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
});

This is good and all, except, I'm repeating myself . 这很好,除了我重复自己 The mustache template is 99% identical to the ERB templates I already have written to render HTML from the server. 胡子模板与我已编写的用于从服务器呈现HTML的ERB模板99%相同。 The intended output/purpose of the mustache and ERB templates are 100% the same: to turn a tweet object into tweet html. 胡子和ERB模板的预期输出/目的是100%相同:将tweet对象转换为tweet html。

What is the best way to eliminate this repetition? 消除这种重复的最佳方法是什么?

UPDATE: Even though I answered my own question, I really want to see other ideas/solutions from other people--hence the bounty! 更新:即使我回答了我自己的问题,我真的希望看到其他人的其他想法/解决方案 - 因此是赏金!

imo the easiest way to do this would involve using AJAX to update the page when a new tweet is created. imo最简单的方法是在创建新推文时使用AJAX更新页面。 This would require creating two files, the first being a standard html.erb file and the second being a js.erb file. 这需要创建两个文件,第一个是标准的html.erb文件,第二个是js.erb文件。 The html.erb will be the standard form which can iterate through and display all the tweets after they are pulled from the database. html.erb将是标准表单,它可以迭代并显示从数据库中提取后的所有推文。 The js.erb file will be your simple javascript to append a new tweet upon creation, ie: js.erb文件将是您在创建时附加新推文的简单javascript,即:

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

In your form for the new tweet you would need to add: 在您的新推文的表单中,您需要添加:

:remote => true

which will enable AJAX. 这将启用AJAX。 Then in the create action you need to add code like this: 然后在创建操作中,您需要添加如下代码:

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

In this instance, if you post a tweet with an AJAX enabled form, it would respond to the call by running whatever code is in create.js.erb (which would be the $('#timeline').append code from above). 在这种情况下,如果你发布一个启用了AJAX形式的推文,它会通过运行create.js.erb中的任何代码来响应调用(这将是$('#timeline')。来自上面的代码) 。 Otherwise it will redirect to wherever you want to send it (in this case 'Index' for tweets). 否则它将重定向到您想要发送它的任何地方(在这种情况下,推文的“索引”)。 This is imo the DRYest and clearest way to accomplish what you are trying to do. 这是实现你想要做的最干净,最清晰的方式。

Thus far, the best solution I found was Isotope . 到目前为止,我找到的最佳解决方案是同位素

It lets you write templates using Javascript which can be rendered by both the client and server. 它允许您使用Javascript编写模板,Javascript可以由客户端和服务器呈现。

I would render all tweets with Javascript. 我会使用Javascript渲染所有推文。 Instead of rendering the HTML on the server, set the initial data up as JS in the head of your page. 不是在服务器上呈现HTML,而是在页面的头部将初始数据设置为JS。 When the page loads, render the Tweets with JS. 页面加载时,使用JS呈现推文。

In your head: 在你的头脑中:

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

In a JS file: 在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();
});

I haven't tried this, but this just occurred to me as a possible solution: 我没试过这个,但这只是我想到的一个可能的解决方案:

In your view create a hidden div which contains an example template (I'm using HAML here for brevity): 在您的视图中创建一个隐藏的div,其中包含一个示例模板(为简洁起见,我在这里使用HAML):

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

Your tweet partial can render a tweet as you do now. 您的推文部分可以像现在一样呈现推文。

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

When creating a tweet prototype you set the fields you want to the js-template replacement syntax, you could definitely dry this up, but I'm including it here in full for example purposes. 在创建推文原型时,你可以将你想要的字段设置为js-template替换语法,你绝对可以把它干掉,但是我在这里完整地包含它作为示例目的。

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

On the client you'd do something like: 在客户端,您可以执行以下操作:

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

The last part will be dependent on how you're doing your templating, but it'd be something like that. 最后一部分将取决于你如何做你的模板,但它就是这样的。

As previously stated, I haven't tried this technique, and it's not going to let you do stuff like loops or conditional formatting directly in the template, but you can get around that with some creativity I'm sure. 如前所述,我没有尝试过这种技术,并且它不会让你直接在模板中做循环或条件格式化之类的东西,但你可以用一些创造力来解决这个问题,我敢肯定。

This isn't that far off what you're looking to do using Isotope, and in a lot of ways is inferior, but it's definitely a simpler solution. 这与你使用同位素所要做的事情相去甚远,并且在许多方面都是劣等的,但它绝对是一个更简单的解决方案。 Personally I like haml, and try to write as much of my mark up in that as possible, so this would be a better solution for me personally. 我个人喜欢haml,尽可能多地写下我的标记,所以这对我个人来说是一个更好的解决方案。

I hope this helps! 我希望这有帮助!

为了能够使用胡子模板在javascript和rails之间共享模板,有smt_rails: https//github.com/railsware/smt_rails (“rails 3的共享胡子模板”)以及Poirot: https:// github .com / olivernn / poirot

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM