Папярэдні занятак

Валідацыя

Калі мы стваралі такое ж прыкладаньне ў кансолі – ўсё было зразумела. Мы проста выводзілі паведамленьне аб тым, што ўведзеныя дадзеныя ня слушныя, й адмаўляліся іх захоўваць.

А як быць тут? Як перадаць паведамленьне на наступную старонку?

Rails мае інструмэнт, які завецца flash messages які дазваляе Вам дадаць паведамленьне, якое будзе існаваць да пераходу на наступную старонку:

def create
    contact = Contact.new(params[:contact])

    if contact.save
      redirect_to '/contacts'
    else
      flash[:alert] = 'Check out your contact. Something went wrong.'
      render :new
    end
end

Наступным крокам дадайце наступную лінію першай у new.html.erb.

    <%= flash[:alert] %>

Паспрабуйце захаваць пусты кантакт. Адлюстроўваецца памылка? Ура! Калі не – магчыма Вы забыліся дадаць валідацыю ў саму мадэль.

Адлюстраваньне памылкаў

Праз flash усё працуе добра, але кожны раз, калі чалавек робіць памылку – ён страчвае ўсю інфармацыю. Ды й ня вельмі тлумачальна паведамленьне “Something went wrong”.

Зьмяніце Ваш кантролер каб ён выглядаў так (звярніце ўвагу на зьменныя асобнікаў):

class ContactsController < ApplicationController
  def index
    @contacts = Contact.all
  end

  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(params[:contact])

    if @contact.save
      redirect_to '/contacts'
    else
      render :new
    end
  end
end

Вью мае доступ да зьменных асобнікаў таго кантролера, да якога яно належыць. Таму можам выкарыстоўваць іх.

Зьмяніце new.html.erb:

<%= flash[:notice] %>
<%= flash[:alert] %>

<h1>New contact</h1>

<%= form_for @contact do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :phone %>
  <%= f.text_field :phone %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <%= f.submit %>
<% end %>

Мэтад Rails form_for генеруе HTML форму для аб'екта, які Вы ў яго перадалі. Абнавіце старонку new у браўзеры. Яна выглядае гэтак жа як і раней, але html трошкі зьмяніўся. Праглядзіце яго. Зараз калі Вы паспрабуеце стварыць кантакт з пустым імём дадзеныя захаваюцца. Выдатна!

Зараз давайце дададзім адлюстраваньне памылкаў у вью:

<%= flash[:notice] %>
<%= flash[:alert] %>

<h1>New contact</h1>

<% if @contact.errors.any? %>
  <h3>Please correct these problems:</h3>
  <ul>
    <% @contact.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

<%= form_for @contact do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :phone %>
  <%= f.text_field :phone %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <%= f.submit %>
<% end %>

Мы зноў звярнуліся да нашага добрага сябра errors, але ў гэты адлюстрованым у html.

Спасылка

Дадайце спасылку з app/views/contacts/index.html.erb на старонку стварэньня новага кантакту:

<h1>Contacts</h1>

<h2>A user-contributed phone (and email) book.</h2>

<table>
  <tr>
    <td>Name</td>
    <td>Phone</td>
    <td>Email</td>
  </tr>
  <% @contacts.each do |contact| %>
    <tr>
      <td><%= contact.name %></td>
      <td><%= contact.phone %></td>
      <td><%= contact.email %></td>
    </tr>
  <% end %>
</table>

<p><%= link_to 'Add contact', '/contacts/new' %></p>

Звярніце ўвагу на мэтад-дапаможнік link_to; ён генеруе спасылку.

REST-Рэсурсы

Маршруты накшталт:

RailsFirst::Application.routes.draw do
  match 'contacts' => 'contacts#create', :via => :post
  match 'contacts/:id' => 'contacts#show', :via => :get
  match 'contacts/:id' => 'contacts#update', :via => :put
  match 'contacts/:id' => 'contacts#destroy', :via => :delete
  match 'contacts' => 'contacts#index', :via => :get
end

вельмі настолькі распаўсюджаны ў Rails, што нават ёсьць спецыяльны мэтад:

Wikipages::Application.routes.draw do
  resources :contacts
end

Гэта нашмат больш прыгажэй. Зьмяніце свой файл routes.rb, каб ён выкарыстоўваў resources.

Зараз у Вас таксама зьявілася магчымасьць выкарыстоўваць мэтады-дапаможнікі Rails. Замест <%= link_to 'Add contact', '/contacts/new' %> зараз можна пісаць <%= link_to 'Add contact', new_contact_path %>. Выканайце $ rake routes каб праглядзець якія яшчэ ёсьць дапаможнікі. Адрэфактарце Ваш код, каб ён выкарыстоўваў іх.

Праглядзіце таксама Rails Guide on routing.

Edit-action і partials

Давайце створым старонку каб рэдагаваць кантакт. Назавіце яе edit.html.erb:

<% provide :title, "Edit #{@contact.name}" %>

<h1><%= yield :title %></h1>

<% if @contact.errors.any? %>
  <h3>Please correct these problems:</h3>
  <ul>
    <% @contact.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

<%= form_for @contact do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :phone %>
  <%= f.text_field :phone %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <%= f.submit %>
<% end %>

Дадайце спасылку на старонку рэдагаваньня кантакта ў табліцу з кантактамі на старонцы index.html.erb. Ваш мэтад-дапаможнік мусіць выглядаць вось так edit_contact_path(contact) - каб мэтад ведаў, які кантакт трэба рэдагаваць, мы перадаем аб'екта кантакта ўнутр.

Перад тым як рухацца да мэтада update, ёсьць адно пытаньне, якое трэба вырашыць з вью edit: яно выглядае зусім аднолькава з вью new. Калі мы дададзім новы атрыбут у кантакт, нам прыйдзецца зьмяняць дзьве старонкі. Гэта не DRY.

У Rails ёсьць інструмэнт, які завецца partials які дазваляе захоўваць часткі вью, каб далей іх выкарыстоўваць у некалькіх месцах. Давайце створым partial для формы. Стварыце файл у app/views/contacts які завецца _form.html.erb. Ніжняе падкрэсліваньне кажа што гэты файл ёсьць partial. Зрушце код у файл, каб ён выглядаў вось так:

<% if @contact.errors.any? %>
  <h3>Please correct these problems:</h3>
  <ul>
    <% @contact.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

<%= form_for @contact do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :phone %>
  <%= f.text_field :phone %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <%= f.submit %>
<% end %>

Потым, ва ўсіх вью зьмяніце код формы на <%= render 'form' %>. Праверце. Усё тое ж самае, проста чысьцей.

Update

А зараз Вам засталося напісаць мэтад update у кантролера. Я ўпэўнен, Вы зробіце гэта самі!

Layouts і provide

Зараз калі мы ствараем новы кантакт мы пераходзім на старонку new, што насамрэч ня мае сэнсу. Перанакіруйце замест гэтага наindex. Праверце rake routes каб праглядзець сьпіс мэтаў якія ёсьць у прыкладаньні для навігацыі. Памятайце пра тое што трэба зьмяніць кантролер таксама.

Зараз, хаця Вы й стварылі кантакт флэш-паведамленьне страцілася. Гэта таму што мы дадалі флэш толькі ў старонку contacts/new. Ці не было б файна калі гэтыя паведамленьні маглі зьяўляцца на кожнай старонцы?

Канешне было б, таму зробім. Адчыніце файл app/views/layouts/application.html.erb. Бачыце <%= yield %>? Калі Вы бачыце старонку сгенераваную Rails, гэты файл насамрэч і адлюстроўваецца а старонка накшталт new.html.erb устаўляецца замест <%= yield %>. Таму ўсё што б мы туды не размесцілі будзе адлюстроўвацца на кожнай старонцы. Зрушце флэш-паведамленьні з contacts/new у layout, прама зверху yield.

Лаяўт таксама мае назвау старонкі, якая пакуль Contacts. Але што лепей мець - гэта асобную назву для кожнай старонкі накшталт Contacts | New contact. Вось як гэта зрабіць.

Давайце пачнем з вью new. Зьмяніце верхнія радкі файла на наступныя:

<% provide :title, "New contact" %>

<h1><%= yield :title %></h1>

І зьмяніце лаяўт на наступны:

<title>Contacts | <%= yield :title %></title>

Мэтад provide захоўвае любы кантэнт, які Вы яму даіцё, які можа быць адлюстрованы дзе ўзгодна. Зьмяніце старонку contacts/index каб яна мела назву Home.

Выдаленьне кантактаў

Спачатку вось код, які атрымаўся ў мяне. Код ў мэтад destroy таксама дададзены.

class ContactsController < ApplicationController
  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(params[:contact])

    if @contact.save
      flash[:notice] = "Your contact was successfully created."
      redirect_to contacts_path
    else
      render :new
    end
  end

  def edit
    @contact = Contact.find(params[:id])
  end

  def update
    @contact = Contact.find(params[:id])
    if @contact.update_attributes(params[:contact])
      flash[:notice] = "Your contact was successsfully updated."
      redirect_to contacts_path
    else
      render :edit
    end
  end

  def destroy
    @contact = Contact.find(params[:id])
    @contact.destroy
    flash[:notice] = "Your contact was successfully deleted."
    redirect_to contacts_path
  end

  def index
    @contacts = Contact.all
  end
end

Index вью:

<% provide :title, 'Home' %>

<h1>Wikipages</h1>

<h2>A user-contributed phone (and email) book.</h2>

<table>
  <tr>
    <td>Name</td>
    <td>Phone</td>
    <td>Email</td>
    <td></td>
    <td></td>
  </tr>
  <% @contacts.each do |contact| %>
    <tr>
      <td><%= contact.name %></td>
      <td><%= contact.phone %></td>
      <td><%= contact.email %></td>
      <td><%= link_to 'Edit', edit_contact_path(contact) %></td>
      <td><%= link_to 'Delete', contact, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
    </tr>
  <% end %>
</table>

<p><%= link_to 'Add contact', new_contact_path %></p>

Звярніце ўвагу на тое, як спасылка Delete мае :method => :delete які кажа Rails які мэтад HTTP выкарыстоўваць для запыта й таксама ўключае попап з пацверджаньнем выдаленьня кантакта.

Asset pipeline і Bootstrap

Афігець. Вы ўжо маеце першае працуючае Вэб-прыкладаньне! Але... пакуль даволі дрэнна выглядаючае. Давайце дададзім трошкі CSS каб адпрыгожыць яго.

Некалькі дызайнераў з Twitter вельмі дапамаглі Вэб-распрацоўцы зрабіўшы выдатна css-framework - Bootstrap мэта якога палепшыць якасьць і зьменшыць час Вэб-разпрацоўцы. Існуе шмат фрэймворкаў, але Bootstrap зрабіўся чымсьці накшталт стандарта Сеціва. Спампуйце Bootstrap, потым скапіюйце bootstrap.css у app/assets/stylesheets, bootstrap.js у app/assets/javascripts, і абодва файла ў img у app/assets/images. Зараз Вы маеце доступ да гэтых файлаў са свайго прыкладаньня.

Мы ня будзем глыбока разглядаць як працуе asset pipeline, за выключэньне таго, што скажам, што ўсё што знаходзіцца ў тэчцы assets можна выкарыстоўваць у Вашым прыкладаньні. Акрамя гэтага asset pipeline робіць шмат клёвых рэчаў, накшталт збіраньня ўсяго ў адзін файл і мініфакацыю Вашых asset'аў. Таксама asset pipeline дазваляе выкарыстоўваць такія мовы праграмаваньня як Haml, SaSS, and Coffeescript. Пачытайце болей пра яго на Asset Pipeline Rails Guide.

Калі Вы абнавіце старонку Вы заўважыце, што некаторыя з стыляў Bootstrap ужо прымяніліся. Зараз давайце дададзім класы Bootstrap у нашыя старонкі. Зьмяніце цела Вашага лаяўта:

<body>
  <div class="container">
    <% if flash[:notice] %>
      <div class="alert alert-info"><%= flash[:notice] %></div>
    <% end %>
    <% if flash[:alert] %>
      <div class="alert alert-error"><%= flash[:alert] %></div>
    <% end %>

    <%= yield %>
  </div>
</body>

Зараз index:

<table class="table">
  <tr>
    <td>Name</td>
    <td>Phone</td>
    <td>Email</td>
    <td></td>
    <td></td>
  </tr>
  <% @contacts.each do |contact| %>
    <tr>
      <td><%= contact.name %></td>
      <td><%= contact.phone %></td>
      <td><%= contact.email %></td>
      <td><%= link_to 'Edit', edit_contact_path(contact) %></td>
      <td><%= link_to 'Delete', contact, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
    </tr>
  <% end %>
</table>

<p><%= link_to 'Add contact', new_contact_path, :class => 'btn btn-primary' %></p>

Форма:

<% if @contact.errors.any? %>
  <div class="alert alert-error">
    <h3>Please correct these problems:</h3>
    <ul>
      <% @contact.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

<%= form_for @contact do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :phone %>
  <%= f.text_field :phone %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <div class="form-actions">
  <%= f.submit :class => 'btn btn-primary' %>
  </div>
<% end %>

Праглядзіце дакумэнтацыю Bootstrap каб праглядзець якія стылі й класы Вы маеце.

Hacker News

Hacker News - гэта выдатны сайт стартаперскіх навінаў. Вось яго й трэба пабудаваць з дапамогай Rails. Але спачатку без аўтарызацыі. Яе даробім далей.

Наступны занятак