【问题标题】:Rails 4 update instance variables without page reloadRails 4 更新实例变量而无需重新加载页面
【发布时间】:2016-07-16 16:16:01
【问题描述】:

有没有一种方法可以在不重新加载页面的情况下重新加载视图中的实例变量?

我有一个 AJAX 帖子,它创建了一条新记录。我希望将该记录添加到现有的实例变量中。

所以我有一个诸如action 之类的操作,它处理我在action.html.erb 中的视图:

def action
  @variable = Variable.where().to_a
end

然后我有一个执行诸如action2 之类的操作的 AJAX 请求:

def action2
  @new_record = Variable.create(params)
  respond_to do |format|
    format.html
    format.json {render :json => @new_record.to_json}
  end
end

有没有办法刷新@variable 实例变量以包含新创建的记录?

我可以向action 发出另一个 AJAX 请求吗?如果是这样,我将如何检测action 中的 AJAX 请求?

我已经用request.xhr? 尝试过这个^^,但是在我执行一个JS .click() 之后它会抛出一个奇怪的StandardError

我不确定我是否朝着正确的方向前进,但如果是,这里是我整理的所有代码...

我正在构建一个即时消息服务,我承认我完全是在拼凑一个原型。我知道有更简洁的方法可以处理高流量负载,但我现在正在制作原型。

视图代码看起来像这样...有:HTML 聊天框,我每 2 秒发出一次 AJAX 请求以查找新消息,在同一个函数中,我向上面称为 action 的内容发出 AJAX 请求(实际上命名不同),在“刷新”功能之后,我有一个.click() 功能来更新用户视图(刚刚提交的那个)。

<div id="wrapper">
    <div id="menu">
        <p class="welcome">Welcome, <b></b></p>
        <p class="logout"><a id="exit" href="#">Exit Chat</a></p>
        <div style="clear:both"></div>
    </div>

    <div id="chatbox">
      <% @messages.each do |message| %>
        <% if session[:email] == message.email %>
          <!-- post to the right side -->
          <div id="right-side">
            <p>Email: <%= message.email %></p>
            <p>Message: <%= message.message %></p>
          </div>
        <% else %>
          <!-- post to the left side -->
          <div id="left-side">
            <p>Email: <%= message.email %></p>
            <p>Message: <%= message.message %></p>
          </div>
        <% end %>
      <% end %>
    </div>

    <form id="frm1" action="">
      Message: <input id="message_input" type="text" name="fname"><br>
      <!-- <input type="submit" value="Submit"> -->
      <button name="submitmsg" type="submit"  id="submitmsg">Try it</button>
    </form>
</div>

<p id="demo"></p>
<script>

  function retrieveMessages(){
    var message;
    $.ajax({
      type:"GET",
      url:"<%= get_messages_path %>",
      dataType:"json",
      data: {chat_id: <%= @message_info[:chat_id] %>,
              last_message: <%= @messages.last.created_at.to_i %>},
      success:function(data){
        if (data != null){
          console.log(data);
          console.log(data.message);
          message = data.message;
          document.getElementById("chatbox").innerHTML +=  message + '<br>';
        }
      }
    });

    $.ajax({
      type:"GET",
      url:"<%= new_message_path %>",
      dataType:"json",
      success:function(data){
        console.log("success!");
        }
      }
    });
    setTimeout(retrieveMessages, 2000);
  }

  $(document).ready(function(){
    //get messages
    setTimeout(retrieveMessages, 2000);

    //send messages
    $("#submitmsg").on('click', function(e){
      e.preventDefault();
      var x = document.getElementById("frm1"); //This is the form, and not the value of the textbox
      var text = "";
      var i;
      for (i = 0; i < x.length ;i++) {
        text += x.elements[i].value;
      }
      // document.getElementById("chatbox").innerHTML += text;
      text_with_br = text + "<br>"
      document.getElementById("chatbox").innerHTML += text_with_br;
      document.getElementById("frm1").reset();
      // console.log(document.getElementById("right"));
      //Actual message is in 'text'
      $.ajax({
        url: "/messages",
        method: "post",
        data: { message: text,
                chat_id: <%= @message_info[:chat_id] %>,
                message_counter: <%= @message_info[:message_counter] + 1 %> },
        dataType: "JSON",
        success: function(response){
          //do something,maybe notify user successfully posted their message
        },
        error: function(error){
          console.log(error);
        }
      });

    });
  });
</script>

那么对于我的action 动作(实际上是new 动作,而我所谓的@variable 实际上是@messages

def new
  # debugger
  # Message.create(:chat_id => 5)
  if session[:email].nil?
    #user not logged in
    redirect_to new_session_path(:message => "need to login")
  else
      @user = User.where(:email => session[:email]).first
      @message_info = Hash(email: @user.email, message_counter: 0, chat_id: Message.last.chat_id + 1)
      @messages = Message.where(:chat_id => @message_info[:chat_id]).to_a

      respond_to do |format|
        format.html
        format.json
      end

  end
end

那么执行AJAX请求获取最新消息(以便其他浏览器的查看者可以查看)的动作如下...

def get
  @new_message = Message.where(["created_at > ?", Time.at(params[:last_message].to_i)]).first

  respond_to do |format|
    format.html
    # format.json {render json: @new_message}
    format.json {render :json => @new_message.to_json}
  end
end

更新

@eggroll 建议的代码如下,但我遇到了引导问题

【问题讨论】:

  • 我建议您将 div 附加到 ajax 成功回调的 $("#chatbox") 上。 div 包含响应 @variable
  • @power 它不会仍然只是向 HTML DOM 插入一条重复的消息吗?另外我遇到的主要问题是,当我在 AJAX GET 函数中传递参数 last_message: &lt;%= @messages.last.created_at.to_i %&gt; 时,它只会发送自页面加载以来的最后一条消息——我需要一种将最新消息附加到 @message 的方法变量
  • 在这种情况下,您可以通过new_message = Message.order("created_at").last 找到最后一条消息,然后将其created_at 与Time.at(params[:last_message].to_i 进行比较) 如果 new_message.created_at 更大,则表示您收到了 new_message,但如果相等,则表示您没有收到任何新消息,因此只需发送 nil 进行响应。有意义吗?

标签: javascript jquery ruby-on-rails ajax ruby-on-rails-4


【解决方案1】:

从您的代码开始,以下是我对解决方案的尝试。我重命名了元素,希望它可以使这段代码更加自文档化,从而让您更容易辨别我所做的事情。它并不完美,但它在 Chrome 中对我有用,所以希望它有所帮助。我假设起始chat_id是1,如果不是,可以在控制器的get_last_chat_id方法中调整。

(注意:有几行代码对您来说是多余的,但对于我能够在我现有的一个应用程序中运行此代码而言是必要的。您还应该知道我m 使用 Postgres、jQuery、Devise、HAML 和 Bootstrap 4 alpha。)

app/controllers/messages_controller.rb

class MessagesController < ApplicationController

  before_action :authenticate_user!
  before_action :set_current_user_email, only: [:index, :display_all_messages]
  before_action :set_current_messages,   only: [:display_all_messages]

  def index
  end


  def get_last_chat_id
    last_chat_id = Message.pluck(:chat_id).max
    if last_chat_id
      puts '*** LAST CHAT ID: ' + last_chat_id.to_s
    else
      last_chat_id = 0
      puts '*** LAST CHAT ID: ' + last_chat_id.to_s
    end

    respond_to do |format|
      format.json { render json: last_chat_id }
    end
  end


  def save_new_message
    new_message = Message.new do |msg|
      msg.email        = params[:email]
      msg.message_text = params[:message_text]
      msg.chat_id      = params[:chat_id]
    end

    puts '*** NEW MESSAGE EMAIL:   ' + new_message.email
    puts '*** NEW MESSAGE TEXT:    ' + new_message.message_text
    puts '*** NEW MESSAGE CHAT ID: ' + new_message.chat_id.to_s

    # Source: https://makandracards.com/housetrip-deck/16879-jquery-ajax-success-done-will-not-run-callbacks-if-request-is-json-but-the-response-is-empty-typical-200
    respond_to do |format|
      if new_message.save
        puts '*** NEW MESSAGE WAS SAVED!!!'
        format.json { render json: { ok: true }, status: :ok }
      else
        puts '*** NEW MESSAGE WAS NOT SAVED!!!'
        format.json { render json: { ok: false }, status: :unprocessable_entity }
      end
    end
  end


  def display_all_messages
    respond_to do |format|
      if @current_messages
        format.js  {  }
      else
        puts '*** THERE ARE NO MESSAGES TO DISPLAY!!!'
      end
    end
  end


  private

    def set_current_messages
      @current_messages = Message.by_created_desc
    end


    def set_current_user_email
      @current_user_email = current_user.email
    end


    def message_params
      params.require(:message).
        permit(:email, :message_text)
    end


end

app/models/message.rb

class Message < ActiveRecord::Base

  scope :by_created_desc, -> { order(created_at: :desc) }

end

db/migrate/20160717000100_create_messages.rb

class CreateMessages < ActiveRecord::Migration
  def change

    create_table :messages do |t|

      t.string  :email,        null: false
      t.text    :message_text
      t.integer :chat_id,      null: false

      t.timestamps null: false

    end

    add_index :messages, :chat_id, unique: true

  end
end

app/assets/javascripts/messages.js(2016 年 7 月 17 日更新)

var messagesRefresher;

$(document).ready(function(){

  // AJAX error handling, outputting error messaging to the console
  $(document).ajaxError(function (event, jqxhr, settings, thrownError) {
    console.log('EVENT: '        + JSON.stringify(event, null, '\t'));
    console.log('JQXHR: '        + JSON.stringify(jqxhr));
    console.log('SETTINGS: '     + JSON.stringify(settings, null, '\t'));
    console.log('THROWN ERROR: ' + thrownError);
  });

  messagesRefresher = setInterval(refreshMessages, 2000);

  $('#new-message-create-btn').on('click', function(e){
    e.preventDefault();
    $('#new-message-create-btn').addClass('no-display');
    $('#new-message-form-wrapper').removeClass('no-display');
  });

  $('#new-message-cancel-btn').on('click', function(e){
    e.preventDefault();
    $('#new-message-form-wrapper').addClass('no-display');
    $('#new-message-create-btn').removeClass('no-display');
  });

  $('#new-message-submit-btn').on('click', function(e){
    e.preventDefault();
    var newMessageEmail  = $('#new-message-email').val();
    var newMessageText   = $('#new-message-text').val();

    console.log('*** NEW MESSAGE EMAIL: ' + newMessageEmail);
    console.log('*** NEW MESSAGE TEXT:');
    console.log(newMessageText);

    getLastChatId('/messages/get_last_chat_id').done(function(lastChatId) {
      var newMessageChatId = lastChatId + 1;
      console.log('*** NEW MESSAGE CHAT ID: ' + newMessageChatId)

      saveNewMessage('/messages/save_new_message?email=' + newMessageEmail + '&message_text=' + newMessageText + '&chat_id=' + newMessageChatId).done(function(data) {

        console.log('*** MESSAGE # ' + newMessageChatId + ' SAVED!!!')

        $('#new-message-form-wrapper').addClass('no-display');
        document.getElementById('new-message-form').reset();
        $('#new-message-create-btn').removeClass('no-display');

        refreshMessages;
      });

    });

  });

});


function refreshMessages() {
  displayAllMessages('/messages/display_all_messages').done(function(data) {
    console.log('*** MESSAGES REFRESHED!!!');
  });
};


function getLastChatId(url) {
  return $.ajax({
    url:       url,
    type:     'get',
    dataType: 'json'
  })
  .fail(function() {
    alert('AJAX Get Last Chat Id Error');
  });
};


function saveNewMessage(url) {
  return $.ajax({
    url:       url,
    type:     'get',
    dataType: 'json'
  })
  .fail(function() {
    alert('AJAX Save New Message Error');
  });
};


function displayAllMessages(url) {
  return $.ajax({
    url:       url,
    type:     'get',
    dataType: 'script'
  })
  .fail(function() {
    alert('AJAX Display All Messages Error');
  });
};


$(window).unload(
  function(event) {
    clearInterval(messagesRefresher);
  }
);

app/views/layouts/application.html.haml (sn-p)(添加时间:2016-07-18)

.
.
.

%head

  %meta{ charset: 'UTF-8' }
  %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, shrink-to-fit=no' }

  -# Derived from: http://v4-alpha.getbootstrap.com/getting-started/browsers-devices/
  %meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }

  = csrf_meta_tags

  -# For page-specific meta tags
  = content_for?(:meta_tag) ? yield(:meta_tag) : ""

  %title Chatbox

  = stylesheet_link_tag 'application'
  = yield :page_stylesheet_link_tags

  = javascript_include_tag 'application'
  = yield :page_specific_javascript

.
.
.

app/views/messages/index.html.haml

- content_for :page_specific_javascript do
  = javascript_include_tag 'messages.js'

#chatbox
  %h1 Chatbox

  = link_to 'New Message', 'javascript:;', id: 'new-message-create-btn', class: 'btn btn-sm btn-primary'

  #new-message-form-wrapper.no-display
    = form_tag messages_path, id: 'new-message-form' do
      = hidden_field_tag 'new-message-email', @current_user_email
      #new-message-form-label-wrapper
        = label_tag 'new-message-text', 'Enter Your Message:'
      #new-message-form-text-wrapper
        = text_area_tag 'new-message-text', nil, rows: 6, cols: 70
      #new-message-form-buttons-wrapper
        = submit_tag 'Post Message', id: 'new-message-submit-btn', class: 'btn btn-sm btn-success'
        = link_to 'Cancel', 'javascript:;', id: 'new-message-cancel-btn', class: 'btn btn-sm btn-secondary'


  #display-messages-wrapper

    #messages-column-left.pull-md-left
      %h3 Messages From Others
      #messages-other-users

    #messages-column-right.pull-md-right
      %h3 My Messages
      #messages-current-user

app/views/messages/display_all_messages.js.haml

$('#messages-other-users').html('');
$('#messages-current-user').html('');
- @current_messages.each do |msg|
  - if msg.email == @current_user_email
    $('#messages-current-user').append("#{ escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) }");
  - else
    $('#messages-other-users').append("#{ escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) }");

app/views/messages/_message.html.haml

.message-wrapper

  .message-attribution-wrapper
    %span.message-attribution-label Posted by:
    %span.message-attribution-text=email + ' on ' + message_created_at.strftime('%Y-%m-%d') + ' at ' + message_created_at.strftime('%I:%M:%S %p')

  .message-text-wrapper
    .message-label Message:
    .message-text= message_text

app/assets/stylesheets/messages.scss(2016 年 7 月 17 日更新)

.no-display {
  display: none !important;
}

#chatbox {
  width: 90%;
  margin: .5em auto;
}

#new-message-form-wrapper {
  width: 48%;
  padding: 1em;
  border: 1px solid #ccc;
}

#new-message-form-label-wrapper > label {
  font-weight: 700;
}

#new-message-form-buttons-wrapper {
  margin-top: .5em;
}

#new-message-submit-btn {
  margin-right: .3em;
}

#new-message-submit-btn,
#new-message-cancel-btn {
  width: 8em;
}

#new-messages-form-wrapper,
#display-messages-wrapper {
  margin-top: 1.5em;
}

#messages-column-left,
#messages-column-right {
  width: 48%;
}

.message-wrapper {
  width: 96%;
  margin: 1em auto;
  padding: .5em;
  border: 1px solid #ccc;
}

.message-attribution-label,
.message-label {
  font-weight: 700;
}

config/routes.rb

Rails.application.routes.draw do

  resources :messages, only: [:index]
  get  'messages/get_last_chat_id',     to: 'messages#get_last_chat_id'
  get  'messages/save_new_message',     to: 'messages#save_new_message'
  get  'messages/display_all_messages', to: 'messages#display_all_messages'

end

assets/javascripts/application.js(添加时间:2016-07-18)

//= require jquery 
//= require jquery_ujs 
//= require jquery-ui 
//= require bootstrap-sprockets

config/initialization/assets/rb(添加时间:2016-07-18)

Rails.application.config.assets.precompile += %w( messages.js )

assets/stylesheets/application.scss(添加时间:2016-07-18)

@import 'bootstrap_4a/bs_4a_variable_overrides'; 
@import 'bootstrap'; 
@import 'bootstrap_4a/bs_4a_customization'; 

app/assets/stylesheets/bootstrap_4a/bs_4a_variable_overrides(添加时间:2016-07-18)

// http://v4-alpha.getbootstrap.com/getting-started/flexbox/
// Enabling flexbox means reduced browser and device support:
//   Internet Explorer 9 and below do not support flexbox.
//   Internet Explorer 10 has a few known quirks, requires using a prefix,
//   and only supports the syntax from the old 2012 version of the spec.

$enable-flex: true;

app/assets/stylesheets/bootstrap_4a/bs_4a_customization(添加时间:2016-07-18)

// http://v4-alpha.getbootstrap.com/getting-started/browsers-devices/
// As of Safari v8.0, fixed-width .containers can cause Safari
// to use an unusually small font size when printing.
// One potential workaround for this is adding the following CSS:

  @media print {
    .container {
      width: auto;
    }
  }

  body {
    position: relative;
  }

Gemfile(添加时间:2016-07-18)

gem 'sass-rails', '~> 5.0' 
gem 'jquery-rails' 
gem 'jquery-ui-rails', '~> 5.0.5' 
gem 'autoprefixer-rails', '~> 6.3.6' 
gem 'bootstrap', '~> 4.0.0.alpha3'

更新:.js.erb 版本的 apps/views/messages/display_all_messages.js.haml

$('#messages-other-users').html('');
$('#messages-current-user').html('');
<% @current_messages.each do |msg| %>
  <% if msg.email == @current_user_email %>
    $('#messages-current-user').append("<%= escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) %>");
  <% else %>
     $('#messages-other-users').append("<%= escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) %>");
  <% end %>
<% end %>

【讨论】:

  • 更新了messages.scss 以包括.no-display
  • 谢谢!我不完全确定它是否符合您的要求,但听起来确实如此。干杯!
  • 我实际上从未使用过 HAML。你知道一个好的入门教程吗?
  • 不幸的是,没有。我只是随着时间的推移把它捡起来。学习基础知识很容易,但您最终会访问 SO 以克服一些细微差别或不寻常的情况。我本来不喜欢它,因为它没有erb的对称性,但是一旦我承诺使用它,我更喜欢它有多干净。你可以随时使用link 转换我在 HAML 中所做的事情,尽管它可能需要一些调整。可能比较棘手的是display_all_messages.js.haml 文件。它不会正确转换,所以我将添加我首先做的 ERB 版本以使其正常工作。
  • 修正了 app/assets/javascripts/messages.js (#new-messages-form-wrapper => #new-message-form-wrapper) 中的错字。
【解决方案2】:

您可以使用成功回调,并将 div 附加到 $("#chatbox"),下面是示例代码。

jQuery.ajax({
    type: 'GET',
    url: loadUrl,
    data: dataString,
    dataType: 'html',
    success: function(response) {
        $("#chatbox").append("
        <div id="left-side">
        <p>Email:"+  response[:email]+"</p>
        <p>Message:"+ response[:message]+"</p>
      </div>"
    }
});    

【讨论】:

  • 你能回复我上面的评论吗?
猜你喜欢
  • 1970-01-01
  • 2019-07-10
  • 1970-01-01
  • 2016-01-12
  • 2012-06-15
  • 1970-01-01
  • 2016-02-01
  • 1970-01-01
  • 2015-11-02
相关资源
最近更新 更多