I'm trying to update the ranking of room prices with jquery-sortable. I'm following the approach of this example https://gorails.com/episodes/sortable-drag-and-drop.
goal
The room_prices can be dragged and dropped and the goal is to update the rank accordingly.
Issue
The controller action sort is triggered everytime I move a price, but the new order/ranking seems not to be transfered correctly to the controller.
=> The index in my controller action is still the index the page started with instead of the index after the drag and drop action.
index
<div id="room-price-index" data-url="<%= sort_room_category_room_prices_path(#room_category) %>">
<% #room_prices.each do |price| %>
<%= link_to edit_room_price_path(price), id: dom_id(price) do %>
<div class="item"><%= price.name %></div>
<% end %>
<% end %>
</div>
app/assets/javascripts/room_price.js
document.addEventListener("turbolinks:load", function() {
$("#room-price-index").sortable({
update: function(e, ui) {
console.log(e)
console.log(ui)
console.log(this)
console.log($(this).sortable('serialize'));
Rails.ajax({
url: $(this).data("url"),
type: "PATCH",
data: $(this).sortable('serialize'),
});
}
});
});
output console.log(e)
=>
jQuery.Event {originalEvent: j…y.Event, type: "sortupdate", timeStamp: 7277.559999958612, jQuery112403961329860286955: true, isDefaultPrevented: ƒ, …}
altKey: false
bubbles: true
button: 0
buttons: 0
cancelable: true
clientX: 783
clientY: 365
ctrlKey: false
currentTarget: document
data: null
delegateTarget: document
detail: 1
eventPhase: 3
fromElement: null
handleObj: {type: "mouseup", origType: "mouseup", data: null, guid: 30, handler: ƒ, …}
isDefaultPrevented: ƒ returnFalse()
isTrigger: 3
jQuery112403961329860286955: true
metaKey: false
namespace: ""
offsetX: 523
offsetY: 39
originalEvent: jQuery.Event {originalEvent: MouseEvent, type: "mouseup", timeStamp: 7277.559999958612, jQuery112403961329860286955: true, isDefaultPrevented: ƒ, …}
pageX: 783
pageY: 581
relatedTarget: null
result: undefined
rnamespace: null
screenX: 783
screenY: 444
shiftKey: false
target: div#acco-price-index.ui-sortable
timeStamp: 7277.559999958612
toElement: div.room-price-info
type: "sortupdate"
view: Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
which: 1
__proto__: Object
output console.log(ui)
=>
{helper: null, placeholder: jQuery.fn.init(1), position: {…}, originalPosition: {…}, offset: {…}, …}
helper: null
item: jQuery.fn.init [div.card-room-prices.ui-sortable-handle, context: div.card-room-prices.ui-sortable-handle]
offset: {top: 539.4375, left: 222}
originalPosition: {top: 533.4375, left: 15}
placeholder: jQuery.fn.init [div.card-room-prices.ui-sortable-handle.ui-sortable-placeholder, selector: "", context: undefined]
position: {top: 391.4375, left: 16.609375}
sender: null
__proto__: Object
output console.log(this)
<div id="acco-price-index" data-url="/room_categories/10/room_prices/sort" class="ui-sortable">
<a id="room_price_43" href="/room_prices/43/edit" class="ui-sortable-handle">
</a><div class="card-room-prices ui-sortable-handle"><a id="room_price_43" href="/room_prices/43/edit">
<div class="room-price-info">
<div class="room-price-info-item">ppppppp</div>
<div class="room-price-info-item">€3.00</div>
<div class="room-price-info-item"> 31-01-2020 </div>
<div class="room-price-info-item"> 6-03-2020 </div>
</div>
</a><div class="card-room-prices-actions"><a id="room_price_43" href="/room_prices/43/edit">
<!-- <div class="card-room-prices-actions-item">
<p><i class="fas fa-edit"></i> Price details </p>
</div> -->
</a><a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/room_prices/43">
<div class="card-room-prices-actions-item">
<p><i class="fas fa-trash"></i> delete</p>
</div>
</a> </div>
</div>
<a id="room_price_41" href="/room_prices/41/edit" class="ui-sortable-handle">
</a><div class="card-room-prices ui-sortable-handle" style=""><a id="room_price_42" href="/room_prices/42/edit">
<div class="room-price-info">
<div class="room-price-info-item">fdsfsdfdsf</div>
<div class="room-price-info-item">€5.00</div>
<div class="room-price-info-item"> 15-12-2019 </div>
<div class="room-price-info-item"> 31-12-2019 </div>
</div>
</a><div class="card-room-prices-actions"><a id="room_price_42" href="/room_prices/42/edit">
<!-- <div class="card-room-prices-actions-item">
<p><i class="fas fa-edit"></i> Price details </p>
</div> -->
</a><a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/room_prices/42">
<div class="card-room-prices-actions-item">
<p><i class="fas fa-trash"></i> delete</p>
</div>
</a> </div>
</div><div class="card-room-prices ui-sortable-handle"><a id="room_price_41" href="/room_prices/41/edit">
<div class="room-price-info">
<div class="room-price-info-item">Rob II</div>
<div class="room-price-info-item">€5.00</div>
<div class="room-price-info-item"> 15-12-2019 </div>
<div class="room-price-info-item"> 31-12-2019 </div>
</div>
</a><div class="card-room-prices-actions"><a id="room_price_41" href="/room_prices/41/edit">
<!-- <div class="card-room-prices-actions-item">
<p><i class="fas fa-edit"></i> Price details </p>
</div> -->
</a><a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/room_prices/41">
<div class="card-room-prices-actions-item">
<p><i class="fas fa-trash"></i> delete</p>
</div>
</a> </div>
</div>
<a id="room_price_42" href="/room_prices/42/edit" class="ui-sortable-handle">
</a>
</div>
console.log($(this).sortable('serialize'));
room_price[]=43&room_price[]=41&room_price[]=42
controller
def sort
params[:room_price].each_with_index do |id, index|
binding.pry
# id => "43"
# index => "0"
# RoomPrice.where(id: id) => [#<RoomPrice:797987897 .....rank:1>]
RoomPrice.where(id: id).update_all(rank: index + 1)
binding.pry
# id => "43"
# index => "0"
# RoomPrice.where(id: id) => [#<RoomPrice:797987897 .....rank:1>]
end
skip_authorization
head :ok
end
Found the error, I thought I properly wrapped all my price items, but I accidentally also included some items that were not price items. Thanks all you guys for the help.
Related
all_request.js.erb
$(function () {
$( ".kanban-card, .sub-card, .kanban-card .sub-tickets" ).sortable({
connectWith: ".kanban-card",
cancel: ".main_ticket",
start: function (event, ui) {
},
change: function (event, ui) {
},
out: function (event, ui) {
},
over: function (event, ui) {
},
receive: function (event, ui) {
}
});
});
all_request.html.erb
<div class="d-flex justify-content-around">
<% #kanban_columns.each do |col| %>
<div class="box border" data-item="<%=col.id %>">
<h3 class="text-center"><%=col.name.humanize%></h3>
<hr>
<div class="kanban-card" style="height: 770px; overflow: auto;">
<% col.client_requests.each do |interview| %>
<div class="border bg-warning bg-opacity-50 m-2">
<div class="main_ticket card m-2 border border-danger" >
<div class="card-body">
<p class="card-text mb-0">#<%= interview.id %></p>
<h5 class="card-title"><%= interview.customer_name %></h5>
</div>
</div>
<div class="sub-tickets collapse" id="Req<%= interview.id %>">
<% interview.departments.each do |dept| %>
<% if interview.kanban_column.name == 'new_request' %>
<div class="card m-2 border border-dark" >
<div class="card-body">
<p class="mb-0">dept. <%=dept.name%></p>
<p class="mb-0">no of resources: <%=dept.no_of_resources%></p>
</div>
</div>
<% end %>
<% dept.developers.each do |dev| %>
<% if col.name == dev.developer_status %>
<div class="sub-card">
<div class="card m-2 border border-dark" data-item="<%=dev.id %>">
<p class="mb-0">dept. <%=dept.name%></p>
<p class="mb-0">developer. <%=dev.name%></p>
</div>
</div>
<% end %>
<% end %>
<%end%>
</div>
</div>
<%end%>
<!--if developers exist and main ticket is in new request-->
<% #client_requests.each do |interview| %>
<% #card_id = interview.customer_name+"_#{interview.id}"+"_#{col.id}" %>
<% #card_class = interview.customer_name+"_#{interview.id}" %>
<div class="border bg-warning bg-opacity-50 m-2 card-header main-card-div" >
#id : <%=interview.id%>
<%=interview.customer_name%>
<% interview.departments.each do |dept| %>
<% dept.developers.each do |dev| %>
<% if col.name == dev.developer_status %>
<div class="m-2 sub-card" id="<%= #card_id %>">
<div class="card m-2" data-item="<%= dev.id %>">
<div class="card-body">
#id : <%=dev.id%>
<p class="mb-0">dept. <%=dept.name%></p>
<p class="mb-0">developer. <%=dev.name%></p>
</div>
</div>
</div>
<% end %>
<%end%>
<%end %>
</div>
<%end%>
<!-- -->
</div>
</div>
<% end %>
</div>
In above code div with class-name kanban-card, sub-tickets, .sub-cards are sortable so I just want same class will be sortable using sortableJs.
You can see the demo here: https://drive.google.com/file/d/15E9gT0F91mUES8gfLm1tYxUqvDGrUAjM/view?usp=sharing
resources:
sortableJs: https://github.com/SortableJS/Sortable
I have the following datetimepicker, which work well and pass the params to the controller. But I would like to make sure that the user cannot select a end date that is before the start date.
<div class="row">
<div class="col-md-2" style="text-align: right;">
</div>
<div class="col-md-3">
<div class="input-group date" id="datetimepicker4" data-target-input="nearest" style="margin-bottom: 20px">
<%= form.text_field(:start, class: "form-control datetimepicker-input", data: {target:"#datetimepicker4"}, placeholder: "#{t :From}") %>
<div class="input-group-append" data-target="#datetimepicker4" data-toggle="datetimepicker">
<div class="input-group-text"><span class="fas fa-calendar-alt"></span></div>
</div>
</div>
</div>
<div class="col-md-2 col-md-offset-1" style="text-align: right;">
</div>
<div class="col-md-3">
<div class="input-group date" id="datetimepicker5" data-target-input="nearest" style="margin-bottom: 20px">
<%= form.text_field(:end, class: "form-control datetimepicker-input", data: {target:"#datetimepicker5"}, placeholder: "#{t :End_time_or_until}") %>
<div class="input-group-append" data-target="#datetimepicker5" data-toggle="datetimepicker">
<div class="input-group-text"><span class="fas fa-calendar-alt"></span></div>
</div>
</div>
</div>
<div class="col-md-1" style="margin-bottom: 20px; text-align: right">
<%= form.button "#{t :Refresh}", class: "btn btn-primary" %>
</div>
</div>
I have the following scripts:
<script>
$(".end_at").change(function (e) {
end_at = $(e.target).val();
start_at = $(".start_at").val();
$.ajax({
type: "POST",
url: "index",
data: {
start_at: start_at,
end_at: end_at,
},
success: function (data) {
$(".chart-container").html(data);
},
});
});
$(function() {
$('#datetimepicker4').datetimepicker({
viewMode: 'days',
dropdownParent: $("#modal-window")
});
$('#datetimepicker5').datetimepicker({
viewMode: 'days',
useCurrent: false,
dropdownParent: $("#modal-window")
});
$('#datetimepicker4').on('dp.change', function (e) {
$('#datetimepicker5').data('DateTimePicker').minDate(e.date);})
$('#datetimepicker5').on('dp.change', function (e) {
$('#datetimepicker4').data('DateTimePicker').maxDate(e.date);
});
});
</script>
Anybody knows what is missing please ? It does look like the form is not connecting to the scripts.
datetimepicker once initiated won't change configurations , so i think on change of start_date datetimepicker you need to destroy end_date's datetimepicker and then reinitiate with the new configs, set minDate in initiation of the end_date datetimepicker.
I have an hash in the format:
#meals = [
{
name: 'Roasted Chicken A La Ratatouille',
description: '',
tags: ['chicken'],
type: ['program'],
image_url: ''
},
{
name: 'Turkey Nuggets with Buffalo Cauliflower & Spinach',
description: '',
tags: ['turkey'],
type: ['program', 'veggies'],
image_url: ''
}
]
and I'd like to be able to unpack the meal type as class names for the element:
<% meals.shuffle.each do |meal| %>
<!-- Line in question -->
<div class="item col-3 p-2 a b <%= meal[:type].each {|t| t } %>">
<!-- End line in question -->
<div class="card">
<img class="card-img-top" src="<%= meal[:image_url] %>">
<div class="card-body">
<h5 class="card-title font-medium"><%= meal[:name] %></h5>
</div>
<div class="card-footer text-muted justify-content-center row">
<% meal[:tags].each do |tag| %>
<span style="margin: 2px;" class="badge bg-info-gradiant pointer"><%= tag %></span>
<% end -%>
</div>
</div>
</div>
<% end %>
But when the view renders, it displays as:
<div class="item col-3 p-2 a b ["program"]" style="position: absolute; left: 295px; top: 0px;">
<div class="card" style="height: 399px;">
...
</div>
</div>
Where program is displayed within the brackets. Is there a different way to accomplish this so that the values within the array are applied as class names?
You can use Array#join to explicitly convert the array of classes into a string of space-separated class names:
<div class="item col-3 p-2 a b <%= meal[:type].join(' ') %>">
How it works:
> meal[:type]
=> ["program", "veggies"]
> meal[:type].join(' ')
=> "program veggies"
Note that meal[:type].each does not do what you think it does. It calls the block for each element in the meal[:type] array with the expectation that the block performs a side effect (e.g. logging something or saving something), and then it returns the unmodified meal[:type] array. If you wanted to get a new array, you would have to use Array#map instead:
> meal[:type].each { |t| t.reverse }
=> ["program", "veggies"] # the block doesn’t affect the return value
> meal[:type].map { |t| t.reverse }
=> ["margorp", "seiggev"] # the block affects each returned element
I have 1 form which use dropzone gem, what i am trying to achieve is
i want to upload my image inside a form which have 1 to many relationship, i've been struggle to make it work with no luck.
here is my database structure :
apartement:
id
name
desc
unitplan:
id
unitplanphoto
apartement_id
an apartement has many unit plans, instead an unit plan only belong to 1 apartement,
<div class="container-fluid">
<div class="animated fadeIn">
<!--/.row-->
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<strong>Apartement Form</strong>
</div>
<%= form_for #apt, html: {class: "form-horizontal", :multipart=>true} do |f| %>
<div class="card-block">
<div class="form-group row">
<%= f.label 'Apartement Name', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.text_field :apt_name, class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Apartement Address', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.text_area :apt_address, rows: 5, cols: 46 , class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Latitude', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.text_field :apt_lat, class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Longtitude', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.text_field :apt_long, class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Thumbnail', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.file_field :thumbnail, accept: 'image/jpeg,image/gif,image/png' %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Appartement Description', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.text_area :apt_desc, rows: 5, cols: 46 , class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Developer', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= collection_select(:apt, :developer_id, #developers, :id, :devname, {:prompt => false}, class: "form-control") %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Area', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= collection_select(:apt, :area_id, #areas, :id, :area_desc, {:prompt => false}, class: "form-control") %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Status', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= f.select :apt_status, options_for_select(#apt_statuses.collect { |s| [s[0].humanize, s[0]] }, selected: #apt.apt_status), {} , class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Facility', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= collection_check_boxes(:apt, :facility_ids, #facilities, :id, :facility_desc) %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Point of Interest', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= collection_check_boxes(:apt, :poi_ids, #pois, :id, :poi_name) %>
</div>
</div>
<div class="form-group row">
<%= f.label 'Floor plan photos', class: 'col-md-4 form-control-label' %>
<div class="col-md-8">
<%= file_field_tag "images[]", type: :file, multiple: true %>
</div>
</div>
<% end %>
<div class="form-group row">
<div class="col-md-8">
<input id="unitplans_ids" name="unitplans_ids" type="hidden" value="">
<!--<input data-url="/photos" id="unitplans_upload" multiple="multiple" name="unitplans[]" type="file" ></input>-->
<!--<form action="/unitplans/create" id="dzCover" method="post" enctype="multipart/form-data" class="dropzone">-->
<%= form_for #unitplan, html: {class: "dropzone", :multipart=>true, id: "dzCover"} do |f| %>
<div id="actionsCover" class="row">
<div class="col-lg-8">
<label for="image">Unit plan photos : </label>
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button2">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
</span>
<button type="button" class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="button" class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
</div>
<div class="col-lg-5">
<!-- The global file processing state -->
<span class="fileupload-process">
<div id="total-progress" class="progress progress-striped active" style="display:none" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</span>
</div>
</div>
<div class="form-group">
<!-- HTML heavily inspired by http://blueimp.github.io/jQuery-File-Upload/ -->
<div class="table table-striped" class="files" id="previewsCover">
<div id="templateCover" class="file-row">
<!-- This is used as the file preview template -->
<div>
<span class="preview"><img data-dz-thumbnail /></span>
</div>
<div>
<p class="name" data-dz-name></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<p class="size" data-dz-size></p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</div>
<div>
<button type="button" class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button type="button" data-dz-remove class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
<button type="button" data-dz-remove class="btn btn-danger delete">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</div>
</div>
</div>
<br>
</div>
<!-- </form>-->
<% end %>
</div>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-sm btn-primary"><i class="fa fa-dot-circle-o"></i> Submit</button>
</div>
</div>
</div>
<!--/col-->
</div>
<!--/.row-->
</div>
</div>
my js file :
$( document ).ready(function()
{
var previewNode1 = document.querySelector("#templateCover");
console.log(previewNode1);
console.log(previewNode1.id);
previewNode1.id = "";
var previewTemplate1 = previewNode1.parentNode.innerHTML;
previewNode1.parentNode.removeChild(previewNode1);
var myDropzoneCover = new Dropzone("#dzCover", { // Make the whole body a dropzone
url: "/unitplans/create", // Set the url
thumbnailWidth: 80,
thumbnailHeight: 80,
parallelUploads: 20,
paramName: "unitplanphoto",
previewTemplate: previewTemplate1,
autoQueue: false, // Make sure the files aren't queued until manually added
previewsContainer: "#previewsCover", // Define the container to display the previews
clickable: ".fileinput-button2", // Define the element that should be used as click trigger to select files.
maxFilesize: 5,
acceptedFiles: ".png, .jpg, .jpeg", //is this correct? I got an error if im using this
init: function() {
this.on("success", function(file, response)
{
console.log(response);
file.serverId = response;
//alert(response);
});
this.on("removedfile", function(file)
{
//console.log(file);
//console.log(file.serverId);
if (!file.serverId)
{
return;
}
else
$.post("/unitplans/destroy?id=" + file.serverId);
});
this.options.previewaDropzone = false;
}
});
myDropzoneCover.on("addedfile", function(file) {
// Hookup the start button
file.previewElement.querySelector("#previewsCover .start").onclick = function() { myDropzoneCover.enqueueFile(file); };
//console.log(file);
});
// Update the total progress bar
myDropzoneCover.on("totaluploadprogress", function(progress) {
document.querySelector("#actionsCover #total-progress .progress-bar").style.width = progress + "%";
});
myDropzoneCover.on("sending", function(file) {
// Show the total progress bar when upload starts
document.querySelector("#actionsCover #total-progress").style.opacity = "1";
// And disable the start button
file.previewElement.querySelector("#previewsCover .start").setAttribute("disabled", "disabled");
//alert("sending");
});
// Hide the total progress bar when nothing's uploading anymore
myDropzoneCover.on("queuecomplete", function(progress) {
document.querySelector("#actionsCover #total-progress").style.opacity = "0";
//alert("complete");
});
// Setup the buttons for all transfers
// The "add files" button doesn't need to be setup because the config
// `clickable` has already been specified.
document.querySelector("#actionsCover .start").onclick = function() {
myDropzoneCover.enqueueFiles(myDropzoneCover.getFilesWithStatus(Dropzone.ADDED));
};
document.querySelector("#actionsCover .cancel").onclick = function() {
myDropzoneCover.removeAllFiles(true);
};
} );
controller file:
class UnitplansController < ApplicationController
def create
#unitplan = Unitplan.new(unitplan_params)
#respond_to do |format|
if #unitplan.save
render json: {message: "sukses", unitplanID: #unitplan.id}, status: 200
else
render json: { error: #unitplan.errors.full_messages.join(", ") }, status: 400
end
end
private
def unitplan_params
params.require(:unitplan).permit(:unitplanphoto)
end
end
somehow i can not make it to work,if i check dropzone request when i click submit, it always throw this error :
ActionController::ParameterMissing in UnitplansController#create
param is missing or the value is empty: unitplan
Your params appear to be missing the unitplan key that your unitplan_params method is looking for.
Try changing your paramNamevalue in your js file to this:
paramName: "unitplan[unitplanphoto]",
unitplan_params is acting as a whitelist for params to ensure you only pass approved params to your controller method, so you need to make sure your incoming params match the require and permit rules.
In my rails app, I have a view with some images.
<div class="item-images">
<div class="row">
<% #user_item.user_item_images.each_with_index do |image, index| %>
<% if (index + 1) % 5 == 0 %>
</div><div class="row">
<% end %>
<div class="image-container">
<a class="fancybox" rel="group" href="<%= image.picture.large.url %>"><img class="img-thumbnail img-circle" src="<%= image.picture.thumb.url %>" alt="" /></a>
<% if #user.eql?(current_user) && #user_item.primary_image_id != image.id %>
<button class="btn btn-xs btn-danger delete-image" data-id="<%= image.id %>" data-toggle="modal" data-target="#delete-image-modal">
<i class="fa fa-trash-o fa-lg"></i>
</button>
<% if #user_item.primary_image_id != image.id %>
<button class="btn btn-xs btn-info make-primary" data-id="<%= image.id %>" data-toggle="modal" data-target="#make-primary-modal">
<i class="fa fa-thumb-tack fa-lg"></i>
</button>
<% end %>
<% end %>
</div>
<% end %>
As you can see I have two buttons associated with each image. The corresponding modals:
<!-- Delete Image Modal -->
<div id="delete-image-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete-image" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 id="delete-image">Are you sure you want to delete this image?</h4>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Cancel</button>
<%= link_to 'Delete', '', method: :delete, :class => 'btn btn-danger' %>
</div>
</div>
</div>
</div>
<!-- Make Primary Modal -->
<div id="make-primary-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="make-primary" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 id="make-primary">Are you sure you want to make this the primary image for your item?</h4>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Cancel</button>
<%= link_to 'Make Primary', make_primary_path(????), :class => 'btn btn-info' %>
</div>
</div>
</div>
</div>
The make_primary_path brings us to a controller action which changes an attribute of the modal's associated image:
def make_primary
#user_item = UserItemImage.find(params[:user_item_image_id]).user_item
#user_item.update_attributes(primary_image_id: params[:id])
flash[:notice] = "primary image set"
redirect_to :back
end
The route for this action:
get 'user_item_images/:user_item_image_id/make_primary', to: 'user_item_images#make_primary', as: :make_primary
My problem is generating the link in the "Make Primary Modal". How do I get the data-id from the button and use that in the link_to helper?
One option would be to use ajax and tie it to a click event to that link. Something like:
$('.btn-info').on('click', function(e){
e.preventDefault()
var imageId = $('make-primary').data('id');
$.ajax({
type: 'GET', // either
url: '/user_items/make_primary',
data: {
image_id: imageId
}
}).done(function(ajax_success){
// use jquery to update whatever you need to on the page
// close modal
})
})
You can also use rails like:
<%= button_to '>Are you sure you want to make this the primary image for your item?', make_primary_path(image_id: image.id), :remote=>true, :method=>:post %>
I ended up doing this:
$('.make-primary').on('click', function() {
$('#make-primary-modal').find('a').attr('href', '/user_item_images/' + $(this).data('id') + '/make_primary');
});
and changing the link_to helper:
<%= link_to 'Make Primary', '', method: :post, :class => 'btn btn-info' %>