Class: Netzke::GridPanel

Inherits:
Base
  • Object
show all
Includes:
DataAccessor, GridPanelApi, GridPanelJs, Plugins::ConfigurationTool
Defined in:
lib/netzke/grid_panel.rb

Overview

GridPanel

Ext.grid.EditorGridPanel + server-side code

Features:

  • multi-line CRUD operations - get, post, delete, create

  • (multe-record) editing and adding records through a form

  • column resize, move and hide

  • permissions

  • sorting

  • pagination

  • filtering

  • extended configurable search

  • rows reordering (drag-n-drop)

  • dynamic configuration of properties and columns

Class configuration

Configuration on this level is effective during the life-time of the application. They can be put into a .rb file inside of config/initializers like this:

Netzke::GridPanel.configure :column_filters_available, false
Netzke::GridPanel.configure :default_config => {:ext_config => {:enable_config_tool => false}}

Most of these options directly influence the amount of JavaScript code that is generated for this widget’s class. The less functionality is enabled, the less code is generated.

The following configuration options are available:

  • :column_filters_available - (default is true) include code for the filters in the column’s context menu

  • :config_tool_available - (default is true) include code for the configuration tool that launches the configuration panel

  • :edit_in_form_available - (defaults to true) include code for (multi-record) editing and adding records through a form

  • :extended_search_available - (defaults to true) include code for extended configurable search

  • :default_config - a hash of default configuration options for each instance of the GridPanel widget.

See the “Instance configuration” section below.

Instance configuration

The following config options are available:

  • :data_class_name - name of the ActiveRecord model that provides data to this GridPanel.

  • :strong_default_attrs - a hash of attributes to be merged atop of every created/updated record.

  • :scopes - an array of searchlogic-compatible scopes to filter grid data like this:

    ["user_id_not", 100]
    

In the :ext_config hash (see Netzke::Base) the following GridPanel specific options are available:

  • :enable_column_filters - enable filters in column’s context menu

  • :enable_edit_in_form - provide buttons into the toolbar that activate editing/adding records via a form

  • :enable_extended_search - provide a button into the toolbar that shows configurable search form

  • :enable_context_menu - enable rows context menu

  • :enable_rows_reordering - enable reordering of rows with drag-n-drop; underlying model (specified in :data_class_name) must implement “acts_as_list”-compatible functionality; defaults to false

  • :enable_pagination - enable pagination; defaults to true

  • :rows_per_page - number of rows per page (ignored when :enable_pagination is set to false)

  • :load_inline_data - load initial data into the grid right after its instantiation (saves a request to server); defaults to true

  • :mode - when set to :config, GridPanel loads in configuration mode

Additionally supports Netzke::Base config options.

Direct Known Subclasses

FieldsConfigurator

Constant Summary collapse

TYPE_EDITOR_MAP =
{
  :integer => :numberfield,
  :boolean => :checkbox,
  :date => :datefield,
  :datetime => :xdatetime,
  :text => :textarea
  # :string => :textfield
}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Plugins::ConfigurationTool

#config_tool_needed?, #config_with_config_tool, included, #initial_aggregatees_with_config_tool, #js_config_with_config_tool, #tools_with_config_tool

Methods included from DataAccessor

#apply_helpers, #normalize_array_of_columns, #normalize_column, #normalize_columns, #predefined_columns

Methods included from GridPanelApi

#configuration_panel__columns__get_combobox_options, #delete_data, #get_combobox_options, #get_data, #get_search, #hide_column, #move_column, #post_data, #resize_column

Methods included from GridPanelJs

included, #js_config

Constructor Details

#initialize(config = {}, parent = nil) ⇒ GridPanel

Returns a new instance of GridPanel.



149
150
151
152
153
# File 'lib/netzke/grid_panel.rb', line 149

def initialize(config = {}, parent = nil)
  super

  apply_helpers
end

Class Method Details

.configObject

Class-level configuration. This options directly influence the amount of generated javascript code for this widget’s class. For example, if you don’t want filters for the grid, set :column_filters_available to false, and the javascript for the filters won’t be included at all.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/netzke/grid_panel.rb', line 75

def self.config
  set_default_config({
    
    :column_filters_available     => true,
    :config_tool_available        => true,
    :edit_in_form_available       => true,
    :extended_search_available    => true,
    :rows_reordering_available    => false,
    
    :default_config => {
      :ext_config => {
        :enable_edit_in_form    => true,
        :enable_extended_search => true,
        :enable_column_filters  => true,
        :load_inline_data       => true,
        :enable_context_menu    => true,
        
        :enable_pagination      => true,
        :rows_per_page          => 25,
        
        :mode                   => :normal, # when set to :config, :configuration button is enabled
        
        :enable_rows_reordering => false, # drag n drop
        
        :tools => %w{ refresh }
      },
      
      :persistent_config => false
    }
  })
end

.config_columnsObject

Columns to be displayed by the FieldConfigurator.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/netzke/grid_panel.rb', line 156

def self.config_columns
  [
    {:name => :name,       :type => :string, :editor => :combobox, :width => 200},
    {:name => :excluded,   :type => :boolean, :editor => :checkbox, :width => 40, :header => "Excl"},
    {:name => :value},
    {:name => :header},
    {:name => :hidden,     :type => :boolean, :editor => :checkbox},
    {:name => :read_only,  :type => :boolean, :editor => :checkbox, :header => "R"},
    {:name => :editor,     :type => :string, :editor => {:xtype => :combobox, :options => Netzke::Ext::FORM_FIELD_XTYPES}},
    {:name => :renderer,   :type => :string},
    # {:name => :renderer, :type => :string, :editor => {:xtype => :jsonfield}},
    {:name => :with_filters,   :type => :boolean, :editor => :checkbox, :default => true, :header => "Filters"},

    # some rarely used configurations, hidden
    {:name => :width,      :type => :integer, :editor => :numberfield, :hidden => true},
    {:name => :hideable,   :type => :boolean, :editor => :checkbox, :default => true, :hidden => true},
    {:name => :sortable,   :type => :boolean, :editor => :checkbox, :default => true, :hidden => true},
  ]
end

.enforce_config_consistencyObject



66
67
68
69
70
# File 'lib/netzke/grid_panel.rb', line 66

def self.enforce_config_consistency
  config[:default_config][:ext_config][:enable_edit_in_form] &&= config[:edit_in_form_available]
  config[:default_config][:ext_config][:enable_extended_search] &&= config[:extended_search_available]
  config[:default_config][:ext_config][:enable_rows_reordering] &&= config[:rows_reordering_available]
end

.include_jsObject

Include extra javascript that we depend on



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/netzke/grid_panel.rb', line 108

def self.include_js
  res = []
  
  # Checkcolumn
  res << "#{File.dirname(__FILE__)}/grid_panel_extras/javascripts/check-column.js"
  
  # Filters
  if config[:column_filters_available]
    ext_examples = Netzke::Base.config[:ext_location] + "/examples/"
    res << ext_examples + "grid-filtering/menu/EditableItem.js"
    res << ext_examples + "grid-filtering/menu/RangeMenu.js"
    res << ext_examples + "grid-filtering/grid/GridFilters.js"

    %w{Boolean Date List Numeric String}.unshift("").each do |f|
      res << ext_examples + "grid-filtering/grid/filter/#{f}Filter.js"
    end
    
    res << "#{File.dirname(__FILE__)}/grid_panel_extras/javascripts/filters.js"
    
  end
  
  # DD
  if config[:rows_reordering_available]
    res << "#{File.dirname(__FILE__)}/grid_panel_extras/javascripts/rows-dd.js"
  end

  res
end

.property_fieldsObject



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/netzke/grid_panel.rb', line 176

def self.property_fields
  res = [
    {:name => :ext_config__title,               :type => :string},
    {:name => :ext_config__header,              :type => :boolean, :default => true},
    {:name => :ext_config__enable_context_menu, :type => :boolean, :default => true},
    {:name => :ext_config__context_menu,        :type => :json},
    {:name => :ext_config__enable_pagination,   :type => :boolean, :default => true},
    {:name => :ext_config__rows_per_page,       :type => :integer},
    # {:name => :ext_config__bbar,              :type => :json},
    {:name => :ext_config__prohibit_create,     :type => :boolean},
    {:name => :ext_config__prohibit_update,     :type => :boolean},
    {:name => :ext_config__prohibit_delete,     :type => :boolean},
    {:name => :ext_config__prohibit_read,       :type => :boolean}
  ]
  
  res << {:name => :ext_config__enable_extended_search, :type => :boolean} if config[:extended_search_available]
  res << {:name => :ext_config__enable_edit_in_form, :type => :boolean} if config[:edit_in_form_available]
  
  res
  
end

Instance Method Details

#actionsObject



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/netzke/grid_panel.rb', line 238

def actions
  # Defaults
  res = { 
    :add    => {:text => 'Add',     :disabled => ext_config[:prohibit_create]},
    :edit   => {:text => 'Edit',    :disabled => true},
    :del    => {:text => 'Delete',  :disabled => true},
    :apply  => {:text => 'Apply',   :disabled => ext_config[:prohibit_update] && ext_config[:prohibit_create]}
  }
  
  # Edit in form
  res.merge!({
    :add_in_form => {:text => 'Add in form'},
    :edit_in_form => {:text => 'Edit in form', :disabled => true}
  }) if ext_config[:enable_edit_in_form]
  
  # Extended search
  res.merge!({
    :search => {:text => 'Search'}
  }) if ext_config[:enable_extended_search]

  res
end

#column_at(index) ⇒ Object

Normalizes the column at position index and returns it.



343
344
345
346
347
348
349
350
351
352
# File 'lib/netzke/grid_panel.rb', line 343

def column_at(index)
  if columns[index].is_a?(Hash)
    columns[index]
  else
    column_name = columns.delete_at(index)
    normalized_column = normalize_column(column_name)
    columns.insert(index, normalized_column)
    normalized_column
  end
end

#columnsObject



321
322
323
# File 'lib/netzke/grid_panel.rb', line 321

def columns
  @columns ||= get_columns
end

#configuration_widgetsObject



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/netzke/grid_panel.rb', line 220

def configuration_widgets
  res = []
  res << {
    :persistent_config => true,
    :name              => 'columns',
    :widget_class_name => "FieldsConfigurator",
    :active            => true,
    :widget            => self
  }
  res << {
    :name               => 'general',
    :widget_class_name  => "PropertyEditor",
    :widget             => self,
    :ext_config         => {:title => false}
  }
  res
end

#data_classObject



144
145
146
# File 'lib/netzke/grid_panel.rb', line 144

def data_class
  @data_class ||= config[:data_class_name].nil? ? raise(ArgumentError, "No data_class_name specified for widget #{id_name}") : config[:data_class_name].constantize
end

#default_columnsObject



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'lib/netzke/grid_panel.rb', line 368

def default_columns
  # columns specified in widget's config
  columns_from_config = config[:columns] && normalize_columns(config[:columns]) 
  
  if columns_from_config
    # reverse-merge each column hash from config with each column hash from exposed_attributes (columns from config have higher priority)
    for c in columns_from_config
      corresponding_exposed_column = predefined_columns.find{ |k| k[:name] == c[:name] }
      c.reverse_merge!(corresponding_exposed_column) if corresponding_exposed_column
    end
    columns_for_create = columns_from_config
  else
    # we didn't have columns configured in widget's config, so, use the columns from the data class
    columns_for_create = predefined_columns
  end
  
  columns_for_create.map! do |c|
    # detect ActiveRecord column type (if the column is "real") or fall back to :virtual
    type = (data_class.columns_hash[c[:name].to_s] && data_class.columns_hash[c[:name].to_s].type) || :virtual

    # detect :assoc__method columns
    if c[:name].to_s.index('__')
      assoc_name, method = c[:name].to_s.split('__').map(&:to_sym)
      if assoc = data_class.reflect_on_association(assoc_name)
        assoc_column = assoc.klass.columns_hash[method.to_s]
        assoc_method_type = assoc_column.try(:type)
        if assoc_method_type
          c[:editor] ||= TYPE_EDITOR_MAP[assoc_method_type] == :checkbox ? :checkbox : :combobox
        end
        type = :association
      end
    end
    
    # detect association column (e.g. :category_id)
    assoc = data_class.reflect_on_all_associations.detect{|a| a.primary_key_name.to_sym == c[:name]}
    if  assoc && !assoc.options[:polymorphic]
      c[:editor] ||= :combobox
      assoc_method = %w{name title label id}.detect{|m| (assoc.klass.instance_methods + assoc.klass.column_names).include?(m) } || assoc.klass.primary_key
      c[:name] = "#{assoc.name}__#{assoc_method}".to_sym
      type = :association
    end
    
    # Some smart defaults
    
    # default editor, dependent on column type
    c[:editor] ||= TYPE_EDITOR_MAP[type] unless TYPE_EDITOR_MAP[type].nil?
    # narrow column for checkbox
    c[:width] ||= 50 if c[:editor] == :checkbox
    # wider column for xdatetime
    c[:width] ||= 120 if c[:editor] == :xdatetime
    # hide ID column
    c[:hidden] = true if c[:name] == data_class.primary_key.to_sym && c[:hidden].nil?
    
    # Some default limitations for virtual columns
    if type == :virtual
      # disable filters
      c[:with_filters].nil? && c[:with_filters] = false
      # disable sorting
      c[:sortable].nil? && c[:sortable] = false
      # read-only
      c[:read_only].nil? && c[:read_only] = true
    end
    
    # denormalize column (save space)
    c.reject{ |k,v| k == :name }.empty? ? c[:name] : c
  end
  
  columns_for_create
  
end

#default_context_menu(indep_config) ⇒ Object



214
215
216
217
218
# File 'lib/netzke/grid_panel.rb', line 214

def default_context_menu(indep_config)
  res = %w{ edit del }
  res << "-" << "edit_in_form" if indep_config[:ext_config][:enable_edit_in_form]
  res
end

#get_columnsObject



330
331
332
333
334
335
336
337
338
339
340
# File 'lib/netzke/grid_panel.rb', line 330

def get_columns
  if config[:persistent_config]
    persistent_config['layout__columns'] ||= default_columns
    res = normalize_array_of_columns(persistent_config['layout__columns'])
  else
    res = default_columns
  end

  # denormalize
  res.map{ |c| c.is_a?(Hash) && c.reject{ |k,v| k == :name }.empty? ? c[:name].to_sym : c }
end

#independent_configObject



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/netzke/grid_panel.rb', line 198

def independent_config
  res = super
  
  # Bottom bar
  if res[:ext_config][:bbar].nil?
    res[:ext_config][:bbar] = %w{ add edit apply del }
    res[:ext_config][:bbar] << "-" << "add_in_form" << "edit_in_form" if res[:ext_config][:enable_edit_in_form]
    res[:ext_config][:bbar] << "-" << "search" if res[:ext_config][:enable_extended_search]
  end
  
  # Context menu
  res[:ext_config][:context_menu] ||= default_context_menu(res)
  
  res
end

#initial_late_aggregateesObject



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/netzke/grid_panel.rb', line 261

def initial_late_aggregatees
  res = {}
  
  # Edit in form
  res.merge!({
    :edit_form => {
      :widget_class_name => "FormPanel",
      :persistent_config => true,
      :data_class_name => config[:data_class_name],
      :ext_config => {
        :bbar => false,
        :header => false,
        :mode => ext_config[:mode]
      }
    },
    
    :multi_edit_form => {
      :widget_class_name => "FormPanel",
      :persistent_config => true,
      :data_class_name => config[:data_class_name],
      :ext_config => {
        :bbar => false,
        :header => false,
        :mode => ext_config[:mode]
      }
    },
    
    :new_record_form => {
      :widget_class_name => "FormPanel",
      :persistent_config => true,
      :data_class_name => config[:data_class_name],
      :ext_config => {
        :bbar => false,
        :header => false,
        :mode => ext_config[:mode]
      },
      :record => config[:data_class_name].constantize.new
    }
  }) if ext_config[:enable_edit_in_form]
  
  # Extended search
  res.merge!({
    :search_panel => {
      :widget_class_name => "SearchPanel",
      :search_class_name => config[:data_class_name],
      :persistent_config => true,
      :ext_config => {
        :header => false, 
        :bbar => false, 
        :mode => ext_config[:mode]
      },
    }
  }) if ext_config[:enable_extended_search]
  
  res
end

#normalized_columnsObject

Normalized columns



326
327
328
# File 'lib/netzke/grid_panel.rb', line 326

def normalized_columns
  @normalized_columns ||= normalize_columns(columns)
end

#save_columns!Object

Stores modified columns in persistent storage



355
356
357
# File 'lib/netzke/grid_panel.rb', line 355

def save_columns!
  persistent_config[:layout__columns] = columns
end