codeigniter jquery sortable save to database - jquery-ui

I am trying to save the order of a list of images to the database when using jquery sortable.
I feel i am very close, but cant get my head around the final details.
I am working with CI 2.1.3 and jquery-ui 1.10.3.
I have a dynamicaly generated list with an image:
<ul id="order">
<li id="item-1"><img src="abc.jpg" /></li>
<li id="item-2"><img src="def.jpg" /></li>
<li id="item-3"><img src="ghi.jpg" /></li>
</ul>
And the following Jquery:
<script>
$(document).ready(function() {
$( "#order" ).sortable({
opacity: 0.6,
cursor: 'move',
update: function(event, ui){
var order = $(this).sortable("serialize");
console.log(order);
$.ajax({
url: "http://localhost/gridrobin/home/save_order",
type: 'POST',
data: order,
success: function (data) {
$("#test").html(data);
}
});
}
});
});
</script>
This works fine and i can reorder my list. Now i want to save the new order to the database. I send the ajax post to the controller and it comes through. I checked with a var_dump.
//var_dump($_POST);
$items = $this->input->post('item');
$total_items = count($this->input->post('item'));
echo '<h3>Debugging</h3>';
echo "<p>Total items sent: $total_items</p>";
$this->rd_model->update_order($total_items, $items);
Then I send this data to my model:
for($item = 0; $item < $total_items; $item++ )
{
$data = array(
'id' => $items[$item],
'order' => $order = $item
);
$this->db->where('id', $data['id']);
$this->db->update('portfolio_items', $data);
echo '<br />'.$this->db->last_query();
}
And echo out the last db-query for debugging.
Now when i switch item 1 and item 2, i get a 500 internal error. When i switch them back, i receive the echo of the last query executed, which seems fine.
UPDATE `portfolio_items` SET `order` = 1 WHERE `id` = '1'
UPDATE `portfolio_items` SET `order` = 2 WHERE `id` = '2'
UPDATE `portfolio_items` SET `order` = 3 WHERE `id` = '3'
I dont quite understand why the database updates when th list is switched back to its orignial state, but not otherwise.
UPDATE
For people with the same problem, sakibmoon answer helped me a lot, but the main problem was a duplicate entry error, because apparently i had set the order table as a unique index...

The problem is within your data array in the model. Change that with this -
$data = array(
'id' => $items[$item],
'order' => $item+1
);
Also change the update line -
$this->db->update('portfolio_items', $data['order']);
UPDATE:
Couple of changes. Change the update to this-
$this->db->update('portfolio_items', array('order' => $data['order']));
The code is working now if I set $config['csrf_protection'] = FALSE in config.php. But you should set it to TRUE. I don't know how to make that work. All I know is that you have to send csrf_token with your ajax call. You should create a separate question for that. This question title means something entirely different.

Related

View rails record details in bootstrap modal on row click

I have been stuck on this problem for quite some time now and looked through several posts as well, however I cannot achieve exactly what I want for my Rails application. Essentially, I want to be able to click on a table row on my page and have a modal pop up which displays all the information for that specific record. Here are the scenarios which I have thought of/attempted partially:
Set the data-link attribute in table row with some JS as follows
HTML:
<tr data-link="<%= kid_path %>">
...
</tr>
JS:
$("tr[data-link]").dblclick(function() {
window.location = $(this).data("link")
})
This worked fine to open the show path page generated by the scaffold, but I was not able to modify it to work with a modal and have the same data for the kid in the modal.
Use data-id and JavaScript to load onto the modal
HTML:
<tr data-id="<%= kid.id %>">
...
</tr>
JS:
$(function () {
$('#showModal').modal({
keyboard: true,
backdrop: "static",
show: false,
}).on('show', function () {
});
$(".table-striped").find('tr[data-id]').on('click', function () {
debugger;
$('#showDetails').html($('<p>' + 'Kid ID: ' + $(this).data('id') + '<%= Kid.find(30).first_name %>' + '</p>'));
$('#showModal').modal('show');
});
});
In this approach I am able to load the modal on row click and am able to access the Kid ID, however I cannot move further to access other attributes of the record. For example, I want to set #Kid = kid.find(id) using JS where id would be the extracted ID from the row. And then, I want to be able to write the generic modal template which displays other elements (ex. kid.first_name, kid.last_name, etc).
I am totally stuck and cannot find any approach that helps to accomplish my goal. Any help is appreciated, thank you.
You need to ajax call record attributes because the line Kid.find(30).first_name doesn't exist at the time page loaded.
Try this:
KidsController
def show
kid = Kid.find(params[:id])
respond_to do |format|
format.html { // Usually show.html.erb }
format.json do
# Return kid as json object
result = {
first_name: kid.first_name,
last_name: kid.last_name
}
# If you want to return the whole kid record attributes in json: result = kid.attributes.to_json
render json: result
end
end
end
Try /kid/[:id].json to verify that you are not getting UnknownFormat error.
JS
$(".table-striped").find('tr[data-id]').on('click', function () {
var kid_id = $(this).data('id');
$.getJSON("/kid/" + kid_id, function(data) {
// Render the modal body here
// first_name = data.first_name, last_name = data.last_name etc
$('#showDetails').html($('<p>'+ data.first_name + '</p>'));
$('#showModal').modal('show');
});
})
If you have setup correct route for Kid model then these are what you needed.
UPDATED: I made a typo in the result hash. FIXED

React.js - Rails: Set State

I have two components: App and Registration Form
The form has two inputs: Name and Last name
Looking at the App state in dev. tools I see length: undefined and name: "name entered". I'm not getting any errors but I'm missing the last name.
This is only happening in Rails. I tried the same code in a non rails environment and it works fine. I'm using this gem for React: gem 'react-rails', '~> 1.5.0' and running Rails 4.2.4
var App = React.createClass({
getInitialState : function(){
return {
registrations: {}
}
},
addRegistration : function(registration){
// create unique id
var timestamp = (new Date()).getTime();
// update state
this.state.registrations['registration-' + timestamp] = registration;
//set the state
this.setState({ registrations : this.state.registrations });
},
render : function(){
return (
<RegistrationForm addRegistration={this.addRegistration}/>
)
}
});
var RegistrationForm = React.createClass({
createRegistration : function(event){
// prevent default
event.preventDefault();
// take data from form and create object
var registration = {
name : this.refs.name.value,
lastname : this.refs.lastname.value
}
// Add registration to App Object
this.props.addRegistration(registration);
this.refs.registrationForm.reset();
//console.log(registration);
},
render : function(){
return (
<div className="col-sm-12">
<form action="" className="form" ref="registrationForm" onSubmit={this.createRegistration}>
<div className="form-group">
<label >Name</label>
<input className="form-control" ref="name"/>
</div>
<div className="form-group">
<label >Last Name</label>
<input className="form-control" ref="lastname"/>
</div>
<div>
<button className="btn btn-primary">Submit</button>
</div>
</form>
</div>
)
}
});
App = React.createFactory(App)
What I'm trying to do is to give each registration a unique id number based on the time stamp.
When I console log the following:
addRegistration : function(registration){
// create unique id
var timestamp = (new Date()).getTime();
// update state
this.state.registrations['registration-' + timestamp] = registration;
//set the state
this.setState({ registrations : this.state.registrations });
},
I can see a registration object the way I want it. I can add as many unique registrations to the App state but each registration has length: undefined, name: "name" , but it's missing the last name.
If I change the set state to this:
this.setState({ registrations : registration });
This gives me a single registration with name and last name but it doesn't add multiple registrations. It only creates one registration which gets update every time I submit the add registration form.
this.state.registrations['registration-' + timestamp] = registration;
You seem to be mutating the state directly, based on the React Docs https://facebook.github.io/react/docs/component-api.html
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but
creates a pending state transition. Accessing this.state after calling
this method can potentially return the existing value.
There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
setState() will always trigger a
re-render unless conditional rendering logic is implemented in
shouldComponentUpdate(). If mutable objects are being used and the
logic cannot be implemented in shouldComponentUpdate(), calling
setState() only when the new state differs from the previous state
will avoid unnecessary re-renders.
Try cloning the current state then use that as the argument.
// if array
var clonedRegistration = this.state.registrations.slice();
clonedRegistration['registration-' + timestamp] = registration;
this.setState({registrations: clonedRegistration})
or
this.setState({registrations: {['registration-'+ timestamp]: registration} });
I think the answer Road put was close.
First set your initial state to an array.
getInitialState: function(){
return { registrations: []}
}
your addRegistration function
addRegistration : function(registration){
I think this is what you're missing:
//getting current state
var oldRegistrations = this.state.registrations;
Otherwise I believe you're updating the same thing over and over, instead of adding a new registration object. Then push your registration. You should set the timestamp
// update state
oldRegistrations.push(registration);
var registrations = oldRegistrations;
//set the state
this.setState({ registrations : registrations });
},
I would advise creating the id somewhere in here since you're not using an actual ajax call to a rails db:
var registration = {
name : this.refs.name.value,
lastname : this.refs.lastname.value
id: (new Date()).getTime();
}
I'm not sure I understand your question regarding your form values or if you were having trouble with them. But if you were I think doing something like this may help:
<input type='text' className='form-control'
placeholder='Name' name='name'
value={this.state.name} onChange={this.handleChange} >
</input>
<input type='text' className='form-control'
placeholder='Last Name' name='last name'
value={this.state.last_name} onChange={this.handleChange} >
</input>
Then implement a handleChange function within the same component to constantly handle the form's values onChange. That should look like this:
handleChange: function(e) {
var name = e.target.name;
var obj = {};
obj[name] = e.target.value;
this.setState(obj);
}
Hope this helps,

Rails check_box_tag - get all the values checked in view(haml) file

So I have multiple checkboxes on my page. I collect all of them like shown in the code below. I would like to access the values of the array before passing it on to the controller
= check_box_tag "names[]", ob.name, false, class: 'cbx'
I am able to pass them with my older code
%fieldset.actions
= form.submit "Upgrade", :class => 'button'
Logs:
Processing by SomeController#create as HTML Parameters:
{"utf8"=>"✓",
"names"=>["ron", "jacob"], "commit"=>"NameButton"}
Ok. So i would like to access all values in my haml files. Is there a way before i submit my form, I can access which checkboxes are selected.
I would like to pass the names[] to my controller as a parameter.
=link_to script_name1, { :action => 'create', :names => 'dontknowhowtopassnames[]' }, :method => :post
Rails version - 3.2.17
You can do that using Javascript.
The exact implementation depends on what exactly you want to do with those values, but you could, for example, use the change event to track and maintain an array of all checked values :
/*
* should properly init this array if some of your checkboxes are already
* checked when the form is loaded
*/
var names = [];
$(document).ready(function() {
$(document).on('change', '.cbx', function() {
var name = $(this).val();
var idx = names.indexOf(name);
if ($(this).prop('checked') && idx === -1) {
names.push(name);
} else if (!($(this).prop('checked')) && idx >= 0) {
names.splice(idx, 1);
}
});
});
Updated with complementary answer:
To submit a form with a link instead of a button:
In your view, replace
%fieldset.actions
= form.submit "Upgrade", :class => 'button'
with
= link_to "Submit", "#", :class => 'submit_link'
Then in your Javascript, add the following inside the $(document).ready body:
$(document).on('click', 'a.submit_link', function(e) {
$(this).closest('form').submit();
e.preventDefault();
});
And that's it. You're making your life very complicated by trying to serialize the form data on your own while the form's submit() method can take care of it for you. You can serialize data on your own, and it's sometimes useful, for instance when dealing with AJAX, but in your case the default submit() is perfect for the job.

Is it possible to submit a hidden field and control its value with x-editable?

inside the document of X-editable, we can create a new record, but how to edit an existing record, and post its name and email fields as well as its id =1(this id not changed) to the backend?
<table>
<thead><th>id</th><th>name</th><td>email</th></thead>
<tbody>
<tr><td><span>1</span></td><td><span class='myeditable'>name</span></td><td><span class='myeditable'>email#example.com</span></td></tr>
</tbody>
</table>
$('.myeditable').editable({
type: input,
url: '/edituser'
});
$('#save-btn').click(function() {
$('.myeditable').editable('submit', {
url: '/edituser',
ajaxOptions: {
dataType: 'json' //assuming json response
},
success: function(data, config) {
if(data && data.id) { //record created, response like {"id": 2}
},
error: function(errors) {
}
});
});
I used Angular-xeditable to do this, but the idea is the same I think.
I added a hidden span to my table and gave it an e-name. ng-show sets display:none, which I think is just what you need to do as well.
<span
editable-text="journalEntry._id"
e-name="_id"
e-form="rowform"
ng-show="false">
</span>
I used Angular-xeditable also, but had to change Michael's code because the hidden field appeared (I wanted it to remain hidden) when I edited the row.
Therefore I had to insert
e-class="hidden"
So in the end I had:
<span
e-class="hidden"
editable-text="employee.key"
e-name="key"
e-form="rowform"
ng-show="false">
</span>
To post a hidden field, you could try to modify your
url: '/edituser'
to
url: '/edituser?hidden-name1=hidden-value1&hidden-name2=hidden-value2' and so on...

Update progress bar in rails using jQuery

I'm a complete novice in JavaScript/jQuery and I believe it's a very simple question; however I'm not being able to accomplish it.
I have an asynchronous task being performed (by sidekiq) and it's progress is available by a method from the model (percentage_complete) that retrieves its progress from Redis.
I want to display a progress bar in model's show view, and I want it to update every x seconds using AJAX.
The progress bar is being displayed like this on the show.html.erb file:
<div class="progress">
<div class="bar" style="width: <%= #model.percentage_complete %>%;"></div>
</div>
How can I set a jQuery script to update this attribute asynchronously?
EDIT
I also have a a :status attribute which is set do "done" when the task is complete. I would like to stop updating when that happens.
By reading my question it appears that I haven't tried nothing and just want someone to write the code for me. Let me add some comments:
I know I should use setInterval to update the attribute every "x" seconds
I know I should use $('.progress .bar').width(<%= #model.percentage_complete %>%) to set the new percentage
However, since I'm not familiar to jQuery and JavaScript, specially in Rails, I'm not sure if this script should be loaded in a view, or if it should be a view itself.
I solved it by creating an action to retrieve the status
# GET /status/1.json
def status
#batch = Batch.find(params[:id])
respond_to do |format|
format.json
end
end
and using the following JavaScript:
<script type="text/javascript">
function progress(){
var progress = setInterval(function() {
var $bar = $('.bar');
var $pct = $('#pct');
$.get("<%= #batch.id %>/status.json", function(data){
if (data.status == "done") {
location.reload();
} else {
$bar.width(data.progress+"%");
$pct.text(data.progress+"%");
}
});
}, 800);
}
$(document).ready(function() {
$.get("<%= #batch.id %>/status.json", function(data) {
if (data.status == "processing") {
progress();
}
});
});
</script>

Resources