zl程序教程

您现在的位置是:首页 >  前端

当前栏目

[Rails Level 2] RENDERING EXTREMITIES -- Ex (Return in Json, resoure route)

JSONJSON -- in level return Route Rails
2023-09-14 08:59:21 时间

RENDER

Complete the method below so that if the ammo is low it will render the fire_and_reload view, otherwise it should render the fire_weapon view.

1. In this case, render will take a single argument that maps to the name of the view.
2. If you don't call render, Rails will automatically render the view of the same name as the action
3. The correct answer should be:

class WeaponsController < ApplicationController 
  def fire_weapon
    @weapon = Weapon.find(params[:id]) 
    @weapon.fire!

    if @weapon.low_ammo?
      render :fire_and_reload 
    end
  end 
end

 

CUSTOM RESOURCE ROUTES

Create two custom member routes on the weapons resource, so you have a put method calledtoggle_condition and a put method called reload.

1. To add routes to the :weapons resource pass a block, like this:
resources :weapons do
  # custom routes go here
end

2. To add a custom route, call the HTTP method and then pass in the name, like this:
resources :weapons do
  put :toggle_condition, on: :member
end

RailsForZombies::Application.routes.draw do
  resources :zombies do
    resources :weapons do
      put :toggle_condition, on: :member
      put :reload, on: :member
    end
  end
end

 

RENDER JSON

Complete the create method below. When @weapon.save is successful it should render the @weapon object in JSON, have status :created, and set the location to the @weapon's show url. When @weapon.save fails it should return the @weapon.errors and have the status:unprocessable_entity.

1. To render a @weapon as json, you can do this:
render json: @weapon

2. render takes the options :status and :location, like this:
render json: @weapon, status: :created, location: @weapon

class WeaponsController < ApplicationController 
  def create
    @weapon = Weapon.new(params[:weapon]) 
    if @weapon.save
      render json: @weapon, status: :created,
      location: @weapon
    else
      render json: @weapon.errors, status: :unprocessable_entity
    end
  end 
end

 

RENDER JSON W/OPTIONS

Complete the controller so that it returns in JSON only the amount of ammo which is left in the weapon. If the ammo has less than 30 bullets it should return the status code :ok, and if not it should return the status code :unprocessable_entity.

1. To limit the json that is output, you'd use to_json and pass in the :only option, like this:
@weapon.to_json(only: :ammo)

2. render takes the option :status, like this:
render json: @weapon.to_json(only: :ammo), status: :ok 

class WeaponsController < ApplicationController
  def reload
    @weapon = Weapon.find(params[:id]) 

    if @weapon.ammo < 30
      @weapon.reload(params[:ammo_to_reload])
      render json: @weapon.to_json(only: :ammo), status: :ok
    else
      render json: @weapon.to_json(only: :ammo), status: :unprocessable_entity
    end
  end
end

 

MORE TO JSON OPTIONS

Modify the show action so that the JSON it renders includes the zombie record the @weapon belongs to. Also make it exclude the :id:created_at, and :updated_at fields.

1. To exclude some data in a to_json call, use :except, like this:
@weapon.to_json(except: [:updated_at, :id, :created_at])

2. To include an association in a to_json call, use the :include option, like this:
@weapon.to_json(except: [:updated_at, :id, :created_at], include: :zombie)

class WeaponsController < ApplicationController
  def show
    @weapon = Weapon.find(params[:id])
    render json: @weapon.to_json(except: [:id, :created_at, :updated_at], include: :zombie)
  end
end

 

AS JSON

Edit the as_json method so the Zombie class only returns the zombie's name and weapons (useinclude). Only return the weapon's name and ammo.

class Zombie < ActiveRecord::Base
  has_many :weapons

  def as_json(options=nil)
    super (options || {only: :name, include: {weapons: {only: [:name, :ammo]}}})
  end 
end

 

LINK REMOTE

Modify the show.html.erb view below so that both the Toggle link and the Reload form use AJAX. All you need to do is add the option that makes them ajaxified.

http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to

1. Use the remote option to make a link use Ajax, like this:
link_to "Toggle", toggle_condition_weapon_path(@weapon), remote: true

<ul>
  <li>
    <em>Name:</em> <%= @weapon.name %>
  </li> 
  <li>
    <em>Condition:</em>
    <span id="condition"><%= @weapon.condition %></span>
    <%= link_to "Toggle", toggle_condition_weapon_path(@weapon), remote: true %>
  </li> 
  <li>
    <em>Ammo:</em>
    <span id="ammo"><%= @weapon.ammo %></span>
  </li>
</ul>

<%= form_for @weapon, url: reload_weapon_path(@weapon), remote: true do |f| %>
  <div class="field">
    Number of bullets to reload:
    <%= number_field_tag :ammo_to_reload, 30 %> <br /> <%= f.submit "Reload"%>
  </div>
<% end %>

 

AJAX RESPONSE

Modify the toggle_condition action so that it responds to JavaScript, and complete thetoggle_condition.js.erb using jQuery to update the condition span with the @weapon's changed condition and make it highlight.

<p id="notice"><%= notice %></p>
<ul>
  <li>
    <em>Name:</em>
    <%= @weapon.name %>
  </li>
  <li>
    <em>Condition:</em>
    <span id="condition"><%= @weapon.condition %></span> 
    <%= link_to "Toggle", toggle_condition_user_weapon_path(@user, @weapon), remote: true %>
  </li>
</ul>

Answer:

weapons_controller.rb:

class WeaponsController < ApplicationController
  def toggle_condition
    @weapon = Weapon.find(params[:id]) 
    @weapon.toggle_condition 

    respond_to do |format|
      format.html { redirect_to @weapon, notice: 'Changed condition' }
      format.js
    end
  end
end

toggle_condition.ks.erb:

1. To get an action to respond to an Ajax request, you need to add format.js, like this:
respond_to do |format|
  format.html { redirect_to @weapon, notice: 'Changed condition' }
  format.js 
end

2. You can add javascript code with erb tags in toggle_condition.js.erb, like this:
$('#condition').text("<%= @weapon.condition %>").effect("highlight")

//=require jQuery_ui

$("#condition").text("<%= @weapon.condition %>").effect('highlight')

 

AJAX RESPONSE II

Now write the controller and JavaScript code needed to properly reload the weapon using the ajaxified form. In the reload.js.erb use jQuery to update the #ammo text to the current @weapon.ammo value and if the ammo value is over or equal to 30, fadeOut the #reload_form div.

<p id="notice"><%= notice %></p>
<ul>
  <li>
    <em>Name:</em>
    <%= @weapon.name %>
  </li>
  <li>
    <em>Condition:</em>
    <span id="condition"><%= @weapon.condition %></span> 
    <%= link_to "Toggle", toggle_condition_user_weapon_path(@user, @weapon), remote: true %>
  </li>
  <li>
    <em>Ammo:</em>
    <span id="ammo"><%= @weapon.ammo %></span>
  </li>
</ul>
 
<div id="reload_form">
<%= form_for [@user, @weapon], url: reload_user_weapon_path(@user, @weapon), remote:true do |f| %>
  <div class="field">
    Number of bullets to reload:
    <%= number_field_tag :ammo_to_reload, 30 %> <br />
    <%= f.submit "Reload" %>
  </div>
<% end %>
</div>
 
<%= link_to 'Edit', edit_weapon_path(@weapon) %> |
<%= link_to 'Back', weapons_path %>

Answer:

#weapons_controller.erb

class WeaponsController < ApplicationController
  def reload
    @weapon = Weapon.find(params[:id]) 

    respond_to do |format|
      if @weapon.ammo < 30
        @weapon.reload(params[:ammo_to_reload])
      
        format.json { render json: @weapon.to_json(only: :ammo), status: :ok }
        format.html { redirect_to @weapon, notice: 'Weapon ammo reloaded' }
     else
        format.json { render json: @weapon.to_json(only: :ammo), status: :unprocessable_entity }
        format.html { redirect_to @weapon, notice: 'Weapon not reloaded' }
      end
      format.js
    end
  end
end
$("#ammo").text("<%= @weapon.ammo %>")
<% if @weapon.ammo >= 30%>
  $("#reload_form").fadeOut();
<% end %>

 

COFFEESCRIPT

Instead of returning jQuery which gets executed on the client-side, lets write the ajax request in CoffeeScript communicating with JSON. It should do the same thing as the last challenge, updating & highlighting the ammo, and fading out the form (hint: fade out the wrapper element) if ammo is equal or above 30. 
Tip for your ajax form: data: {ammo_to_reload: ammo}.

class WeaponsController < ApplicationController
  def reload
    @weapon = Weapon.find(params[:id]) 
 
    respond_to do |format|
      if @weapon.ammo < 30
        @weapon.reload(params[:ammo_to_reload])
      
        format.json { render :json => @weapon.to_json(:only => :ammo), status: :ok }
        format.html { redirect_to @weapon, notice: 'Weapon ammo reloaded' }
      else
        format.json { render :json => @weapon.to_json(:only => :ammo), status: :unprocessable_entity }
        format.html { redirect_to @weapon, notice: 'Weapon not reloaded' }
      end
    
      format.js
    end
  end
end
<p id="notice"><%= notice %></p>
<ul>
  <li>
    <em>Name:</em>
    <%= @weapon.name %>
  </li>
  <li>
    <em>Condition:</em>
    <span id="condition"><%= @weapon.condition %></span> 
    <%= link_to "Toggle", toggle_condition_user_weapon_path(@user, @weapon), remote: true %>
  </li>
  <li>
    <em>Ammo:</em>
    <span id="ammo"><%= @weapon.ammo %></span>
  </li>
</ul>
 
<div id="reload_form">
<%= form_for [@user, @weapon], url: reload_user_weapon_path(@user, @weapon), remote:true do |f| %>
  <div class="field">
    Number of bullets to reload:
    <%= number_field_tag :ammo_to_reload, 30 %> <br />
    <%= f.submit "Reload" %>
  </div>
<% end %>
</div>
 
<%= link_to 'Edit', edit_weapon_path(@weapon) %> |
<%= link_to 'Back', weapons_path %>

Answer:

$(document).ready ->
  $('div#reload_form form').submit (event) ->
    event.preventDefault()
    url = $(this).attr('action')
    ammo = $('#ammo_to_reload').val()
    $.ajax
      type: 'put',
      data: {ammo_to_reload: ammo},
      url: url,
      dataType: 'json',
      success: (json)->
        $("#ammo").text(json.ammo).effect('highlight')
        if json.ammo >= 30
          $("#reload_form").fadeOut()