Railsãæçš¿ç»åãç·šéã»ãã¬ãã¥ãŒã»åé€ãã®ããæ¹ïŒjsãjQueryïŒ
ã¹ãã³ãµãŒãªã³ã¯
Â
ããã«ã¡ã¯ïŒ
ä»åã¯ãååã®ç¶ã
 Railsãç»åè€æ°æçš¿ã®ããæ¹ïŒjsãjQueryã§ãã©ãŒã äœæïŒ
ããæçš¿ç»åã®ãç·šéã»ãã¬ãã¥ãŒã»åé€ãã«ã€ããŠæžããŠãããŸããÂ
Â
Â
1.è€æ°ç»åã®æçš¿æ©èœãå®è£ ãâãããŸã§ãååãããŸããã
2.ç·šéæ©èœã®å®è£ ãâ次ã¯ããã§ãã
3.ãã¬ãã¥ãŒæ©èœãå®è£ ã
4.åé€ã®å®è£ ã
Â
Â
ã§ã¯æ¹ããŠïŒã€ç®ãç·šéæ©èœãããã¹ã¿ãŒãã§ãã
Â
Â
1ã€ã®æçš¿ã«å¯Ÿãè€æ°ç»åãæ·»ä»ããæ©èœã«å¿ èŠãªãfields_forã
ãã®ãfields_forãã¡ãœããå©çšã®éã«ãæžããaccepts_nested_attributes_forã
ãã®ãaccepts_nested_attributes_forãã¡ãœããã¯ã
paramsã®ââs_attritbutes:ãšããããŒã®äžã§ç¹å®ã®å€ãéãããšã§ã芪ã¢ãã«ã«çŽã¥ããåã¢ãã«ã®åé€ãç·šéïŒæŽæ°ïŒãè¡ããŸãã
Â
ãã®ãã€ã³ãããµãŸããŠé²ããŸãã
Â
Â
Â
Â
Â
â ã³ã³ãããŒã©ã§åºæ¬çãªåããšã¹ããã³ã°ãã©ã¡ãŒã¿ãè¿œå ã
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
before_action :set_product, except: [:index, :new, :create]
def update
if @product.update(product_params)
redirect_to root_path
else
render :edit
end
end
private
def product_params
params.require(:product).permit(:name, :price, images_attributes: [:src, :_destroy, :id])
end
def set_product
@product = Product.find(params[:id])
end
|
Â
14è¡ç®ã«å ã»ã©èª¬æãããã€ã³ããããs_attributesïŒãã«ç¶ãã
[ïŒsrc, ïŒ_destroy, ïŒid]ãã®ïŒã€ãè¿œå ãããŸããã
Â
ããã§èŠªã¢ãã«ïŒproductïŒã«åºã¥ããåã¢ãã«ïŒimagesïŒãšçŽä»ã
欲ããå€ãsrcã_destroy ãidããåã£ãŠããèšè¿°ã«ãªããŸããã
Â
ããã§åºãŠãããdestroy ããåããŠã¿ãŸããã
ããã¯ã
fields_forããéãããŠããããã®ããŒãæã£ãæ å ±ãé Œãã«
railsãåã¢ãã«ã®åé€ãè¡ã£ãŠãããã
 ãšããèšè¿°æ¹æ³ã®ããã§ãã
Â
Â
Â
â¡ topããŒãžã«editãã¿ã³ãäœæãã
Â
1 2 3 |
// çç¥
<%= link_to 'ç·šé', edit_product_path(product) %>
|
â¢ãã¥ãŒã«ãç·šéæ©èœçšã®ãã©ãŒã ãè¿œå ããŸãã
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<%= form_for @product do |f| %>
ååå<%= f.text_field :name %><br>
äŸ¡æ Œ<%= f.number_field :price %><br>
<div id="image-box">
<%= f.fields_for :images do |image| %>
<div data-index="<%= image.index %>" class="js-file_group">
<%= image.file_field :src, class: 'js-file' %><br>
<span class="js-remove">åé€</span>
</div>
<% if @product.persisted? %>
<%= image.check_box :_destroy, data:{ index: image.index }, class: 'hidden-destroy' %>
<% end %>
<% end %>
<% if @product.persisted? %>
<div data-index="<%= @product.images.count %>" class="js-file_group">
<%= file_field_tag :src, name: "product[images_attributes][#{@product.images.count}][src]", class: 'js-file' %>
<div class="js-remove">åé€</div>
</div>
<% end %>
</div>
<%= f.submit %>
<% end %>
|
 远èšããã®ã¯ã2ã€ã®if @product.persisted?
ã®éšåã§ãã
Â
ãã®ãpersistedïŒãã¯ã@productã®ãããªã€ã³ã¹ã¿ã³ã¹ãå©çšã§ããã¡ãœããã§
ããããŒã¿ããŒã¹ã«ä¿åæžã¿ãªãtrueãããã§ãªããã°falseãè¿ããŸãã
Â
Â
次ã¯jsãã¡ã€ã«ã«è¿œèšããŠãããŸãã
Â
ããããã®ç»åã®äžã«ããåé€ãã¿ã³ãæŒãããã§ãã¯ããã¯ã¹ã«ãã§ãã¯ãå ¥ãããã«ããŠãããã以äžã®éšåãè¿œèšã
Â
ã»ããŒãžèªã¿èŸŒã¿æã«ãçšæããé
åããæ¢ã«äœ¿ãããŠããindexãåãé€ãåŠç
ã»åé€ãã¿ã³ãæŒããéã«ã該åœindexãæ¯ããããã§ãã¯ããã¯ã¹ãžãã§ãã¯ãå
¥ããåŠçÂ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
~~~~~~~~~~~~~~~~~~~~~~(çç¥)~~~~~~~~~~~~~~~~~~~~~~~~
let fileIndex = [1,2,3,4,5,6,7,8,9,10];
// æ¢ã«äœ¿ãããŠããindexãé€å€
lastIndex = $('.js-file_group:last').data('index');
fileIndex.splice(0, lastIndex);
$('.hidden-destroy').hide();
~~~~~~~~~~~~~~~~~~~~~~(çç¥)~~~~~~~~~~~~~~~~~~~~~~~~
$('#image-box').on('click', '.js-remove', function() {
const targetIndex = $(this).parent().data('index')
// 該åœindexãæ¯ãããŠãããã§ãã¯ããã¯ã¹ãååŸãã
const hiddenCheck = $(`input[data-index="${targetIndex}"].hidden-destroy`);
// ãããã§ãã¯ããã¯ã¹ãååšããã°ãã§ãã¯ãå
¥ãã
if (hiddenCheck) hiddenCheck.prop('checked', true);
(çç¥)
});
|
Â
Â
ããã§ãç·šéæ©èœããå®è£ ã§ããŸããã
Â
Â
Â
次ã¯ïŒã€ç®ããã¬ãã¥ãŒæ©èœãå®è£ ã§ãã
Â
1.è€æ°ç»åã®æçš¿æ©èœãå®è£ ã
2.ç·šéæ©èœã®å®è£ ã
3.ãã¬ãã¥ãŒæ©èœãå®è£ ãâ次ã¯ããã§ãã
4.åé€ã®å®è£ ã
Â
Â
Â
â ãŸããã¥ãŒã«ããã¬ãã¥ãŒè¡šç€ºçšã®èŠçŽ ããè¿œå ã
Â
ã»imgã¿ã°ã®srcãæžãæããããšã§ãã¬ãã¥ãŒãå®è£
ã
ã»åé€æindexãé Œãã«ãã¬ãã¥ãŒãåé€ããããã
ãã«ã¹ã¿ã ããŒã¿ãšããŠæãããã
Â
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<%= form_for @product do |f| %>
ååå<%= f.text_field :name %><br>
äŸ¡æ Œ<%= f.number_field :price %><br>
<div id="image-box">
<div id="previews">
<% if @product.persisted? %>
<% @product.images.each_with_index do |image, i| %>
<%= image_tag image.src.url, data: { index: i }, width: "100", height: '100' %>
<% end %>
<% end %>
</div>
<%= f.fields_for :images do |image| %>
<div data-index="<%= image.index %>" class="js-file_group">
<%= image.file_field :src, class: 'js-file' %><br>
<span class="js-remove">åé€</span>
</div>
<% if @product.persisted? %>
<%= image.check_box :_destroy, data:{ index: image.index }, class: 'hidden-destroy' %>
<% end %>
<% end %>
<% if @product.persisted? %>
<div data-index="<%= @product.images.count %>" class="js-file_group">
<%= file_field_tag :src, name: "product[images_attributes][#{@product.images.count}][src]", class: 'js-file' %>
<div class="js-remove">åé€</div>
</div>
<% end %>
</div>
<%= f.submit %>
<% end %>
|
Â
Â
â¡jsã§ç»åãã¡ã€ã«ãè¿œå /å€æŽ/åé€ããæã«ãimgã¿ã°ãè¿œå /srcå€æŽ/åé€ã§ããããã«å®è£ ã
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
~~~~~~~~~~~~~~~~~~~~~~(çç¥)~~~~~~~~~~~~~~~~~~~~~~~~
// ãã¬ãã¥ãŒçšã®imgã¿ã°ãçæããé¢æ°
const buildImg = (index, url)=> {
const html = `<img data-index="${index}" src="${url}" width="100px" height="100px">`;
return html;
}
~~~~~~~~~~~~~~~~~~~~~~(çç¥)~~~~~~~~~~~~~~~~~~~~~~~~
$('#image-box').on('change', '.js-file', function(e) {
const targetIndex = $(this).parent().data('index');
// ãã¡ã€ã«ã®ãã©ãŠã¶äžã§ã®URLãååŸãã
const file = e.target.files[0];
const blobUrl = window.URL.createObjectURL(file);
// 該åœindexãæã€imgã¿ã°ãããã°ååŸããŠå€æ°imgã«å
¥ãã(ç»åå€æŽã®åŠç)
if (img = $(`img[data-index="${targetIndex}"]`)[0]) {
img.setAttribute('src', blobUrl);
} else { // æ°èŠç»åè¿œå ã®åŠç
$('#previews').append(buildImg(targetIndex, blobUrl));
// fileIndexã®å
é ã®æ°åã䜿ã£ãŠinputãäœã
$('#image-box').append(buildFileField(fileIndex[0]));
fileIndex.shift();
// æ«å°Ÿã®æ°ã«1足ããæ°ãè¿œå ãã
fileIndex.push(fileIndex[fileIndex.length - 1] + 1)
}
});
~~~~~~~~~~~~~~~~~~~~~~(çç¥)~~~~~~~~~~~~~~~~~~~~~~~~
$('#image-box').on('click', '.js-remove', function() {
// (çç¥)
$(`img[data-index="${targetIndex}"]`).remove();
}
|
Â
Â
 å®æããjsã³ãŒãã¯ãã¡ã
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
$(document).on('turbolinks:load', ()=> {
const buildFileField = (num)=> {
const html = `<div data-index="${num}" class="js-file_group">
<input class="js-file" type="file"
name="product[images_attributes][${num}][src]"
id="product_images_attributes_${num}_src"><br>
<div class="js-remove">åé€</div>
</div>`;
return html;
}
const buildImg = (index, url)=> {
const html = `<img data-index="${index}" src="${url}" width="100px" height="100px">`;
return html;
}
let fileIndex = [1,2,3,4,5,6,7,8,9,10];
lastIndex = $('.js-file_group:last').data('index');
fileIndex.splice(0, lastIndex);
$('.hidden-destroy').hide();
$('#image-box').on('change', '.js-file', function(e) {
const targetIndex = $(this).parent().data('index');
const file = e.target.files[0];
const blobUrl = window.URL.createObjectURL(file);
if (img = $(`img[data-index="${targetIndex}"]`)[0]) {
img.setAttribute('src', blobUrl);
} else {
$('#previews').append(buildImg(targetIndex, blobUrl));
$('#image-box').append(buildFileField(fileIndex[0]));
fileIndex.shift();
fileIndex.push(fileIndex[fileIndex.length - 1] + 1);
}
});
$('#image-box').on('click', '.js-remove', function() {
const targetIndex = $(this).parent().data('index');
const hiddenCheck = $(`input[data-index="${targetIndex}"].hidden-destroy`);
if (hiddenCheck) hiddenCheck.prop('checked', true);
$(this).parent().remove();
$(`img[data-index="${targetIndex}"]`).remove();
if ($('.js-file').length == 0) $('#image-box').append(buildFileField(fileIndex[0]));
});
});
|
Â
 以äžã§ããã¬ãã¥ãŒæ©èœãå®è£ å®äºã§ãã
Â
Â
Â
ãããããã®ãç»åè€æ°ææçš¿ãã¢ããªã®ãŽãŒã«ãèŠããŸããïŒ
æåŸã¯ãåé€æ©èœãã§ãã
Â
Â
1.è€æ°ç»åã®æçš¿æ©èœãå®è£ ã
2.ç·šéæ©èœã®å®è£ ã
3.ãã¬ãã¥ãŒæ©èœãå®è£ ã
4.åé€ã®å®è£ ãâ次ã¯ããã§ãã
Â
Â
ãããŸã§å®è£ ãããŠããã°ãåé€æ©èœã«ã€ããŠã¯ç¹å¥ãªå®è£ ã¯äœããããŸããã
Â
â ã³ã³ãããŒã©ã«ã¬ã³ãŒãåé€ã®èšè¿°ãè¿œå ã
1 2 3 4 |
def destroy
@product.destroy
redirect_to root_path
end
|
â¡ãã¥ãŒã«åé€ãã¿ã³ãè¿œå ã
1 2 |
//çç¥
<%= link_to 'åé€', product_path(product), method: :delete %>
|
 â¢productã¢ãã«ã«ã芪ã¢ãã«åé€ãããåã¢ãã«ããŸãšããŠåé€ããšãããªãã·ã§ã³ãè¿œå ã
1 |
has_many :images, dependent: :destroy
|
Â
以äžã§ãåé€æ©èœããš
ãã®ïŒãïŒèšäºã«ããããè€æ°ç»åæçš¿ã¢ããªãã®èšäºãçµäºããŸããïŒ
Â
åã¯ãã®æ©èœå®è£ ããã£ãšã§ããã«ã€ãã¢ãããŠãŸããïŒ
jsã³ãŒãã®èšè¿°ã¯ãé£ãããšæããŸãã
Â
ã§ãã人ã«èšãããã°ãäœãåãããªãã®ãåãããªãã
ãªããŠèšãããŠããŸãããã§ãããã
Â
ãã©ãã©ããªäººã ã£ãŠå§ãã¯ãäœãåãããªãç¶æ ãããå§ããŠããŸãã
ã ããå®å¿ããŠè¯ãã®ã ãšæããŸãã
Â
ãã®èšäºããåãããªããŸãŸè«ŠããŠããŸãããã
ãããªäººã®åœ¹ã«ç«ãŠã°å¹žãã§ãã
Â
Â
æåŸãŸã§èªãã§ãã ããããããšãããããŸãã
次åããããããé¡ãããŸããÂ
Â