Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    How do I best manage a "time-balance" field in SmartGWT?

    This is related to my other question, but it's a different case so I'm starting my own thread:

    I have a field in the database which is an integer (like in the other thread), but this one is representing a timebank balance. I.e. 2 hours and 10 minutes more than expected , or 29 minutes less than expected.

    So, it can be a negative integer (in the database they are stored as seconds, so 8940 and -1740 in the two examples above.

    Here's what I would like to happen:
    The user can see and edit this on a "time" format. I.e see and enter 2:29 or -00.27.

    On the server, I am translating this integer in the database to and from a java.time.Duration, but from what I've found in the docs, there is no support for that in smartGWT?

    Of course, I could just have the integer field and ask the users. to "enter the balance in minutes", but that's not a great user experience.


    You guys are wizards, so perhaps you have a suggestion as to how I could handle this?

    #2
    Correct, we don't have a "duration" type as a built-in.

    If this is going to appear even twice in your application, we'd recommend implementing it as a custom SimpleType. This allows you to define custom formatting, editing and filtering rules for your type that will be reused any time you declare a field of that type.

    It would be somewhat tempting to use inheritsFrom:"time" and use a Date object on the client to represent the duration, but that doesn't really work that well for your negative durations, and could only represent durations up to 24 hours, so we'd recommend just inheriting from "integer".

    You can then define a custom editor for the type - you could use a CanvasItem that uses a TimeItem for editing, with various overrides. However, we wouldn't recommend trying to "fight" the TimeItem to get it to allow a "-" into the field. If you want to use TimeItem, the negative vs positive should be a toggle (like perhaps a RadioGroupItem) external to the TimeItem.

    If you prefer to allow the "-" to be typed right in, then just base your editor on TextItem, possibly with no intervening CanvasItem, although the CanvasItem APIs give you a very nice & clear place to transform the typed-in value to the stored integral value.

    Comment


      #3
      Hi, thanks for responding.

      It's kind of "famous last words" but I really think this is a one-off.

      I suppose what you're recommending, then, is a custom CanvasItem, or TextEditor with a custom formatter? But are there no good callbacks for a TextItem to do setAttribute for the actual "balance" background attribute?

      Comment


        #4
        It's actually probably worthwhile to capture it as a SimpleType even if there is only one such field ever, if the field can appear in multiple different DBCs (for example, in a ListGrid, but also in a DynamicForm).

        SimpleType's parseInput() and editFormatter() APIs provide the ability to transform between typed-in values and an underlying value.

        Comment


          #5
          Is there example code somewhere for how to best do this? Didn't find anything in the showcase. Just trying to save some time :)

          Comment


            #6
            There isn't an example of something like a custom SimpleType (like "duration"). There are two CanvasItem samples that show the general concept of having an editor that stores a value different from what is typed in, but you've already seen those, we think.

            Comment


              #7
              OK. so I ended up doing a custom TextItem, with formatters up and down from the underlying integer seconds value. see code at the end if interested.

              However, I'm having trouble with String masking and validation. I want to force entering the value on (-)HH:mm, with it being possible to enter -1:50 for example.

              I have fixed a working regexp for this, but cant get it to validate, nor work as a field mask.

              my regexp is
              Code:
              private String pattern = "^-?\\d+:[0-5]\\d$";
              but it never validates. I also can't seem to get it working as a mask.

              so, 2 questions:

              1. how do I make the regexp work as form item validation? see my attempt below.
              2. can I get this regexp to work as a field mask? I saw your example, but I didn't quite understand If I also could use a "normal" regexp?

              EDIT:
              My dream scenario. The above regexp works, so that texts like -100:59, 1:30 and -10:50 work, and *also* I get a item mask like in the time item mask, so that the ":" is automatically entered, but also can lead with an optimal "-"

              code:
              Code:
              import com.google.gwt.core.client.GWT;
              import com.smartgwt.client.data.DataSourceField;
              import com.smartgwt.client.widgets.form.fields.IntegerItem;
              import com.smartgwt.client.widgets.form.fields.TextItem;
              import com.smartgwt.client.widgets.form.validator.RegExpValidator;
              
              /**
               * No comments for you.
               * It was hard to write, so it
               * should be hard to read.
               *
               */
              public class BalanceFormItem extends TextItem {
              
                  private String pattern = "^-?\\d+:[0-5]\\d$";
              
              
                  private IntegerItem balanceField;
              
                  private DataSourceField theField;
              
                  public BalanceFormItem(DataSourceField theField) {
                      super(theField.getName(), theField.getTitle());
                      this.theField = theField;
              
                      setEditorValueFormatter((o, record, dynamicForm, formItem) -> {
                          Integer val = parseInt(o);
                          GWT.log("formatValue " + o + ", int: " + val);
                          return secondsToHoursMinutes(val);
                      });
              
                      setEditorValueParser((s, dynamicForm, formItem) -> {
                          int secs = parseValueToInt(s);
                          GWT.log("parseValue " + s + ", becomes: " + secs);
                          return secs;
                      });
              
                      setMask(pattern);//not working
              
                      //validator not working
                      RegExpValidator matchesValidator = new RegExpValidator(pattern);
                      matchesValidator.setErrorMessage("error");
                      setValidators(matchesValidator);
              
                  }
              
                  private String secondsToHoursMinutes(int seconds) {
                      boolean isNegative = seconds < 0;
                      int absSeconds = Math.abs(seconds);
                      int hours = absSeconds / 3600;
                      int minutes = (absSeconds % 3600) / 60;
              
                      return (isNegative ? "-" : "") + formatTime(hours, minutes);
                  }
              
                  private String formatTime(int hours, int minutes) {
                      return padNumber(hours) + ":" + padNumber(minutes);
                  }
              
                  private String padNumber(int number) {
                      return (number < 10) ? "0" + number : String.valueOf(number);
                  }
              
                  public int parseInt(Object obj) {
                      if (obj != null) {
                          try {
                              return Integer.parseInt(obj.toString());
                          } catch (Exception e) {
                              // Handle exception or log it
                          }
                      }
                      return 0; // Default value if obj is null or not a valid integer
                  }
              
                  public int parseValueToInt(String time) {
                      boolean isNegative = time.startsWith("-");
                      if (isNegative) {
                          time = time.substring(1); // Remove the negative sign
                      }
              
                      String[] parts = time.split(":");
                      int hours = Integer.parseInt(parts[0]);
                      int minutes = Integer.parseInt(parts[1]);
              
                      int totalSeconds = hours * 3600 + minutes * 60;
                      return isNegative ? -totalSeconds : totalSeconds;
                  }
              }
              Last edited by mathias; 23 Jan 2024, 23:31.

              Comment


                #8
                Just a quick response here - we'll see if we can pull together some time to actually give you correct replacement code:

                1) you have to consider the multiple layers of "quoting" involved when specifying your regexp as a Java String. It's a good idea to just log out the value so you can see how it ends up

                2) if you want to validate based on a regexp, and have the value transformed when it matches the regexp, you need the MaskValidator, which allows you to specify both the regexp to match and a "transformTo" string:

                https://smartclient.com/smartgwt/jav...Validator.html
                https://smartclient.com/smartclient-...ValueTransform

                3) FormItem.mask forcing typing in a particular format, and doesn't deal with optional characters, so it's not going to work well with your optional "-" and the possibility of single vs multiple digit hour or minute values. So you probably don't want to use it here, once you've applied MaskValidator, which will allow more freeform input



                Comment


                  #9
                  Yeah, that would be super great, since I'm at a loss right now, pretty frustrating tbh.

                  1. Not 100% sure what you mean. Just log the value? I did this and it prints the same:
                  Code:
                  private String pattern = "^-?\\d+:[0-5]\\d$";
                  
                  MaskValidator val = new MaskValidator();
                  val.setMask(pattern);
                  GWT.log("pattern: " + val.getMask());
                  the printout is:
                  pattern: ^-?\d+:[0-5]\d$
                  which seems correct?

                  2. Not sure I get this either. I have those two methods to transform to and from an integer seconds value that work fine, but in the javadoc it says
                  This should be set to a string in the standard format for string replacement.
                  It's not possible to define a transform pattern that transforms from a "10:00" string to a 36000 int, I need to do calculations.

                  3. Right ok. that brings me back to the regexp not working I guess.
                  Last edited by mathias; 24 Jan 2024, 00:36.

                  Comment


                    #10
                    Hi there, just following up on this, you had any time to look at this issue?

                    Comment

                    Working...
                    X