Thêm và xóa field với Rails Nested Forms and AngularJS (Phần 2)
Bài đăng này đã không được cập nhật trong 8 năm
Giới thiệu
Ở phần trước chúng ta đã thực hiện bược tạo Plan có các field con là Poll bằng AngularJs và Rails. Giả sử khi tạo ta gặp lỗi cần render lại trang new hoặc ta muốn update Plan vừa tạo thì làm sao để các field vẫn được điền đúng giá trị. Chúng ta cùng tìm hiểu trong phần này.
Validate Model
Thêm validate cho Poll
# app/models/poll.rb
class Poll < ActiveRecord::Base
belongs_to :plan
validate :title, presence: true, length: {maximum: 20}
validate :content, presence: true, length: {maximum: 200}
end
Khi tạo poll mà chưa điền đầy đủ title hoặc điền quá maxlength thì sẽ không tạo được.
Render
# app/controllers/plans_controller.rb
class PlansController < ApplicationController
def new
@ plan = Plan.new
@ poll = @ plan.polls.build
end
def create
@ plan = Plan.new plan_params
if @ plan.save
riderect_to root_path
else
render :new
end
end
end
Khi tạo lỗi sé render về trang new. Vậy làm cách nào để những thông tin mình điền trước đấy vẫn còn trên form.
Truyền dữ liệu ngược lại từ controller
Biến lưu dữ liệu của cả trang là vm.polls. Nhưng vì đây là biến của angularJS nên khi render hoặc load lại trang thì vm.polls không còn lưu được giá trị. Cách giải quyết là truyền dữ liệu cho vm.polls từ một biến trong controller
# app/controllers/plans_controller.rb
class PlansController < ApplicationController
def new
@ plan = Plan.new
@ poll = @ plan.polls.build
@ render_polls = [{title: "", content: ""}]
end
def create
@ plan = Plan.new plan_params
if @ plan.save
riderect_to root_path
else
@ render_polls = @ plan.polls.map do |poll|
{
title: poll.title,
content: poll.content
}
end
render :new
end
end
end
Tạo biến @ render_polls chứa toàn bộ dữ liệu về các polls của plan. Sau đấy sẽ truyền sang view và gán giá trị cho vm.polls Bên cạnh đó ta cũng cần sửa lại hàm new, để truyền vào giá trị mặc định cho @ render_polls khi mà plan chưa có poll con nào.
# app/views/plans/new.html.erb
<div ng-app>
<%= form_for @plan do |form| %>
<div ng-controller="PlanCtrl as vm" ng-init="vm.polls = <%= @render_polls.to_json %>">
<div ng-repeat="poll in vm.polls">
<%= form.fields_for :polls, @poll, child_index: '{{$index}}' do |poll_form| %>
<%= poll_form.text_field :title, id: 'plan_poll_{{$index}}', "ng-model": "poll.title" %>
<%= poll_form.text_field :content, id: 'plan_poll_{{$index}}', "ng-model": "poll.content" %>
<% end %>
<a href="#" ng-click="vm.remove($index)" ng-show="vm.isRemovable()">Remove</a>
</div>
<a href="#" ng-click="vm.add()">Add</a>
</div>
<% end %>
</div>
Khi này vm.polls đã có đủ dữ liệu của các poll con của plan.
Hiện error messages
Ngoài việc truyền dữ liệu từ controller ra, ta có thể truyền thêm error messages để người dùng có thể biết mình nhập sai thông tin nào.
# app/controllers/plans_controller.rb
class PlansController < ApplicationController
def new
@ plan = Plan.new
@ poll = @ plan.polls.build
end
def create
@ plan = Plan.new plan_params
if @ plan.save
riderect_to root_path
else
@ render_polls = @ plan.polls.map do |poll|
{
title: poll.title,
content: poll.content,
title_err_mes: poll.errors.messages[:title].try(:first),
content_err_mes: poll.errors.messages[:content].try(:first)
}
end
render :new
end
end
end
title_err_mes
và content_err_mes
lưu error message của title và content.
Ngoài view, ta sẽ hiển thị error message nếu có.
# app/views/plans/new.html.erb
<div ng-app>
<%= form_for @plan do |form| %>
<div ng-controller="PlanCtrl as vm" ng-init="vm.polls = <%= @render_polls.to_json %>">
<div ng-repeat="poll in vm.polls">
<%= form.fields_for :polls, @poll, child_index: '{{$index}}' do |poll_form| %>
<%= poll_form.text_field :title, id: 'plan_poll_{{$index}}', "ng-model": "poll.title" %>
<div class="error" ng-bind="poll.title_err_mes"></div>
<%= poll_form.text_field :content, id: 'plan_poll_{{$index}}', "ng-model": "poll.content" %>
<div class="error" ng-bind="poll.content_err_mes"></div>
<% end %>
<a href="#" ng-click="vm.remove($index)" ng-show="vm.isRemovable()">Remove</a>
</div>
<a href="#" ng-click="vm.add()">Add</a>
</div>
<% end %>
</div>
Update Plan
Trong trường hợp muốn chỉnh sửa 1 plan, thì cách làm vẫn tương tự là ta truyền dữ liệu đã có cho vm.polls trong hàm edit.
# app/controllers/plans_controller.rb
class PlansController < ApplicationController
def new
@ plan = Plan.new
@ poll = @ plan.polls.build
end
def edit
@ plan = Plan.find params[:id]
@ poll = @ plan.polls.build
@ render_polls = @ plan.polls.map do |poll|
{
title: poll.title,
content: poll.content
}
end
end
def create
@ plan = Plan.new plan_params
if @ plan.save
riderect_to root_path
else
@ render_polls = @ plan.polls.map do |poll|
{
title: poll.title,
content: poll.content,
title_err_mes: poll.errors.messages[:title].try(:first),
content_err_mes: poll.errors.messages[:content].try(:first)
}
end
render :new
end
end
def update
@ plan = Plan.find params[:id]
if @ plan.update_attributes plan_params
riderect_to root_path
else
@ render_polls = @ plan.polls.map do |poll|
{
title: poll.title,
content: poll.content,
title_err_mes: poll.errors.messages[:title].try(:first),
content_err_mes: poll.errors.messages[:content].try(:first)
}
end
render :edit
end
end
end
Còn view trang edit thì giống hoàn toàn trang new.
Tổng kết
Trên đây là toàn bộ bài viết về việc thêm và xóa field với Rails Nested Forms and AngularJS, bao gồm cả trường hợp tạo lỗi, update và hiển thị error messages.
All rights reserved