<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 3.3 視圖中的 AJAX 交互 ## 概要: 本課時通過對商品的添加、編輯和刪除,講解視圖中如何使用 UJS,jQuery 和 JSON,實現無刷新情況下的頁面更新。 ## 知識點: 1. jQuery 1. UJS 1. AJAX 1. JSON ## 正文 上一節,我們講解了 Rails 中的視圖(View),我們再回顧一下這個視圖是如何產生的:我們向服務器發起一個請求,服務器返給我們結果,查看源代碼,它是一篇 `HTML` 的代碼。 我們每次請求一個地址,都會給我們完整的 HTML 結果,對于內容較少的網頁,傳輸起來還是很快的,但是對于內容多的網頁,大篇的結果自然會拖慢頁面顯示。 當我們瀏覽頁面的時候,并不期望總是刷新整個頁面,因為它沒必要。現在我們有 ajax 技術,可以只加載和顯示部分頁面代碼。舉個簡單的例子:當我們提交了一條評論,頁面上自動顯示出我們提交的評論內容。我們點擊購買按鈕,頁面上就提示我們購物車里增加了一個商品。而這些,都不必要刷新整個頁面。 ajax 是 Asynchronous Javascript And XML 的縮寫,含義是異步的 js 和 XML 交互技術。XML,可擴展標記語言,我們使用的 HTML 是基于其發展起來的。 下面我們看下 Rails 是如何把 ajax 技術應用在視圖(View)中的。 ### 3.3.1 ujs 我們在 Gemfile 中已經使用了 `gem 'jquery-rails'` 這個 Gem,它可以讓我們在 `application.js` 中增加這兩行: ~~~ //= require jquery //= require jquery_ujs ~~~ [jQuery](http://jquery.com/) 是一個輕量級的 js 庫,可以方便的處理HTML,事件(Event),動態效果,為頁面提供 ajax 交互。jQuery 有很完善的文檔及演示代碼,以及大量的插件。 Rails 使用一種叫 [ujs](https://github.com/rails/jquery-ujs)(Unobtrusive JavaScript)的技術,將 js 應用到 DOM 上。我們來看一個例子: ![](https://box.kancloud.cn/2015-08-18_55d2e47fd80e1.png) 我們已經給刪除連接增加了兩個屬性: ~~~ <%= link_to "刪除", product, :method => :delete, :data => { :confirm => "點擊確定繼續" } %> ~~~ 來看看我們的 HTML: ~~~ <a data-confirm="點擊確定繼續" rel="nofollow" data-method="delete" href="/products/1">刪除</a> ~~~ 輔助方法 `link_to` 使用了 `:data => { :confirm => "點擊確定繼續" }` 這個屬性,為我們添加了 `data-confirm="點擊確定繼續"` 這樣的 HTML 代碼,之后 ujs 將它處理成一個彈出框。 在刪除按鈕上,還有 `:method => :delete` 屬性,這為我們的連接上增加了 `data-method="delete"` 屬性,這樣,ujs 會把這個點擊動作,會發送一個 `delete` 請求刪除資源,這是符合 REST 要求的。 我們可以給 `a` 標簽增加 `data-disable-with` 屬性,當點擊它的時候,使它禁用,并提示文字信息。這樣可以防止用戶多次提交表單,或者重復的鏈接操作。 我們為商品表單中的按鈕,增加這個屬性: ~~~ <%= f.submit nil, :data => { :"disable-with" => "請稍等..." } %> ~~~ 當我們提交表單時,會有: ![](https://box.kancloud.cn/2015-08-18_55d2e47fed6f3.png) 如果你還沒看清楚效果,頁面就已經跳轉了,我們可以給 create 方法增加一個 `sleep 10`: ~~~ def create sleep 10 @product = Product.new(product_params) ... ~~~ 更多 ujs 支持的屬性,我們在 [這里](https://github.com/rails/jquery-ujs/blob/master/src/rails.js) 看到。 ### 3.3.2 無刷新頁面的操作 ujs 給我們帶來的一些便利還不止這些,我們來點復雜的:在不刷新頁面的情形下,添加一個商品,并顯示在列表中。 我們現在的列表頁是這樣的: ![](https://box.kancloud.cn/2015-08-18_55d2e480047e2.png) 現在點擊添加,我們會進入到 `http://localhost:3000/products/new`,我們并不改變它,畢竟在某些 js 失效的情形下,點擊這個按鈕還是要跳轉到 new 頁面的。 我們希望給頁面增加一個表單,來輸入新商品的信息,在這之前,我們想更酷一點,我們使用 `modal` 來顯示這個表單: ~~~ <%= link_to t('.new', :default => t("helpers.links.new")), new_product_path, :class => 'btn btn-primary', data: {toggle: "modal", target: "#productForm"} %> ~~~ ujs 允許我們在 link 上增加額外的屬性,當我們再次點擊 `添加` 按鈕時: ![](https://box.kancloud.cn/2015-08-18_55d2e4801e0fc.png) 當然我做了其他一些修改,你可以在 [這里](https://github.com/liwei78/rails-practice-code/tree/master/chapter_3/shop3.3) 找到完整的代碼。 為了產生一個 ajax 的請求,我們在表單上增加一個參數 `remote: true`: ~~~ <%= form_for @product, remote: true, :html => { :class => 'form-horizontal' } do |f| %> ~~~ 這時,ujs 將會調用 `jQuery.ajax()` 提交表單,此時的請求是一個 `text/javascript` 請求,Rails 會返回給我們相應的結果,在我們的 action 里,增加這樣的聲明: ~~~ respond_to do |format| if @product.save format.html {...} format.js else format.html {...} format.js end end ~~~ 在保存(save)成功時,我們返回給視圖(view)一個 js 片段,它可以在瀏覽器端執行。 我們創建一個新文件 `app/views/products/create.js.erb`,在這里,我們將新添加商品,顯示在上面的列表中。 ~~~ $('#productsTable').prepend('<%= j render(@product) %>'); $('#productFormModal').modal('hide'); ~~~ 我們使用 `.js.erb` 的文件,方便我們在 js 文件里插入 erb 的語法。 我們將一行商品信息使用 `prepend` 方法,插入到 `productsTable` 的最上面,`j` 方法將我們的字符串轉換成 js 片段。 好了,你可以試一試效果了。 你可能也像我一樣做了一些測試,導致插入了很多測試數據,為了繼續不刷新頁面就完成刪除操作,我們給 `刪除` 按鈕上也增加一個 ajax 調用。 我們先給每一行記錄,增加一個唯一的 ID 標識,通常使用“名字 + id”的形式,我們還需要給刪除連接增加 `remote: true` 屬性,我們編輯 `app/views/products/_product.html.erb`: ~~~ <tr id="product_<%= product.id %>"> ... <%= link_to "刪除", product, :method => :delete, remote: true, :data => { :confirm => "點擊確定繼續" }, :class => 'btn btn-danger btn-xs' %> ~~~ 我們再增加一個文件以返回 js 片段給瀏覽器執行 `app/views/products/destroy.js.erb`: ~~~ $('#product_<%= @product.id %>').fadeOut(); ~~~ 你可以再試試看。 現在,我們看一下添加商品時的返回結果: ~~~ $('#productsTable').prepend('<tr id=\"product_14\">\n <td><a href=\"/products/14\">kkk<\/a><\/td>\n <td>jjj<\/td>\n <td class=\"text-right\">CN¥ 999.00<\/td>\n <td>2015年2月26日 星期四 23:57:55<\/td>\n <td>\n <a class=\"btn btn-primary btn-xs\" href=\"/products/14/edit\">編輯<\/a>\n <a data-confirm=\"點擊確定繼續\" class=\"btn btn-danger btn-xs\" data-remote=\"true\" rel=\"nofollow\" data-method=\"delete\" href=\"/products/14\">刪除<\/a>\n <\/td>\n<\/tr>\n'); $('#productFormModal').modal('hide'); ~~~ 這里面大部分代碼是不必要的 HTML代碼,如何讓我們的返回結果更簡潔呢?我們現在發送個是 `text/javascript` 請求,返回給我們的是 js 片段。下一節我們發送 'json' 請求,我們在瀏覽器端使用 js 處理返回的 json 數據。 ### 3.3.3 json 數據的頁面處理 為了和添加商品區分開,我們在修改商品時,使用 `json` 來處理數據,而且也在一個 `modal` 中完成。 ~~~ <%= link_to t('.edit', :default => t("helpers.links.edit")), edit_product_path(product), remote: true, data: { type: 'json' }, :class => 'btn btn-primary btn-xs editProductLink' %> ~~~ 我們給編輯鏈接,增加了 `remote: true, data: { type: 'json' }`,這時我們沒有打開`modal`,我們把 js 代碼寫在 coffeescript 中。 我們新建一個文件,`app/assets/javascripts/products.coffee`。這個文件我們只在商品頁面使用,所以不必把它放到 `simplex.js` 中,現在我們只在商品的 `index.html.erb` 中使用它,所以: ~~~ <%= content_for :page_javascript do %> <%= javascript_include_tag "products" %> ... ~~~ 當我們點擊編輯按鈕時,我們期望幾件事: 1. 打開 `modal` 層,顯示編輯表單 1. 讀取這個商品的信息(json 格式),把需要編輯的內容填入表單 好,我們寫上這部分代碼: ~~~ jQuery -> $(".editProductLink") .on "ajax:success", (e, data, status, xhr) -> $('#alert-content').hide() [1] $('#editProductFormModal').modal('show') [2] $('#editProductName').val(data['name']) [3] $('#editProductDescription').val(data['description']) [3] $('#editProductPrice').val(data['price']) [3] $("#editProductForm").attr('action', '/products/'+data['id']) [4] ~~~ - [1] 我們隱藏錯誤信息提示框 - [2] 顯示層 - [3] 填入編輯的信息 - [4] 更改表單提交的地址 再來看看我們的編輯表單: ~~~ ... <%= form_tag "", method: :put, remote: true, data: { type: "json" }, id: "editProductForm", class: "form-horizontal" do %> ... <%= text_field_tag "product[name]", "", :class => 'form-control', id: "editProductName", required: true %> ... <%= text_field_tag "product[description]", "", :class => 'form-control', id: "editProductDescription" %> ... <%= text_field_tag "product[price]", "", :class => 'form-control', id: "editProductPrice" %> ... ~~~ 我們讓表單提交的地址,可以根據選擇的商品而改變,同時我們設定它的 type 為 json 格式。 我們為每一個輸入框,設定了 ID,這樣,我們用讀取的 json 信息,分別填入對應的編輯框內。 然后,我們改動一下 controller 中的方法: ~~~ def edit respond_to do |format format.html format.json { render json: @product, status: :ok, location: @product } [1] end end ~~~ - [1] 我們讓 edit 方法,返回給我們商品的 json 格式信息。 ~~~ def update respond_to do |format| if @product.update(product_params) format.html { redirect_to @product, notice: 'Product was successfully updated.' } format.json [1] else format.html { render :edit } format.json { render json: @product.errors.full_messages.join(', '), status: :error } [2] end end end ~~~ - [1] 我們讓 update 方法,可以接受 json 的請求, - [2] 當 update 失敗時,我們需要把失敗的原因告訴客戶端,它也是 json 格式的。 當我們需要考慮 update 方法會有成功和失敗兩種可能時,我們的 ajax 調用,就要這樣來寫了: ~~~ $("#editProductForm") .on "ajax:success", (e, data, status, xhr) -> $('#editProductFormModal').modal('hide') [1] $('#product_'+data['id']+'_name').html( data['name'] ) [2] $('#product_'+data['id']+'_description').html( data['description'] ) [2] $('#product_'+data['id']+'_price').html( data['price'] ) [2] .on "ajax:error", (e, xhr, status, error) -> $('#alert-content').show() [3] $('#alert-content #msg').html( xhr.responseText ) [4] ~~~ - [1] 我們隱藏這個層 - [2] 當成功的時候,我們把修改好的信息,放回到我們的頁面中 - [3] 當失敗的時候,我們顯示個錯誤信息提示框 - [4] 我們向這個框內,填入信息 更多 controller 的介紹,后面章節還會有,這里我們要了解的是,我們頁面拿到的信息,不再是 js 片段,而是 json 格式的數據。 當我們書里大量數據的時候,json 明顯要比 js 片段更節省傳輸空間,我們也可以把處理動作寫到獨立的 js 文件中,不過,json 格式返回給我們的,是 9.9,而我們頁面顯示的是格式化后的 `CN¥ 9.90`,如果我們想把處理好格式的數據返還回來,該如何處理呢? 我們可以使用 jbuilder 做這件事,我們新建一個 `update.json.jbuilder`: ~~~ json.id @product.id json.name link_to @product.name, product_path(@product) [1] json.description @product.description json.price number_to_currency(@product.price) [2] ~~~ - [1] 我們把鏈接的地址用輔助方法生成 - [2] 我們用 number_to_currency 方法把價格格式化,這里可以使用輔助方法 如何知道我們的確使用的是 json 數據呢?我們可以查看瀏覽器的控制臺,或者查看命令行的 log 輸出。 ![](https://box.kancloud.cn/2015-08-18_55d2e480369be.png) ![](https://box.kancloud.cn/2015-08-18_55d2e4804ac12.png) 在 [這里](https://github.com/liwei78/rails-practice-code/tree/master/chapter_3/shop3.3) 可以找到我調試好的代碼。 在實踐開發中,我們會從服務端拿到很多的內容,比如幾十條訂單信息,我們可以用上面的方法把它們顯示到頁面上,也可以使用 [http://handlebarsjs.com/](http://handlebarsjs.com/) 這種模板引擎,使頁面和邏輯更加的獨立,清晰。當我們面對少量的內容時,js 片段要比寫一大堆 coffeescript 來的更省事些。所以,我們在確定選用哪種方式處理,要看我們面對的是怎樣的問題。 最后附上兩個附表。 附表一,當我們 `render json:..., status: :ok, ...` 時,status 和符號的對應,可以在這里找到,一般我們用 :ok, :create, :success, :error 就足夠了。 | Response Class | HTTP Status Code | Symbol | |-----|-----|-----| | **Informational** | 100 | :continue | | | 101 | :switching_protocols | | | 102 | :processing | | **Success** | 200 | :ok | | | 201 | :created | | | 202 | :accepted | | | 203 | :non_authoritative_information | | | 204 | :no_content | | | 205 | :reset_content | | | 206 | :partial_content | | | 207 | :multi_status | | | 208 | :already_reported | | | 226 | :im_used | | **Redirection** | 300 | :multiple_choices | | | 301 | :moved_permanently | | | 302 | :found | | | 303 | :see_other | | | 304 | :not_modified | | | 305 | :use_proxy | | | 306 | :reserved | | | 307 | :temporary_redirect | | | 308 | :permanent_redirect | | **Client Error** | 400 | :bad_request | | | 401 | :unauthorized | | | 402 | :payment_required | | | 403 | :forbidden | | | 404 | :not_found | | | 405 | :method_not_allowed | | | 406 | :not_acceptable | | | 407 | :proxy_authentication_required | | | 408 | :request_timeout | | | 409 | :conflict | | | 410 | :gone | | | 411 | :length_required | | | 412 | :precondition_failed | | | 413 | :request_entity_too_large | | | 414 | :request_uri_too_long | | | 415 | :unsupported_media_type | | | 416 | :requested_range_not_satisfiable | | | 417 | :expectation_failed | | | 422 | :unprocessable_entity | | | 423 | :locked | | | 424 | :failed_dependency | | | 426 | :upgrade_required | | | 428 | :precondition_required | | | 429 | :too_many_requests | | | 431 | :request_header_fields_too_large | | **Server Error** | 500 | :internal_server_error | | | 501 | :not_implemented | | | 502 | :bad_gateway | | | 503 | :service_unavailable | | | 504 | :gateway_timeout | | | 505 | :http_version_not_supported | | | 506 | :variant_also_negotiates | | | 507 | :insufficient_storage | | | 508 | :loop_detected | | | 510 | :not_extended | | | 511 | :network_authentication_required | 附表二:ajax 的回調方法,我們使用了 :success 和 :error,當然還有其他的一些,我們需要了解下。 | event name | extra parameters * | when | |-----|-----|-----| | `ajax:before` | | before the whole ajax business , aborts if stopped | | `ajax:beforeSend` | [event, xhr, settings] | before the request is sent, aborts if stopped | | `ajax:send` | [xhr] | when the request is sent | | `ajax:success` | [data, status, xhr] | after completion, if the HTTP response was a success | | `ajax:error` | [xhr, status, error] | after completion, if the server returned an error ** | | `ajax:complete` | [xhr, status] | after the request has been completed, no matter what outcome | | `ajax:aborted:required` | [elements] | when there are blank required fields in a form, submits anyway if stopped | | `ajax:aborted:file` | [elements] | if there are non-blank input:file fields in a form, aborts if stopped |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看