טפסים דינמיים בריילס
במבנה CRUD הרגיל שריילס אוהב לייצר יש לכל שורה בבסיס הנתונים אוביקט שמתאים לה, ולכל אוביקט כזה יש טופס עריכה שמתאים לו. יצירת "ישות" חדשה בריילס יוצרת דף אינדקס, דף צפיה וטופס עריכה.
לפעמים משתלם להשתמש בדף האינדקס בתור טופס עריכה מיידי, במיוחד בישויות קטנות. בואו נראה איך עושים את זה.
1. נקודת הפתיחה
יצרתי פרויקט ריילס חדש ובו יצרתי ישות שנקראת Reminder באמצעות הפקודות:
rails new demoapp
cd demoapp
bundle exec rails g scaffold reminder text:string done:boolean
bundle exec rails db:migrate
המנגנון יצר קובץ אינדקס בשם app/views/reminders/index.html.erb שנקרא כך:
<p id="notice"><%= notice %></p>
<h1>reminders</h1>
<table>
<thead>
<tr>
<th>text</th>
<th>done</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @reminders.each do |reminder| %>
<tr>
<td><%= reminder.text %></td>
<td><%= reminder.done %></td>
<td><%= link_to 'show', reminder %></td>
<td><%= link_to 'edit', edit_reminder_path(reminder) %></td>
<td><%= link_to 'destroy', reminder, method: :delete, data: { confirm: 'are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'new reminder', new_reminder_path %>
בשביל לאפשר עריכה השינוי הראשון שנרצה לעשות הוא להחליף כל שורה בטבלא בטופס. למזלנו ריילס יצר את הטופס בתור partial ולכן אפשר להחליף את תוכן הקובץ ללולאה הבאה:
<% @reminders.each do |reminder| %>
<%= render partial: 'form', locals: { reminder: reminder } %>
<% end %>
<%= link_to 'New Reminder', new_reminder_path %>
בשביל שהטופס יראה יפה אוסיף לטופס קלאס בשם form-inline ובקובץ ה css אוסיף את הקוד הבא:
.form-inline {
label {
display: inline;
}
.field, .actions {
display: inline-block;
}
}
אם יש לכם ריילס ואתם רוצים לראות את הקוד בפעולה הוא נמצא כאן:
https://github.com/ynonp/rails-forms-in-index/tree/step1
2. ומה עם תוספת פריט?
אם אנחנו כבר עושים את כל העבודה בתוך האינדקס יהיה נחמד לאפשר שם גם הוספה דינמית של פריט. בשביל זה נצטרך קוד JavaScript שיפנה ב Ajax לשרת, יביא את השורה החדשה (כלומר את הטופס החדש) ויצרף אותה לרשימה.
נתחיל בצד הריילס להוסיף נתיב שישלח טופס ריק ללקוח. הנתיב new נשמע כמו רעיון טוב אם הוא רק היה שולח את הטופס בלי ה Layout של כל הדף. ניגש ל Controller לתקן את ההתנהגות:
def new
@reminder = Reminder.new
if request.xhr?
return render template: 'reminders/_form', locals: { reminder: @reminder }
end
end
כעת פניה רגילה לנתיב תחזיר את טופס יצירת הפריט החדש כמו שהיה קודם, אבל פניה בבקשת Ajax תחזיר את הטופס בלבד בלי ה Layout המלא.
משימה שניה היא להוסיף את קוד ה JavaScript שבעת לחיצה על כפתור "פריט חדש" יביא את הטופס ויצרף אותו לרשימה. נוסיף את הבלוק הבא של קוד jQuery לקובץ app/assets/javascripts/reminders.js:
$(document).on('click', '.ajax-new-reminder', async function(e) {
e.preventDefault();
const form = await $.get('/reminders/new');
$('.reminders-index').append(form);
});
ולסיום נחזור לקובץ ה view כדי להוסיף את הקלאסים המתאימים לחיבור עם ה JavaScript:
<div class="reminders-index">
<% @reminders.each do |reminder| %>
<%= render partial: 'form', locals: { reminder: reminder } %>
<% end %>
</div>
<%= link_to 'New Reminder', new_reminder_path, class: 'ajax-new-reminder' %>
התוצאה היא טבלא שמציגה טופס עבור כל פריט תזכורת, וכפתור שמוסיף שורה לטבלא באמצעותו אפשר ליצור תזכורות חדשות. את הקוד המלא אפשר לראות כאן:
https://github.com/ynonp/rails-forms-in-index
נקודה לסיום- היכולת שלנו דרך מעט מאוד שינויים להיכנס למנגנון של ריילס ולשנות את מבנה האפליקציה תוך שמירה ושימוש חוזר בכל הרכיבים שריילס יצר היא הסיבה שאני כל כך אוהב את ריילס וממליץ בחום ללמוד את הפריימוורק הזה. גם אם לא תשתמשו בו, הבחירות הטכנולוגיות שלהם והראייה הרחבה זה משהו שנדיר למצוא בפריימוורקס בסדר גודל כזה.