Error-Handling: Bei AJAX-Requests Fehler als JSON-Object zurückgeben
authorMoritz Bunkus <m.bunkus@linet-services.de>
Thu, 7 Mar 2013 11:17:06 +0000 (12:17 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Thu, 7 Mar 2013 12:12:27 +0000 (13:12 +0100)
SL/ClientJS.pm
SL/Dispatcher.pm
SL/Form.pm
js/client_js.js
scripts/generate_client_js_actions.tpl

index 99598af..6a458be 100644 (file)
@@ -9,10 +9,13 @@ use SL::JSON ();
 
 use Rose::Object::MakeMethods::Generic
 (
-  'scalar --get_set_init' => [ qw(_actions) ],
+  'scalar --get_set_init' => [ qw(_actions _flash _error) ],
 );
 
 my %supported_methods = (
+  # ## Non-jQuery methods ##
+  flash        => 2,            # display_flash(<TARGET>, <ARGS>)
+
   # ## jQuery basics ##
 
   # Basic effects
@@ -123,8 +126,18 @@ sub init__actions {
   return [];
 }
 
+sub init__flash {
+  return {};
+}
+
+sub init__error {
+  return '';
+}
+
 sub to_json {
   my ($self) = @_;
+
+  return SL::JSON::to_json({ error        => $self->_error   }) if $self->_error;
   return SL::JSON::to_json({ eval_actions => $self->_actions });
 }
 
@@ -144,6 +157,29 @@ sub jstree {
   return $self;
 }
 
+sub flash {
+  my ($self, $type, @messages) = @_;
+
+  my $message = join ' ', grep { $_ } @messages;
+
+  if (!$self->_flash->{$type}) {
+    $self->_flash->{$type} = [ 'flash', $type, $message ];
+    push @{ $self->_actions }, $self->_flash->{$type};
+  } else {
+    $self->_flash->{$type}->[-1] .= ' ' . $message;
+  }
+
+  return $self;
+}
+
+sub error {
+  my ($self, @messages) = @_;
+
+  $self->_error(join ' ', grep { $_ } ($self->_error, @messages));
+
+  return $self;
+}
+
 1;
 __END__
 
@@ -301,6 +337,29 @@ Instead of:
 
 The first variation is obviously better suited for chaining.
 
+Additional functions:
+
+=over 4
+
+=item C<flash $type, $message>
+
+Display a C<$message> in the flash of type C<$type>. Multiple calls of
+C<flash> on the same C<$self> will be merged by type.
+
+On the client side the flash of this type will be cleared before the
+message is shown.
+
+=item C<error $message>
+
+Causes L<to_json> (and therefore L<render>) to output a JSON object
+that only contains an C<error> field set to this C<$message>. The
+client will then show the message in the 'error' flash.
+
+The messages of multiple calls of C<error> on the same C<$self> will
+be merged.
+
+=back
+
 =head2 JQUERY FUNCTIONS
 
 The following jQuery functions are supported:
index 5282369..828dc1f 100644 (file)
@@ -30,6 +30,7 @@ use SL::Dispatcher::AuthHandler;
 use SL::LXDebug;
 use SL::LxOfficeConf;
 use SL::Locale;
+use SL::ClientJS;
 use SL::Common;
 use SL::Form;
 use SL::Helper::DateTime;
@@ -74,6 +75,14 @@ sub pre_request_checks {
   }
 }
 
+sub render_error_ajax {
+  my ($error) = @_;
+
+  SL::ClientJS->new
+    ->error($error)
+    ->render(SL::Controller::Base->new);
+}
+
 sub show_error {
   $::lxdebug->enter_sub;
   my $template             = shift;
@@ -85,6 +94,8 @@ sub show_error {
   $::form->{error}         = $::locale->text('The session is invalid or has expired.') if ($error_type eq 'session');
   $::form->{error}         = $::locale->text('Incorrect password!')                    if ($error_type eq 'password');
 
+  return render_error_ajax($::form->{error}) if $::request->is_ajax;
+
   $::form->header;
   print $::form->parse_html_template($template, \%params);
   $::lxdebug->leave_sub;
@@ -272,10 +283,16 @@ sub handle_request {
     1;
   } or do {
     if ($EVAL_ERROR ne END_OF_REQUEST) {
-      print STDERR $EVAL_ERROR;
-      $::form->{label_error} = $::request->{cgi}->pre($EVAL_ERROR);
-      chdir SL::System::Process::exe_dir;
-      eval { show_error('generic/error') };
+      my $error = $EVAL_ERROR;
+      print STDERR $error;
+
+      if ($::request->is_ajax) {
+        eval { render_error_ajax($error) };
+      } else {
+        $::form->{label_error} = $::request->{cgi}->pre($error);
+        chdir SL::System::Process::exe_dir;
+        eval { show_error('generic/error') };
+      }
     }
   };
 
index 3c809b3..d130272 100644 (file)
@@ -704,6 +704,14 @@ sub show_generic_error {
     return;
   }
 
+  if ($::request->is_ajax) {
+    $::lxdebug->message(0, "trying to render AJAX response...");
+    SL::ClientJS->new
+      ->error($error)
+      ->render(SL::Controller::Base->new);
+    ::end_of_request();
+  }
+
   my $add_params = {
     'title_error' => $params{title},
     'label_error' => $error,
index 08b9b72..fa116dd 100644 (file)
@@ -4,10 +4,21 @@
 // "scripts/generate_client_js_actions.pl". See the documentation for
 // SL/ClientJS.pm for instructions.
 
+function display_flash(type, message) {
+  $('#flash_' + type + '_content').text(message);
+  $('#flash_' + type).show();
+}
+
 function eval_json_result(data) {
   if (!data)
     return;
 
+  if (data.error)
+    return display_flash('error', data.error);
+
+  $('#flash_error').hide();
+  $('#flash_error_content').empty();
+
   if ((data.js || '') != '')
     eval(data.js);
 
@@ -15,9 +26,13 @@ function eval_json_result(data) {
     $(data.eval_actions).each(function(idx, action) {
       // console.log("ACTION " + action[0] + " ON " + action[1]);
 
+      // ## Non-jQuery methods ##
+           if (action[0] == 'flash')                display_flash(action[1], action[2]);
+
       // ## jQuery basics ##
+
       // Basic effects
-           if (action[0] == 'hide')                 $(action[1]).hide();
+      else if (action[0] == 'hide')                 $(action[1]).hide();
       else if (action[0] == 'show')                 $(action[1]).show();
       else if (action[0] == 'toggle')               $(action[1]).toggle();
 
@@ -94,3 +109,7 @@ function eval_json_result(data) {
 
   // console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
 }
+
+// Local Variables:
+// mode: js
+// End:
index 6572e22..7fa7baf 100644 (file)
@@ -4,10 +4,21 @@
 // "scripts/generate_client_js_actions.pl". See the documentation for
 // SL/ClientJS.pm for instructions.
 
+function display_flash(type, message) {
+  $('#flash_' + type + '_content').text(message);
+  $('#flash_' + type).show();
+}
+
 function eval_json_result(data) {
   if (!data)
     return;
 
+  if (data.error)
+    return display_flash('error', data.error);
+
+  $('#flash_error').hide();
+  $('#flash_error_content').empty();
+
   if ((data.js || '') != '')
     eval(data.js);
 
@@ -20,3 +31,7 @@ function eval_json_result(data) {
 
   // console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
 }
+
+// Local Variables:
+// mode: js
+// End: