netzke-basepack 0.7.7 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +15 -10
- data/{CHANGELOG.rdoc → CHANGELOG.md} +146 -110
- data/LICENSE +7 -1
- data/README.md +47 -56
- data/Rakefile +5 -5
- data/config/before-travis.sh +10 -0
- data/javascripts/basepack.js +0 -130
- data/javascripts/netzkeremotecombo.js +59 -0
- data/lib/netzke/basepack.rb +9 -14
- data/lib/netzke/basepack/accordion.rb +45 -0
- data/lib/netzke/basepack/active_record.rb +12 -0
- data/lib/netzke/basepack/active_record/relation_extensions.rb +27 -0
- data/lib/netzke/basepack/columns.rb +309 -0
- data/lib/netzke/basepack/data_accessor.rb +22 -12
- data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +75 -11
- data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +154 -49
- data/lib/netzke/basepack/fields.rb +162 -0
- data/lib/netzke/basepack/form.rb +136 -0
- data/lib/netzke/basepack/{form_panel → form}/javascripts/comma_list_cbg.js +0 -1
- data/lib/netzke/basepack/{form_panel/javascripts/form_panel.js → form/javascripts/form.js} +20 -26
- data/lib/netzke/basepack/{form_panel → form}/javascripts/n_radio_group.js +0 -1
- data/lib/netzke/basepack/{form_panel → form}/javascripts/readonly_mode.js +0 -0
- data/lib/netzke/basepack/form/services.rb +115 -0
- data/lib/netzke/basepack/{form_panel → form}/stylesheets/readonly_mode.css +0 -0
- data/lib/netzke/basepack/grid.rb +355 -0
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/advanced_search.js +1 -1
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/check_column_fix.js +0 -0
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/edit_in_form.js +3 -3
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/event_handling.js +5 -2
- data/lib/netzke/basepack/{grid_panel/javascripts/grid_panel.js → grid/javascripts/grid.js} +120 -132
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/misc.js +0 -0
- data/lib/netzke/basepack/grid/services.rb +216 -0
- data/lib/netzke/basepack/item_persistence.rb +44 -0
- data/lib/netzke/basepack/item_persistence/events_plugin.rb +47 -0
- data/lib/netzke/basepack/{paging_form_panel.rb → paging_form.rb} +24 -30
- data/lib/netzke/basepack/{paging_form_panel/javascripts/paging_form_panel.js → paging_form/javascripts/paging_form.js} +2 -4
- data/lib/netzke/basepack/query_builder.rb +44 -73
- data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +16 -2
- data/lib/netzke/basepack/record_form_window.rb +67 -0
- data/lib/netzke/basepack/search_panel.rb +22 -24
- data/lib/netzke/basepack/search_panel/javascripts/condition_field.js +2 -2
- data/lib/netzke/basepack/search_window.rb +47 -53
- data/lib/netzke/basepack/simple_app.rb +10 -13
- data/lib/netzke/basepack/simple_app/javascripts/simple_app.js +2 -8
- data/lib/netzke/basepack/tab_panel.rb +5 -4
- data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +5 -5
- data/lib/netzke/basepack/version.rb +2 -2
- data/lib/netzke/basepack/viewport.rb +16 -0
- data/lib/netzke/basepack/window.rb +27 -18
- data/lib/netzke/basepack/window/javascripts/window.js +7 -1
- data/lib/netzke/basepack/wrap_lazy_loaded.rb +18 -18
- data/locales/en.yml +40 -24
- data/netzke-basepack.gemspec +51 -82
- data/stylesheets/basepack.css +0 -41
- data/test/basepack_test_app/Gemfile +9 -46
- data/test/basepack_test_app/Gemfile.lock +61 -96
- data/test/basepack_test_app/app/components/author_form.rb +8 -5
- data/test/basepack_test_app/app/components/author_grid.rb +2 -2
- data/test/basepack_test_app/app/components/book_form.rb +34 -31
- data/test/basepack_test_app/app/components/book_form_with_defaults.rb +6 -7
- data/test/basepack_test_app/app/components/book_form_with_file_upload.rb +10 -0
- data/test/basepack_test_app/app/components/book_form_with_nested_attributes.rb +5 -6
- data/test/basepack_test_app/app/components/book_grid.rb +19 -8
- data/test/basepack_test_app/app/components/book_grid_filtering.rb +4 -7
- data/test/basepack_test_app/app/components/book_grid_loader.rb +28 -15
- data/test/basepack_test_app/app/components/book_grid_with_custom_columns.rb +45 -21
- data/test/basepack_test_app/app/components/book_grid_with_default_values.rb +26 -8
- data/test/basepack_test_app/app/components/book_grid_with_excluded_columns.rb +11 -0
- data/test/basepack_test_app/app/components/book_grid_with_extra_feedback.rb +2 -2
- data/test/basepack_test_app/app/components/book_grid_with_extra_filters.rb +7 -6
- data/test/basepack_test_app/app/components/book_grid_with_mass_assignment_security.rb +9 -0
- data/test/basepack_test_app/app/components/book_grid_with_nested_attributes.rb +9 -9
- data/test/basepack_test_app/app/components/book_grid_with_overridden_columns.rb +5 -3
- data/test/basepack_test_app/app/components/book_grid_with_paging.rb +6 -8
- data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +6 -4
- data/test/basepack_test_app/app/components/book_grid_with_scope.rb +6 -0
- data/test/basepack_test_app/app/components/book_grid_with_scoped_authors.rb +10 -7
- data/test/basepack_test_app/app/components/book_grid_with_virtual_attributes.rb +21 -13
- data/test/basepack_test_app/app/components/book_paging_form.rb +21 -0
- data/test/basepack_test_app/app/components/book_query_builder.rb +7 -6
- data/test/basepack_test_app/app/components/book_with_custom_primary_key_grid.rb +6 -7
- data/test/basepack_test_app/app/components/books_bound_to_author.rb +9 -7
- data/test/basepack_test_app/app/components/border_layout_panel_with_persistence.rb +12 -0
- data/test/basepack_test_app/app/components/double_book_grid.rb +19 -14
- data/test/basepack_test_app/app/components/form_without_model.rb +15 -16
- data/test/basepack_test_app/app/components/grid_with_initial_sorting.rb +7 -0
- data/test/basepack_test_app/app/components/grid_with_inline_data.rb +7 -0
- data/test/basepack_test_app/app/components/paging_form_with_search.rb +2 -2
- data/test/basepack_test_app/app/components/panel_with_persistent_regions.rb +35 -0
- data/test/basepack_test_app/app/components/query_builder.rb +7 -0
- data/test/basepack_test_app/app/components/simple_panel.rb +16 -11
- data/test/basepack_test_app/app/components/simple_window.rb +7 -6
- data/test/basepack_test_app/app/components/some_accordion.rb +18 -0
- data/test/basepack_test_app/app/components/some_auth_app.rb +5 -5
- data/test/basepack_test_app/app/components/some_border_layout.rb +20 -20
- data/test/basepack_test_app/app/components/some_search_panel.rb +6 -0
- data/test/basepack_test_app/app/components/some_simple_app.rb +30 -16
- data/test/basepack_test_app/app/components/some_tab_panel.rb +18 -15
- data/test/basepack_test_app/app/components/user_form.rb +18 -16
- data/test/basepack_test_app/app/components/user_form_with_default_fields.rb +5 -6
- data/test/basepack_test_app/app/components/user_grid.rb +11 -6
- data/test/basepack_test_app/app/components/user_grid_with_customized_form_fields.rb +5 -3
- data/test/basepack_test_app/app/components/window_component_loader.rb +25 -21
- data/test/basepack_test_app/app/models/address.rb +0 -26
- data/test/basepack_test_app/app/models/author.rb +0 -31
- data/test/basepack_test_app/app/models/book.rb +1 -42
- data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +1 -23
- data/test/basepack_test_app/app/models/role.rb +0 -21
- data/test/basepack_test_app/app/models/user.rb +0 -24
- data/test/basepack_test_app/app/views/layouts/components.html.erb +1 -1
- data/test/basepack_test_app/config/application.rb +1 -1
- data/test/basepack_test_app/config/database.yml.travis +2 -6
- data/test/basepack_test_app/config/initializers/netzke.rb +1 -6
- data/test/basepack_test_app/db/schema.rb +14 -14
- data/test/basepack_test_app/features/accordion_panel.feature +2 -2
- data/test/basepack_test_app/features/form_panel.feature +7 -7
- data/test/basepack_test_app/features/grid_panel.feature +93 -39
- data/test/basepack_test_app/features/grid_panel_with_custom_primary_key.feature +2 -1
- data/test/basepack_test_app/features/grid_sorting.feature +30 -6
- data/test/basepack_test_app/features/paging_form_panel.feature +7 -7
- data/test/basepack_test_app/features/persistent_regions.feature +30 -0
- data/test/basepack_test_app/features/search_in_grid.feature +5 -5
- data/test/basepack_test_app/features/simple_app.feature +6 -7
- data/test/basepack_test_app/features/step_definitions/form_panel_steps.rb +1 -1
- data/test/basepack_test_app/features/step_definitions/generic_steps.rb +109 -4
- data/test/basepack_test_app/features/step_definitions/grid_panel_steps.rb +8 -10
- data/test/basepack_test_app/features/step_definitions/window_steps.rb +27 -0
- data/test/basepack_test_app/features/tab_panel.feature +1 -1
- data/test/basepack_test_app/features/window.feature +17 -0
- data/test/unit/accordion_panel_test.rb +2 -2
- data/test/unit/grid_panel_test.rb +4 -4
- metadata +57 -83
- data/TODO.rdoc +0 -8
- data/lib/generators/netzke/basepack_generator.rb +0 -10
- data/lib/generators/netzke/templates/assets/ts-checkbox.gif +0 -0
- data/lib/generators/netzke/templates/create_netzke_field_lists.rb +0 -18
- data/lib/netzke/active_record.rb +0 -20
- data/lib/netzke/active_record/attributes.rb +0 -259
- data/lib/netzke/active_record/combobox_options.rb +0 -16
- data/lib/netzke/active_record/relation_extensions.rb +0 -37
- data/lib/netzke/basepack/accordion_panel.rb +0 -39
- data/lib/netzke/basepack/action_column.rb +0 -68
- data/lib/netzke/basepack/action_column/javascripts/action_column.js +0 -61
- data/lib/netzke/basepack/auth_app.rb +0 -159
- data/lib/netzke/basepack/basic_app.rb +0 -7
- data/lib/netzke/basepack/border_layout_panel.rb +0 -53
- data/lib/netzke/basepack/border_layout_panel/javascripts/border_layout_panel.js +0 -40
- data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +0 -264
- data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +0 -260
- data/lib/netzke/basepack/form_panel.rb +0 -144
- data/lib/netzke/basepack/form_panel/fields.rb +0 -208
- data/lib/netzke/basepack/form_panel/javascripts/misc.js +0 -4
- data/lib/netzke/basepack/form_panel/services.rb +0 -142
- data/lib/netzke/basepack/grid_panel.rb +0 -441
- data/lib/netzke/basepack/grid_panel/columns.rb +0 -400
- data/lib/netzke/basepack/grid_panel/javascripts/rows-dd.js +0 -281
- data/lib/netzke/basepack/grid_panel/record_form_window.rb +0 -41
- data/lib/netzke/basepack/grid_panel/services.rb +0 -235
- data/lib/netzke/basepack/panel.rb +0 -11
- data/lib/netzke/basepack/wrapper.rb +0 -28
- data/lib/netzke/data_mapper.rb +0 -18
- data/lib/netzke/data_mapper/attributes.rb +0 -273
- data/lib/netzke/data_mapper/combobox_options.rb +0 -11
- data/lib/netzke/data_mapper/relation_extensions.rb +0 -38
- data/lib/netzke/sequel.rb +0 -18
- data/lib/netzke/sequel/attributes.rb +0 -274
- data/lib/netzke/sequel/combobox_options.rb +0 -10
- data/lib/netzke/sequel/relation_extensions.rb +0 -40
- data/locales/zh-cn.yml +0 -79
- data/test/basepack_test_app/app/components/book_form_with_custom_fields.rb +0 -21
- data/test/basepack_test_app/app/components/book_grid_with_column_actions.rb +0 -15
- data/test/basepack_test_app/app/components/book_grid_with_defaults.rb +0 -6
- data/test/basepack_test_app/app/components/book_paging_form_panel.rb +0 -22
- data/test/basepack_test_app/app/components/generic_user_form.rb +0 -12
- data/test/basepack_test_app/app/components/simple_accordion.rb +0 -11
- data/test/basepack_test_app/app/components/simple_tab_panel.rb +0 -11
- data/test/basepack_test_app/app/components/simple_wrapper.rb +0 -7
- data/test/basepack_test_app/app/components/some_accordion_panel.rb +0 -22
- data/test/basepack_test_app/app/presenters/forms/generic_user.rb +0 -6
- data/test/basepack_test_app/app/views/components/loadable_window.html.erb +0 -9
- data/test/basepack_test_app/app/views/components/simple_panel.html.erb +0 -1
- data/test/basepack_test_app/features/components_in_view.feature +0 -11
- data/test/basepack_test_app/features/simple_panel.feature +0 -11
- data/test/basepack_test_app/features/validations_in_grid.feature +0 -13
- data/test/basepack_test_app/features/virtual_attributes.feature +0 -16
- data/test/basepack_test_app/spec/components/form_panel_spec.rb +0 -53
- data/test/basepack_test_app/spec/components/grid_panel_spec.rb +0 -10
- data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +0 -68
- data/test/basepack_test_app/spec/data_adapter/attributes_spec.rb +0 -56
- data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +0 -125
- data/test/basepack_test_app/spec/factories.rb +0 -28
- data/test/basepack_test_app/spec/spec_helper.rb +0 -39
@@ -2,7 +2,6 @@
|
|
2
2
|
bodyStyle : 'padding:5px 5px 0',
|
3
3
|
autoScroll : true,
|
4
4
|
fieldDefaults : { labelWidth : 150 },
|
5
|
-
applyMask : { msg: "Updating..." },
|
6
5
|
|
7
6
|
defaults : {
|
8
7
|
anchor : '-20', // to leave some space for the scrollbar
|
@@ -39,7 +38,7 @@
|
|
39
38
|
|
40
39
|
this.initialConfig.errorReader = new ErrorReader();
|
41
40
|
|
42
|
-
// Now let Ext.form.
|
41
|
+
// Now let Ext.form.Form do the rest
|
43
42
|
this.callParent(arguments);
|
44
43
|
|
45
44
|
// To inform the parent about the apply event
|
@@ -131,48 +130,44 @@
|
|
131
130
|
delete values[fieldName];
|
132
131
|
}
|
133
132
|
}
|
134
|
-
|
135
|
-
//
|
136
|
-
|
137
|
-
// this.applyMaskCmp.show();
|
133
|
+
|
134
|
+
// loading mask
|
135
|
+
this.setLoading(true);
|
138
136
|
|
139
137
|
// We must use a different approach when the form is multipart, as we can't use the endpoint
|
140
|
-
if (this.
|
138
|
+
if (this.getForm().hasUpload()) {
|
141
139
|
this.getForm().submit({ // normal submit
|
142
140
|
url: this.endpointUrl("netzke_submit"),
|
143
141
|
params: {
|
144
142
|
data: Ext.encode(values) // here are the correct values that may be different from display values
|
145
143
|
},
|
146
144
|
failure: function(form, action){
|
147
|
-
|
145
|
+
var respObj = Ext.decode(action.response.responseText);
|
146
|
+
delete respObj.success;
|
147
|
+
this.netzkeBulkExecute(respObj);
|
148
|
+
this.setLoading(false);
|
148
149
|
},
|
149
150
|
success: function(form, action) {
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
this.bulkExecute(respObj);
|
154
|
-
this.fireEvent('submitsuccess');
|
155
|
-
}
|
156
|
-
catch(e) {
|
157
|
-
Ext.Msg.alert('File upload error', action.response.responseText);
|
158
|
-
}
|
159
|
-
if (this.applyMaskCmp) this.applyMaskCmp.hide();
|
151
|
+
var respObj = Ext.decode(action.response.responseText);
|
152
|
+
delete respObj.success;
|
153
|
+
this.netzkeBulkExecute(respObj);
|
160
154
|
},
|
161
155
|
scope: this
|
162
156
|
});
|
163
157
|
} else {
|
164
|
-
this.netzkeSubmit(Ext.apply((this.baseParams || {}), {data:Ext.encode(values)}), function(
|
165
|
-
if (success) {
|
166
|
-
this.fireEvent("submitsuccess");
|
167
|
-
if (this.mode == "lockable") this.setReadonlyMode(true);
|
168
|
-
};
|
169
|
-
if (this.applyMaskCmp) this.applyMaskCmp.hide();
|
170
|
-
}, this);
|
158
|
+
this.netzkeSubmit(Ext.apply((this.baseParams || {}), { data:Ext.encode(values) }), function(){ this.setLoading(false); }, this);
|
171
159
|
}
|
172
160
|
}
|
173
161
|
this.fireEvent('afterApply', this);
|
174
162
|
},
|
175
163
|
|
164
|
+
// called from the server
|
165
|
+
onSubmitSuccess: function() {
|
166
|
+
this.fireEvent("submitsuccess");
|
167
|
+
if (this.mode == "lockable") this.setReadonlyMode(true);
|
168
|
+
this.setLoading(false);
|
169
|
+
},
|
170
|
+
|
176
171
|
setFormValues: function(values){
|
177
172
|
var assocValues = values.meta.associationValues || {};
|
178
173
|
for (var assocFieldName in assocValues) {
|
@@ -220,5 +215,4 @@
|
|
220
215
|
}
|
221
216
|
}, this);
|
222
217
|
}
|
223
|
-
|
224
218
|
}
|
File without changes
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Netzke
|
2
|
+
module Basepack
|
3
|
+
class Form < Netzke::Base
|
4
|
+
module Services
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
#
|
9
|
+
# Endpoints
|
10
|
+
|
11
|
+
# Called when the form gets submitted (e.g. by pressing the Apply button)
|
12
|
+
endpoint :netzke_submit do |params, this|
|
13
|
+
data = ActiveSupport::JSON.decode(params[:data])
|
14
|
+
data.each_pair do |k,v|
|
15
|
+
data[k]=nil if v.blank? || v == "null" # Ext JS returns "null" on empty date fields, or "" for not filled optional integer fields, which gives errors when passed to model (at least in DataMapper)
|
16
|
+
end
|
17
|
+
|
18
|
+
# File uploads are in raw params instead of "data" hash, so, mix them in into "data"
|
19
|
+
controller.params.each_pair do |k,v|
|
20
|
+
data[k] = v if v.is_a?(ActionDispatch::Http::UploadedFile)
|
21
|
+
end
|
22
|
+
|
23
|
+
success = create_or_update_record(data)
|
24
|
+
|
25
|
+
if success
|
26
|
+
this.set_form_values(js_record_data)
|
27
|
+
this.success = true # respond to classic form submission with {success: true}
|
28
|
+
this.on_submit_success # inform the Netzke endpoint caller about success
|
29
|
+
else
|
30
|
+
# flash eventual errors
|
31
|
+
data_adapter.errors_array(@record).each do |error|
|
32
|
+
flash :error => error
|
33
|
+
end
|
34
|
+
this.netzke_feedback(@flash)
|
35
|
+
this.apply_form_errors(build_form_errors(record))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Can be called when the form needs to load a record with given ID. E.g.:
|
40
|
+
#
|
41
|
+
# someForm.netzkeLoad({id: 100});
|
42
|
+
endpoint :netzke_load do |params, this|
|
43
|
+
@record = data_class && data_adapter.find_record(params[:id])
|
44
|
+
this.set_form_values js_record_data
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns options for a combobox
|
48
|
+
# params receive:
|
49
|
+
# +attr+ - column's name
|
50
|
+
# +query+ - what's typed-in in the combobox
|
51
|
+
# +id+ - selected record id
|
52
|
+
endpoint :get_combobox_options do |params, this|
|
53
|
+
attr = fields[params[:attr].to_sym]
|
54
|
+
this.data = data_adapter.combo_data(attr, params[:query])
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def values
|
60
|
+
record && record.netzke_hash(fields)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Builds the form errors
|
66
|
+
def build_form_errors(record)
|
67
|
+
form_errors = {}
|
68
|
+
foreign_keys = data_adapter.hash_fk_model
|
69
|
+
record.errors.to_hash.map{|field, error|
|
70
|
+
# some ORM return an array for error
|
71
|
+
error = error.join ', ' if error.kind_of? Array
|
72
|
+
# Get the correct field name for the errors on foreign keys
|
73
|
+
if foreign_keys.has_key?(field)
|
74
|
+
fields.each do |k, v|
|
75
|
+
# Hack to stop to_nifty_json from camalizing model__field
|
76
|
+
field = k.to_s.gsub('__', '____') if k.to_s.split('__').first == foreign_keys[field].to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
form_errors[field] ||= []
|
80
|
+
form_errors[field] << error
|
81
|
+
}
|
82
|
+
form_errors
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates/updates a record from hash
|
86
|
+
def create_or_update_record(hsh)
|
87
|
+
hsh.merge!(config[:strong_default_attrs]) if config[:strong_default_attrs]
|
88
|
+
@record ||= data_adapter.find_record hsh.delete(data_class.primary_key.to_s) # only pick up the record specified in the params if it was not provided in the configuration
|
89
|
+
#data_class.find(:first, :conditions => {data_class.primary_key => hsh.delete(data_class.primary_key)})
|
90
|
+
success = true
|
91
|
+
|
92
|
+
@record = data_class.new if @record.nil?
|
93
|
+
|
94
|
+
hsh.each_pair do |k,v|
|
95
|
+
data_adapter.set_record_value_for_attribute(@record, fields[k.to_sym].nil? ? {:name => k} : fields[k.to_sym], v, config.role || :default)
|
96
|
+
end
|
97
|
+
|
98
|
+
#hsh.each_pair do |k,v|
|
99
|
+
#begin
|
100
|
+
#@record.send("#{k}=",v)
|
101
|
+
#rescue StandardError => exc
|
102
|
+
#flash :error => exc.message
|
103
|
+
#success = false
|
104
|
+
#break
|
105
|
+
#end
|
106
|
+
#end
|
107
|
+
|
108
|
+
# did we have complete success?
|
109
|
+
success && data_adapter.save_record(@record)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
File without changes
|
@@ -0,0 +1,355 @@
|
|
1
|
+
require "netzke/basepack/grid/services"
|
2
|
+
|
3
|
+
module Netzke
|
4
|
+
module Basepack
|
5
|
+
# {Ext.grid.Panel}[http://docs.sencha.com/ext-js/4-1/#!/api/Ext.grid.Panel] -based component with the following features:
|
6
|
+
#
|
7
|
+
# * automatic column configuration based on used ORM
|
8
|
+
# * pagination
|
9
|
+
# * multi-line CRUD operations
|
10
|
+
# * (multe-record) editing and adding records through a form
|
11
|
+
# * one-to-many association support
|
12
|
+
# * server-side sorting
|
13
|
+
# * filtering
|
14
|
+
# * complex query search with preset management
|
15
|
+
# * persistent column resizing, moving and hiding
|
16
|
+
# * permissions
|
17
|
+
# * virtual attribute support
|
18
|
+
#
|
19
|
+
# == Instance configuration
|
20
|
+
# The following config options are supported:
|
21
|
+
# [:+model+]
|
22
|
+
# Name of the ActiveRecord model that provides data to this Grid, e.g. "User"
|
23
|
+
# [:+columns+]
|
24
|
+
# An array of columns to be displayed in the grid; each column may be represented by a symbol (representing the model's attribute name), or a hash (when extra configuration is needed). See the "Columns" section below.
|
25
|
+
# [:+scope+]
|
26
|
+
# Specifies how the data should be filtered.
|
27
|
+
# When it's a symbol, it's used as a scope name.
|
28
|
+
# When it's a string, it's a SQL statement (passed directly to +where+).
|
29
|
+
# When it's a hash, it's a conditions hash (passed directly to +where+).
|
30
|
+
# When it's an array, it's expanded into an SQL statement with arguments (passed directly to +where+), e.g.:
|
31
|
+
#
|
32
|
+
# scope: ["id > ?", 100])
|
33
|
+
#
|
34
|
+
# [:+role+]
|
35
|
+
# Role for ActiveModel mass-assignment security
|
36
|
+
# [:+strong_default_attrs+]
|
37
|
+
# (defaults to {}) a hash of attributes to be merged atop of every created/updated record, e.g. +role_id: 1+
|
38
|
+
# [:+enable_column_filters+]
|
39
|
+
# (defaults to true) enable filters in column's context menu
|
40
|
+
# [:+enable_edit_in_form+]
|
41
|
+
# (defaults to true) provide buttons into the toolbar that activate editing/adding records via a form
|
42
|
+
# [:+enable_extended_search+]
|
43
|
+
# (defaults to true) provide a button into the toolbar that shows configurable search form
|
44
|
+
# [:+enable_context_menu+]
|
45
|
+
# (defaults to true) enable rows context menu
|
46
|
+
# [:+context_menu+]
|
47
|
+
# An array of actions (e.g. [:edit, "-", :del] - see the Actions section) or +false+ to disable the context menu
|
48
|
+
# [:+enable_pagination+]
|
49
|
+
# (defaults to true) enable pagination
|
50
|
+
# [:+rows_per_page+]
|
51
|
+
# (defaults to 30) number of rows per page (ignored when +enable_pagination+ is set to +false+)
|
52
|
+
# [:+load_inline_data+]
|
53
|
+
# (defaults to false) grid is being loaded along with its initial data; use with precaution, preferred method is auto-loading of data in a separate server request (see +data_store+)
|
54
|
+
# [:+data_store+]
|
55
|
+
# (defaults to {}) extra configuration for the JS class's internal store (see {Ext.data.Store}[http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Store] ). For example, to disable auto loading of data, do:
|
56
|
+
#
|
57
|
+
# data_store: {auto_load: false}
|
58
|
+
#
|
59
|
+
# == Columns
|
60
|
+
# Columns are configured by passing an array to the +columns+ option. Each element in the array is either the name of model's (virtual) attribute (in which case the configuration will be fully automatic), or a hash that may contain the following configuration options as keys:
|
61
|
+
#
|
62
|
+
# [:+name+]
|
63
|
+
# (required) name of the column, that may correspond to the model's (virtual) attribute
|
64
|
+
# [:+read_only+]
|
65
|
+
# A boolean that defines if the cells in the column should be editable
|
66
|
+
# [:+editable+]
|
67
|
+
# Same as +read_only+, but in reverse (takes precedence over +read_only+)
|
68
|
+
# [:+filterable+]
|
69
|
+
# Set to false to disable filtering on this column
|
70
|
+
# [:+getter+]
|
71
|
+
# A lambda that receives a record as a parameter, and is expected to return a string that will be sent to the cell (can be HTML code), e.g.:
|
72
|
+
#
|
73
|
+
# getter: ->(r){ [r.first_name, r.last_name].join }
|
74
|
+
#
|
75
|
+
# [:+setter+]
|
76
|
+
# A lambda that receives a record as first parameter, and the value passed from the cell as the second parameter, and is expected to modify the record accordingly, e.g.:
|
77
|
+
#
|
78
|
+
# :setter => ->(r,v){ r.first_name, r.last_name = v.split(" ") }
|
79
|
+
#
|
80
|
+
# [:+scope+]
|
81
|
+
# The scope for one-to-many association column. Same syntax applies as for scoping out records for the grid itself. See "One-to-many association support" for details.
|
82
|
+
#
|
83
|
+
# [:+sorting_scope+]
|
84
|
+
# The name of the scope used for sorting the column. This can be useful for virtual columns for example. The scope will get one parameter specifying the direction (:asc or :desc). Example:
|
85
|
+
#
|
86
|
+
# columns => [{ name: "complete_user_name", sorting_scope: :sort_user_by_full_name }, ...]
|
87
|
+
#
|
88
|
+
# class User < ActiveRecord::Base
|
89
|
+
# scope :sort_user_by_full_name, ->(dir){
|
90
|
+
# order("users.first_name #{dir.to_s}, users.last_name #{dir.to_s}")
|
91
|
+
# }
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# [:+format+]
|
95
|
+
# The format to display data in case of date and datetime columns, e.g. 'Y-m-d g:i:s'.
|
96
|
+
# [:+excluded+]
|
97
|
+
# When true, this column will not be used in the grid (not even in the hidden mode)
|
98
|
+
# [:+blank_line+]
|
99
|
+
# The blank line for one-to-many association columns, defaults to "---". Set to false to exclude completely.
|
100
|
+
#
|
101
|
+
# Besides these options, a column can receive any meaningful config option understood by {Ext.grid.column.Column}(http://docs.sencha.com/ext-js/4-1/#!/api/Ext.grid.column.Column) (e.g. +hidden+)
|
102
|
+
#
|
103
|
+
# === Customizing columns by extending Grid
|
104
|
+
# Grid itself always uses the columns provided in the `columns` config option. But this behavior can be changed by overriding the `columns` method, which follows the same semantics as the `columns` config option. This can be used, for example, for extending the list of columns provided in the config:
|
105
|
+
#
|
106
|
+
# def columns
|
107
|
+
# super + [:extra_column]
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
#
|
111
|
+
# == One-to-many association support
|
112
|
+
# If the model bound to a grid +belongs_to+ another model, Grid can display an "assocition column" - where the user can select the associated record from a drop-down box. You can specify which method of the association should be used as the display value for the drop-down box options by using the double-underscore notation on the column name, where the association name is separated from the association method by "__" (double underscore). For example, let's say we have a Book that +belongs_to+ model Author, and Author responds to +first_name+. This way, the book grid can have a column defined as follows:
|
113
|
+
#
|
114
|
+
# {name: "author__first_name"}
|
115
|
+
#
|
116
|
+
# Grid will detect it to be an association column, and will use the drop-down box for selecting an author, where the list of authors will be represented by the author's first name.
|
117
|
+
#
|
118
|
+
# In order to scope out the records displayed in the drop-down box, the +scope+ column option can be used, e.g.:
|
119
|
+
#
|
120
|
+
# {name: "author__first_name", scope: ->(relation){relation.where(:popular => true)}
|
121
|
+
#
|
122
|
+
# == Add/edit forms
|
123
|
+
# The forms will by default display the fields that correspond to the configured columns, taking over meaningful configuration options (e.g. +text+ will be converted into +fieldLabel+).
|
124
|
+
# You may override the default fields displayed in the forms by overriding the +default_fields_for_forms+ method, which should return an array understood by the +items+ config property of the +Form+. If you need to use a custom class instead of +Form+, you need to override the +preconfigure_record_window+ method:
|
125
|
+
#
|
126
|
+
# def preconfigure_record_window(c)
|
127
|
+
# super
|
128
|
+
# c.form_config.klass = UserForm
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
#
|
132
|
+
# == Actions
|
133
|
+
# You can override Grid's actions to change their text, icons, and tooltips (see http://rdoc.info/github/netzke/netzke-core/Netzke/Core/Actions).
|
134
|
+
#
|
135
|
+
# Grid implements the following actions:
|
136
|
+
# [:+add+]
|
137
|
+
# Inline adding of a record
|
138
|
+
# [:+del+]
|
139
|
+
# Deletion of records
|
140
|
+
# [:+edit+]
|
141
|
+
# Inline editing of a record
|
142
|
+
# [:+apply+]
|
143
|
+
# Applying inline changes
|
144
|
+
# [:+add_in_form+]
|
145
|
+
# Adding a record in a form
|
146
|
+
# [:+edit_in_form+]
|
147
|
+
# (multi-record) editing in a forrm
|
148
|
+
# [:+search+]
|
149
|
+
# Advanced searching
|
150
|
+
#
|
151
|
+
#
|
152
|
+
#
|
153
|
+
# == Class-level configuration
|
154
|
+
#
|
155
|
+
# Configuration on this level is effective during the life-time of the application. One place for setting these options is initializers:
|
156
|
+
#
|
157
|
+
# Netzke::Basepack::Grid.setup do |c|
|
158
|
+
# c.edit_in_form_available = false
|
159
|
+
# c.advanced_search_available = false
|
160
|
+
# c.column_filters_available = false
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# Most of these options influence the amount of JavaScript code that is generated for this component's class, in the way that the less functionality is enabled, the less code is generated.
|
164
|
+
#
|
165
|
+
# The following class configuration options are available:
|
166
|
+
# [:+column_filters_available+]
|
167
|
+
# (defaults to true) include code for the filters in the column's context menu
|
168
|
+
# [:+edit_in_form_available+]
|
169
|
+
# (defaults to true) include code for (multi-record) editing and adding records through a form
|
170
|
+
# [:+advanced_search_available+]
|
171
|
+
# (defaults to true) include code for extended configurable search
|
172
|
+
class Grid < Netzke::Base
|
173
|
+
include self::Services
|
174
|
+
include Columns
|
175
|
+
include DataAccessor
|
176
|
+
include Netzke::Core::ConfigToDslDelegator
|
177
|
+
|
178
|
+
class_attribute :column_filters_available
|
179
|
+
self.column_filters_available = true
|
180
|
+
|
181
|
+
class_attribute :advanced_search_available
|
182
|
+
self.advanced_search_available = true
|
183
|
+
|
184
|
+
class_attribute :edit_in_form_available
|
185
|
+
self.edit_in_form_available = true
|
186
|
+
|
187
|
+
# JavaScript class configuration
|
188
|
+
js_configure do |c|
|
189
|
+
c.extend = "Ext.grid.Panel"
|
190
|
+
c.mixin :grid, :event_handling
|
191
|
+
c.mixin :advanced_search if advanced_search_available
|
192
|
+
c.mixin :edit_in_form if edit_in_form_available
|
193
|
+
|
194
|
+
c.translate *%w[are_you_sure confirmation]
|
195
|
+
|
196
|
+
# JavaScript includes
|
197
|
+
ex = Netzke::Core.ext_path.join("examples")
|
198
|
+
|
199
|
+
c.require ex.join("ux/CheckColumn.js")
|
200
|
+
c.require :check_column_fix
|
201
|
+
|
202
|
+
# Includes for column filters
|
203
|
+
if column_filters_available
|
204
|
+
[
|
205
|
+
"ux/grid/menu/ListMenu.js",
|
206
|
+
"ux/grid/menu/RangeMenu.js",
|
207
|
+
"ux/grid/FiltersFeature.js"
|
208
|
+
].each{ |path| c.require(ex.join(path)) }
|
209
|
+
|
210
|
+
%w{Boolean Date List Numeric String}.unshift("").each do |f|
|
211
|
+
c.require(ex.join"ux/grid/filter/#{f}Filter.js")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Allows children classes to simply do:
|
217
|
+
#
|
218
|
+
# model "User"
|
219
|
+
delegates_to_dsl :model
|
220
|
+
|
221
|
+
def configure(c)
|
222
|
+
# Defaults. The nil? checks are needed because these can be already set in a subclass
|
223
|
+
c.enable_edit_in_form = self.class.edit_in_form_available if c.enable_edit_in_form.nil?
|
224
|
+
c.enable_extended_search = self.class.advanced_search_available if c.enable_extended_search.nil?
|
225
|
+
c.enable_column_filters = self.class.column_filters_available if c.enable_column_filters.nil?
|
226
|
+
c.enable_pagination = true if c.enable_pagination.nil?
|
227
|
+
c.rows_per_page = 30 if c.rows_per_page.nil?
|
228
|
+
c.tools = %w{ refresh } if c.tools.nil?
|
229
|
+
|
230
|
+
super
|
231
|
+
end
|
232
|
+
|
233
|
+
def js_configure(c) #:nodoc:
|
234
|
+
super
|
235
|
+
|
236
|
+
c.title = c.title || self.class.js_config.properties[:title] || data_class.name.pluralize
|
237
|
+
c.bbar = bbar
|
238
|
+
c.context_menu = context_menu
|
239
|
+
c.columns = final_columns(with_meta: true)
|
240
|
+
c.columns_order = columns_order
|
241
|
+
c.inline_data = get_data if c.load_inline_data
|
242
|
+
c.pri = data_adapter.primary_key
|
243
|
+
end
|
244
|
+
|
245
|
+
def config
|
246
|
+
@config ||= ActiveSupport::OrderedOptions.new.tap do |c|
|
247
|
+
# extend with data_store convenient config object
|
248
|
+
c.data_store = ActiveSupport::OrderedOptions.new
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def bbar
|
253
|
+
config.has_key?(:bbar) ? config[:bbar] : default_bbar
|
254
|
+
end
|
255
|
+
|
256
|
+
def context_menu
|
257
|
+
config.has_key?(:context_menu) ? config[:context_menu] : default_context_menu
|
258
|
+
end
|
259
|
+
|
260
|
+
# Override to change the default bottom toolbar
|
261
|
+
def default_bbar
|
262
|
+
res = %w{ add edit apply del }.map(&:to_sym)
|
263
|
+
res << "-" << :add_in_form << :edit_in_form if config[:enable_edit_in_form]
|
264
|
+
res << "-" << :search if config[:enable_extended_search]
|
265
|
+
res
|
266
|
+
end
|
267
|
+
|
268
|
+
# Override to change the default context menu
|
269
|
+
def default_context_menu
|
270
|
+
res = %w{ edit del }.map(&:to_sym)
|
271
|
+
res << "-" << :edit_in_form if config[:enable_edit_in_form]
|
272
|
+
res
|
273
|
+
end
|
274
|
+
|
275
|
+
action :add do |a|
|
276
|
+
a.disabled = config[:prohibit_create]
|
277
|
+
a.handler = "onAddInline" # overriding naming conventions as Ext 4 grid has its own onAdd method
|
278
|
+
a.icon = :add
|
279
|
+
end
|
280
|
+
|
281
|
+
action :edit do |a|
|
282
|
+
a.disabled = true
|
283
|
+
a.icon = :table_edit
|
284
|
+
end
|
285
|
+
|
286
|
+
action :del do |a|
|
287
|
+
a.disabled = true
|
288
|
+
a.icon = :table_row_delete
|
289
|
+
end
|
290
|
+
|
291
|
+
action :apply do |a|
|
292
|
+
a.disabled = config[:prohibit_update] && config[:prohibit_create]
|
293
|
+
a.icon = :tick
|
294
|
+
end
|
295
|
+
|
296
|
+
action :add_in_form do |a|
|
297
|
+
a.icon = :application_form_add
|
298
|
+
end
|
299
|
+
|
300
|
+
action :edit_in_form do |a|
|
301
|
+
a.disabled = true
|
302
|
+
a.icon = :application_form_edit
|
303
|
+
end
|
304
|
+
|
305
|
+
action :search do |a|
|
306
|
+
a.enable_toggle = true
|
307
|
+
a.icon = :find
|
308
|
+
end
|
309
|
+
|
310
|
+
component :add_window do |c|
|
311
|
+
preconfigure_record_window(c)
|
312
|
+
c.title = "Add #{data_class.model_name.human}"
|
313
|
+
c.items = [:add_form]
|
314
|
+
c.form_config.record = data_class.new(columns_default_values)
|
315
|
+
end
|
316
|
+
|
317
|
+
component :edit_window do |c|
|
318
|
+
preconfigure_record_window(c)
|
319
|
+
c.title = "Edit #{data_class.model_name.human}"
|
320
|
+
c.items = [:edit_form]
|
321
|
+
end
|
322
|
+
|
323
|
+
component :multi_edit_window do |c|
|
324
|
+
preconfigure_record_window(c)
|
325
|
+
c.title = "Edit #{data_class.model_name.human.pluralize}"
|
326
|
+
c.items = [:multi_edit_form]
|
327
|
+
end
|
328
|
+
|
329
|
+
component :search_window do |c|
|
330
|
+
c.klass = SearchWindow
|
331
|
+
c.model = config.model
|
332
|
+
c.fields = default_fields_for_forms
|
333
|
+
end
|
334
|
+
|
335
|
+
protected
|
336
|
+
|
337
|
+
def preconfigure_record_window(c)
|
338
|
+
c.klass = RecordFormWindow
|
339
|
+
|
340
|
+
c.form_config = ActiveSupport::OrderedOptions.new.tap do |f|
|
341
|
+
f.model = config[:model]
|
342
|
+
f.persistent_config = config[:persistent_config]
|
343
|
+
f.strong_default_attrs = config[:strong_default_attrs]
|
344
|
+
f.mode = config[:mode]
|
345
|
+
f.items = default_fields_for_forms
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def self.server_side_config_options
|
350
|
+
super + [:scope]
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|