Skip to content

Commit 50501e2

Browse files
Mattia RoccobertonMattia Roccoberton
authored andcommitted
New feature: inline editing
1 parent 9873fd1 commit 50501e2

4 files changed

Lines changed: 146 additions & 9 deletions

File tree

README.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Features:
66

77
- set conditional checks on fields
88
- trigger some actions on other fields
9+
- inline field editing
910
- create links to load some content in a dialog
1011

1112
The easiest way to show how this plugin works is looking the examples [below](#examples-of-dynamic-fields).
@@ -39,7 +40,9 @@ Options are passed to fields using *input_html* parameter as *data* attributes:
3940
- **data-function**: check the return value of a custom function
4041
- **data-arg**: argument passed to the custom set function (as array of strings)
4142

42-
## Examples of dynamic fields
43+
## Examples
44+
45+
### Dynamic fields examples
4346

4447
- A checkbox that hides other fields if false (ex. model *Article*):
4548

@@ -97,7 +100,45 @@ function on_change_category( el ) {
97100
}
98101
```
99102

100-
## Example to open a dialog
103+
### Inline editing examples
104+
105+
- Prepare a custom member action to save data, an *update* helper function is available (third parameter is optional, allow to filter using strong parameters):
106+
107+
```rb
108+
member_action :save, method: [:post] do
109+
render ActiveAdmin::DynamicFields::update( resource, params )
110+
# render ActiveAdmin::DynamicFields::update( resource, params, [:published] )
111+
# render ActiveAdmin::DynamicFields::update( resource, params, Article::permit_params )
112+
end
113+
```
114+
115+
- In *index* config:
116+
117+
```rb
118+
# Edit a string:
119+
column :title do |row|
120+
div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
121+
end
122+
# Edit a boolean:
123+
column :published do |row|
124+
status_tag row.published, ActiveAdmin::DynamicFields::edit_boolean( :published, save_admin_article_path( row.id ), row.published )
125+
end
126+
# Edit a select ([''] allow to have a blank value):
127+
column :author do |row|
128+
select ActiveAdmin::DynamicFields::edit_select( :author_id, save_admin_article_path( row.id ) ) do
129+
options_for_select( [''] + Author.pluck( :name, :id ), row.author_id )
130+
end
131+
end
132+
```
133+
134+
- In *show* config (less useful):
135+
```rb
136+
row :title do |row|
137+
div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
138+
end
139+
```
140+
141+
### Dialog example
101142

102143
Example with 2 models: *Author* and *Article*
103144

app/assets/javascripts/activeadmin/dynamic_fields.js

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,61 @@ function dfSetValue( el, val ) {
9999
el.trigger( 'change' );
100100
}
101101

102+
// Inline update - must be called binded on the editing element
103+
function dfUpdateField() {
104+
if( $(this).data( 'loading' ) != '1' ) {
105+
$(this).data( 'loading', '1' );
106+
var _this = $(this);
107+
var type = $(this).data( 'field-type' );
108+
var new_value;
109+
if( type == 'boolean' ) new_value = !$(this).data( 'field-value' );
110+
else if( type == 'select' ) new_value = $(this).val();
111+
else new_value = $(this).text();
112+
var data = {};
113+
data[$(this).data('field')] = new_value;
114+
$.ajax({
115+
context: _this,
116+
data: { data: data },
117+
method: 'POST',
118+
url: $(this).data( 'save-url' ),
119+
complete: function( req, status ) {
120+
$(this).data( 'loading', '0' );
121+
},
122+
success: function( data, status, req ) {
123+
if( data.status == 'error' ) {
124+
if( $(this).data( 'show-errors' ) ) {
125+
var result = '';
126+
var message = data.message;
127+
for( var key in message ) {
128+
if( typeof( message[key] ) === 'object' ) {
129+
if( result ) result += ' - ';
130+
result += key + ': ' + message[key].join( '; ' );
131+
}
132+
}
133+
if( result ) alert( result );
134+
}
135+
}
136+
else {
137+
$(this).data( 'field-value', new_value );
138+
if( $(this).data('content') ) {
139+
var old_text = $(this).text();
140+
var old_class = $(this).attr( 'class' );
141+
var content = $($(this).data('content'));
142+
$(this).text( content.text() );
143+
$(this).attr( 'class', content.attr( 'class' ) );
144+
content.text( old_text );
145+
content.attr( 'class', old_class );
146+
$(this).data( 'content', content );
147+
}
148+
}
149+
},
150+
// error: function( req, status, error ) {
151+
// // if( $(this).data( 'show-errors' ) && req.responseJSON.message ) { }
152+
// },
153+
});
154+
}
155+
}
156+
102157
// Init
103158
$(document).ready( function() {
104159
// Setup dynamic fields
@@ -124,13 +179,30 @@ $(document).ready( function() {
124179
if( $('#df-dialog').length == 0 ) $('body').append( '<div id="df-dialog"></div>' );
125180
var title = $(this).attr( 'title' );
126181
$.ajax({
127-
url: $(this).attr( 'href' )
128-
}).done( function( result ) {
129-
if( title ) $('#df-dialog').attr( 'title', title );
130-
$('#df-dialog').html( result );
131-
$('#df-dialog').dialog({ modal: true });
132-
$('#df-dialog').data( 'loading', '0' );
182+
url: $(this).attr( 'href' ),
183+
complete: function( req, status ) {
184+
$('#df-dialog').data( 'loading', '0' );
185+
},
186+
success: function( data, status, req ) {
187+
if( title ) $('#df-dialog').attr( 'title', title );
188+
$('#df-dialog').html( data );
189+
$('#df-dialog').dialog({ modal: true });
190+
},
133191
});
134192
}
135193
});
194+
// Inline editing
195+
$('[data-field][data-field-type="boolean"][data-save-url]').each( function() {
196+
$(this).on( 'click', $.proxy( dfUpdateField, $(this) ) );
197+
});
198+
$('[data-field][data-field-type="string"][data-save-url]' ).each( function() {
199+
$(this).data( 'field-value', $(this).text() );
200+
var fnUpdate = $.proxy( dfUpdateField, $(this) );
201+
$(this).on( 'blur', function() {
202+
if( $(this).data( 'field-value' ) != $(this).text() ) fnUpdate();
203+
});
204+
});
205+
$('[data-field][data-field-type="select"][data-save-url]').each( function() {
206+
$(this).on( 'change', $.proxy( dfUpdateField, $(this) ) );
207+
});
136208
});

lib/activeadmin/dynamic_fields/engine.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,29 @@ module DynamicFields
55
class Engine < ::Rails::Engine
66
engine_name 'activeadmin_dynamic_fields'
77
end
8+
9+
def self.edit_boolean( field, url, value )
10+
{ 'data-field': field, 'data-field-type': 'boolean', 'data-field-value': value, 'data-content': "<span class=\"status_tag changed\">#{value ? 'no' : 'yes'}</span>", 'data-save-url': url, 'data-show-errors': '1' }
11+
end
12+
13+
def self.edit_select( field, url )
14+
{ 'data-field': field, 'data-field-type': 'select', 'data-save-url': url, 'data-show-errors': '1' }
15+
end
16+
17+
def self.edit_string( field, url )
18+
{ contenteditable: true, 'data-field': field, 'data-field-type': 'string', 'data-save-url': url, 'data-show-errors': '1' }
19+
end
20+
21+
def self.update( resource, params, permit_params = nil )
22+
if params[:data]
23+
if resource.update( permit_params ? params[:data].permit( permit_params ) : params[:data].permit! )
24+
{ json: { status: 'ok' } }
25+
else
26+
{ json: { status: 'error', message: resource.errors } }
27+
end
28+
else
29+
{ json: { status: 'error', message: 'No data' }, status: 400 }
30+
end
31+
end
832
end
933
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module ActiveAdmin
22
module DynamicFields
3-
VERSION = '0.1.5'
3+
VERSION = '0.2.0'
44
end
55
end

0 commit comments

Comments
 (0)