TECH CAMPは7週目より最終課題に入っていますが、10週目までに必須実装を完了させないといけません。最終課題はメルカリのクローンフリマアプリのチーム開発。
【担当した実装】
・パンくず機能
・商品情報編集機能
そんな中非常に苦労したのが「商品情報編集機能」。出品した商品の編集を行えるようにならないといけません。ルーティング、コントローラー、cssは割愛し、hamlとjsを説明致します。
(対象のコードの記述以外は省略しています。)
・元々登録してあった情報の表示(画像・カテゴリ以外)
= form.label '商品名'
%span.form-group__require 必須
= form.text_field :name, placeholder:"40文字まで",class:"item_name_form", value: "#{@items.name}"
= form.select :item_condition,Item.item_conditions.keys,{include_blank: "選択してください", selected: @items.item_condition}
このvalue:○○、selected:○○で登録済みの情報を表示しています。
(コントローラーのeditアクションで@itemsを定義しています)
・元々登録してあった情報の表示(カテゴリ)
.status_register__status_category_group__category__choose
= form.collection_select :category_id,Category.where(ancestry: nil).limit(13), :id, :name,{prompt: '---', selected: @parent.id}, {id: 'parent_category'}
.status_register__status_category_group__category__choose__edit1
= icon('fa', 'chevron-down', id: 'child_icon')
= form.collection_select :category_id,Category.where(ancestry: "#{@parent.id}"), :id, :name,{prompt: '---', selected: @child.id}, {id: 'child_category'}
.status_register__status_category_group__category__choose__edit2
= icon('fa', 'chevron-down', id: 'grandchild_icon')
= form.collection_select :category_id,Category.where(ancestry: "#{@parent.id}/#{@child.id}"), :id, :name,{prompt: '---', selected: @grandchild.id}, {id: 'grandchild_category'}
$('#child_icon').remove();
$('#child_category').remove();
$('#grandchild_icon').remove();
$('#grandchild_category').remove();
商品出品時は親カテゴリの選択ボックスのみを表示しておき、それを選択する事でjsにて子・孫カテゴリの選択ボックスを出現させていました。この商品編集では最初に全て表示させておき、親カテゴリを選んだ時点でjsにて元々表示していた子・孫カテゴリの選択ボックスを削除する事で選び直す事が可能となりました。
・画像関係
.form_section.image_register
= form.label '出品画像'
%span.form-group__require 必須
%p 最大10枚まで貼れます。
.post__drop__box__container
.prev-content
- @items.item_imgs.each do |img|
.preview-box
.upper-box
= image_tag "#{img.url}", width: "112", height: "112", alt: "preview"
.lower-box
.delete-box
%span 削除
.label-content
= form.label :"item_imgs_attributes_#{@items.item_imgs.length}_url", class: "label-box", id: "label-box--#{@items.item_imgs.length}" do
%pre.label-box__text-visible クリックしてファイルをアップロード
.hidden-content
= form.fields_for :item_imgs do |i|
= i.file_field :url, class: "hidden-field"
= i.check_box:_destroy, class: "hidden-checkbox"
- @items.item_imgs.length.upto(9) do |i|
%input{name: "item[item_imgs_attributes][#{i}][url]", id: "item_item_imgs_attributes_#{i}_url", class:"hidden-field", type:"file"}
$(document).on('turbolinks:load', function(){
$(function(){
function buildHTML(count) {
var html = `<div class="preview-box" id="preview-box__${count}">
<div class="upper-box">
<img src="" alt="preview">
</div>
<div class="lower-box">
<div class="delete-box" id="delete_btn_${count}">
<span>削除</span>
</div>
</div>
</div>`
return html;
}
//商品編集ページへ遷移した際のアクション
if (window.location.href.match(/\/items\/\d+\/edit/)){
//登録済み画像のプレビューの表示
var prevContent = $('.label-content').prev();
labelWidth = (620 - $(prevContent).css('width').replace(/[^0-9]/g, ''));
$('.label-content').css('width', labelWidth);
//プレビューにidを追加
$('.preview-box').each(function(index, box){
$(box).attr('id', `preview-box__${index}`);
})
//削除ボタンにidを追加
$('.delete-box').each(function(index, box){
$(box).attr('id', `delete_btn_${index}`);
})
var count = $('.preview-box').length;
//プレビューが10枚あるときは投稿ボックスを消す
if (count == 10) {
$('.label-content').hide();
}
}
// 投稿ボックスの横幅調整
function setLabel() {
var prevContent = $('.label-content').prev();
labelWidth = (620 - $(prevContent).css('width'));
$('.label-content').css('width', labelWidth);
}
// 画像をアップロードする際のアクション
$(document).on('change', '.hidden-field', function() {
setLabel();
//hidden-fieldのidの数値を取得
var id = $(this).attr('id').replace(/[^0-9]/g, '');
//labelボックスのidとforを更新
$('.label-box').attr({id: `label-box--${id}`,for: `item_item_imgs_attributes_${id}_url`});
//選択したfileのオブジェクトを取得
var file = this.files[0];
var reader = new FileReader();
//readAsDataURLで指定したFileオブジェクトを読み込む
reader.readAsDataURL(file);
//読み込み時に発火するイベント
reader.onload = function() {
var image = this.result;
//画像が元々なかった場合はhtmlを追加
if ($(`#preview-box__${id}`).length == 0) {
var count = $('.preview-box').length;
var html = buildHTML(id);
//投稿ボックスの直前に画像を追加
var prevContent = $('.label-content').prev();
$(prevContent).append(html);
}
//画像を追加
$(`#preview-box__${id} img`).attr('src', `${image}`);
var count = $('.preview-box').length;
//画像が10個あったら投稿ボックスを隠す
if (count == 10) {
$('.label-content').hide();
}
//画像を削除したフィールドに削除用のチェックボックスがあった場合、チェックを外す
if ($(`#item_item_imgs_attributes_${id}__destroy`)){
$(`#item_item_imgs_attributes_${id}__destroy`).prop('checked',false);
}
setLabel();
//ラベルのidとforの値を変更
if(count < 10){
$('.label-box').attr({id: `label-box--${count}`,for: `item_item_imgs_attributes_${count}_url`});
}
}
});
// 画像の削除ボタンをクリックした際のアクション
$(document).on('click', '.delete-box', function() {
var count = $('.preview-box').length;
setLabel(count);
var id = $(this).attr('id').replace(/[^0-9]/g, '');
$(`#preview-box__${id}`).remove();
//削除用チェックボックスの有無を判定
if ($(`#item_item_imgs_attributes_${id}__destroy`).length == 0) {
//画像を削除
$(`#item_item_imgs_attributes_${id}_url`).val("");
var count = $('.preview-box').length;
//10個めが消されたら投稿ボックスを表示
if (count == 9) {
$('.label-content').show();
}
setLabel(count);
if(id < 10){
$('.label-box').attr({id: `label-box--${id}`,for: `item_item_imgs_attributes_${id}_url`});
}
} else {
//投稿編集時
$(`#item_item_imgs_attributes_${id}__destroy`).prop('checked',true);
//10個めが消されたら投稿ボックスを表示
if (count == 9) {
$('.label-content').show();
}
setLabel();
//ラベルのidとforの値を変更
if(id < 10){
$('.label-box').attr({id: `label-box--${id}`,for: `item_item_imgs_attributes_${id}_url`});
}
}
});
});
// ここからは販売手数料、利益の計算(ページ遷移時にイベント発火)
$(function load(){
var data = $('#item_price').val(); // val()でフォームのvalueを取得(数値)
var profit = Math.round(data * 0.9) // 手数料計算を行う。dataにかけているのが0.9なのは単に引きたい手数料が10%のため。
var fee = (data - profit) // 入力した数値から計算結果(profit)を引く。それが手数料となる。
$('.right_bar').html(fee) // 手数料の表示。html()は追加ではなく、上書き。入力値が変わる度に表示も変わるようにする。
$('.right_bar').prepend('¥') // 手数料の前に¥マークを付けたいので
$('.right_bar_2').html(profit)
$('.right_bar_2').prepend('¥')
$('#price').val(profit) // 計算結果を格納用フォームに追加。もし、入力値を追加したいのなら、今回はdataを引数に持たせる。
if(profit == '') { // もし、計算結果が''なら表示も消す。
$('.right_bar_2').html('');
$('.right_bar').html('');
}
})
});
登録されていた画像の表示〜削除、新たな画像の投稿が可能となりました。当初はgem 'gon'というものを使用、コントローラーで定義した情報をjsで使えるようにし、画像のidを配列に入れ、update、destroyを行おうとしましたがうまくいかず、全てをまたゼロから実施しました。作り直した感じになったので非常に時間がかかってしまいました‥。
最終課題発表会まであと2日。
参考
・railsで投稿の編集、削除機能
・gonを使ったRailsとJavascriptの連携について
・11/16 【勉強77日目】 🍎Javascript/rails🍎商品編集画面とそのマイページ🍎
・jQueryで属性を設定するattrとpropの使い方
・画像の複数枚投稿と編集とプレビューと私