Announcement

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

    #16
    So far as we know, in the absence of the declarations we recommend, there is not really enough information exposed from JPA to reliably make the determination of what the involved fields are, nor is it safe to decide that the developer *wants* to do things via ID, since the other approach (loading the subobject) is also viable in other situations.

    But yes, it's an area where an explicit guide of how to set up your JPA declarations would help.

    Comment


      #17
      Originally posted by Isomorphic
      So far as we know, in the absence of the declarations we recommend, there is not really enough information exposed from JPA to reliably make the determination of what the involved fields are, nor is it safe to decide that the developer *wants* to do things via ID, since the other approach (loading the subobject) is also viable in other situations.
      I'd be happy to help with a traversal algorithm, but it seems superficially straightforward to me: Referring to my first post in this thread, when the DataSource reads the field named "preferredAddress", the foreignKey attribute tells it that the foreign key should be retrieved by inspecting the field called "id" on the preferredAddress. (Actually calling customer.getPreferredAddress().getId() would be handled reflectively, as it appears the DataSource already handles such things.)

      I'm not sure I understand what you mean by the use case of "loading the subobject". I had understood that for anything that wasn't defined by one of the FieldTypes had to be loaded in an auxiliary DataSource to be intelligible anyway.

      Finally, on a distinct but related note, would it be possible to provide a JPA SpringEMFProvider that pulled a PersistenceUnit from Spring? I haven't been able to figure out how to write one, and it's keeping me from using the JPADataSource directly (I'm currently running through a DMI facade).

      Comment


        #18
        Okay, I put an adapter method getPreferredAddressId() on my Customer object that returns the explicit FK for the preferred PhysicalAddress record. My customer.ds.xml fields now look like this:

        Code:
        <field name="id" type="sequence" hidden="true" primaryKey="true" />
        <field name="givenName" type="text" required="true" />
        <field name="familyName" type="text" required="true" />
        <field name="preferredAddressId" type="integer" foreignKey="physicalAddress.id" />
        (My physicalAddress.ds.xml has an identical "id" field.) However, even though the correct ID shows up in the ListGrid, the default editor is a text editor (IntegerItem?) and not a SelectItem as you indicated should be automatically inserted. I can successfully manually set a SelectItem editor on it. (UPDATE: After some other changes, the field is now by default a SelectItem.)

        Obviously, however, I don't want to display the FK ID in the dropdown box; instead, I want to format the address as a one-liner. I tried using a FormItemValueFormatter on the SelectItem, but the parameters passed in to the formatter are the actual integer ID (as "value") and the Customer record. How can I query the physicalAddress DataSource to pull the address information into the formatter? (UPDATE: As I continue to work with the SelectItem, I'm getting lots of cases where the value is passed in null. When the dropdown is collapsed to a single line, the value is still the integer ID.)


        (I'd be more than happy to work with Isomorphic to produce some documentation for SmartGWT; your product is quite technically impressive, but it can be thoroughly frustrating to try to figure out how to make the API do what you intend, and neither the Quick Start nor the Javadocs provide enough coverage to predictably understand what SmartGWT will do. UPDATE: For example, I found some documentation for a similar use case for what I'm wanting to do at ListGridField#setOptionDataSource, of all places.)
        Last edited by chrylis; 29 Oct 2010, 00:38.

        Comment


          #19
          I think I can rephrase that more clearly:

          I want to use a CellFormatter and FormItemValueFormatter to provide a one-line formatted address, but the two relevant inputs to the formatter APIs are an untyped "value" and the Record representing the current row.

          I have successfully set the ListGridField displayField to "preferredAddress", and I get a Record jsObj for the row's preferred address passed in to the CellFormatter. I can then put that through a format function and have a nice display in the list.

          I can't for the life of me figure out what's going on with FormItemValueFormatter, though. If I set SelectItem.displayField, then the specified field gets passed into the formatter as "value". The documentation for PickList suggests that the titleField declared on the DataSource should be used, but I set 'titleField="line1"' in my .ds.xml, and it doesn't appear to be observed. Instead, sometimes the ID gets passed in as an Integer, and sometimes "value" is null.

          The actual Record is clearly available to the SelectItem, since I can use setPickListFields to define a custom ListGrid just for the dropdown. Is there any way to either get the SelectItem to pass the entire Record in to the formatter as its value or for the formatter to look up the Record from the DataSource?
          Last edited by chrylis; 29 Oct 2010, 01:14.

          Comment


            #20
            On the automatic conversion from physicalAddress to it's "id" - to clarify, it is a valid and useful option to load and edit the entire physicalAddress rather than it's id (and it's used in the Master-Detail Hibernate sample). Your field declaration explicitly points to an object field as the data to use. We wouldn't want to second guess an explicit declaration like that even though it also says that the field is a foreignKey.

            On the CellFormatter - see previous responses about using valueXPath to have additional, hidden fields which contain data from the related address. You can then access these in your CellFormatter because you are passed the Record.

            On the FormItemValueFormatter: you can get the currently selected record via getSelectedRecord(). If the field starts with a value, the related record is fetched automatically; however this is asynchronous, so expect to be called once when the record is not available, then once again when it's loaded.

            Comment


              #21
              Originally posted by Isomorphic
              On the automatic conversion from physicalAddress to it's "id" - to clarify, it is a valid and useful option to load and edit the entire physicalAddress rather than it's id (and it's used in the Master-Detail Hibernate sample). Your field declaration explicitly points to an object field as the data to use. We wouldn't want to second guess an explicit declaration like that even though it also says that the field is a foreignKey.
              After a little more playing, I think I understand where this is coming from. It's a design decision that isn't optimal for our specific use case, but the adapter's easy enough to write, and I have this part working beautifully (the update request sends exactly the necessary field, etc.).


              On the CellFormatter - see previous responses about using valueXPath to have additional, hidden fields which contain data from the related address. You can then access these in your CellFormatter because you are passed the Record.
              I got this working in a slightly different manner. When the displayField for a ListGridRecord is itself a databound type, the formatter gets passed the linked record as its "value" field. I can retrieve the address lines from this record and format accordingly.


              On the FormItemValueFormatter: you can get the currently selected record via getSelectedRecord(). If the field starts with a value, the related record is fetched automatically; however this is asynchronous, so expect to be called once when the record is not available, then once again when it's loaded.
              I couldn't quite follow this, and it's an area in which more documentation would be helpful. To start, FormItemValueFormatter#format takes an Object value, a ListGridRecord, and a row and column number. It's my understanding that the "record" parameter is the record currently being edited, which is the same record returned by getSelectedRecord. What I want to do is to have a ListGridField column whose underlying value is the integer ID and that has an optionDataSource. I want to be able to access the linked records from the optionDataSource--that is, I want the FormItemValueFormatter to access a Record containing the fields of "physicalAddress".

              When I call getSelectedRecord, I can access the Record object for the physicalAddress that is currently assigned to the row being edited, but the dropdown needs to iterate over all the possible values for the optionDataSource. The best way would be to have the optionDataSource's Records passed in to the FormItemValueFormatter (just like a physicalAddress Record is passed in to the CellFormatter as the value), but it would also be workable to be able to call addressDataSource.getRecord(4) and retrieve the candidate address that way. I can't see a way to do either.

              Comment


                #22
                getSelectedRecord() gets the record from the related DataSource (the one you indicate with your foreignKey declaration) which the user has selected, or which is indicated by the current value of the foreignKey field (see fetchMissingValues).

                You go on to mention iterating over all the related records - it's not clear if you actually want this or were just confused about getSelectedRecord(). If you do need access to all related records that have been loaded, it's available to you in the DataArrivedEvent.

                Comment


                  #23
                  I'm attaching a screenshot that may be more helpful in explaining what I'm trying to do.

                  Recap: "Customer" has a foreign-key field in the database pointing to an independently-existing "Address". We want to select the address from a dropdown and have the DataSource modify the customer's foreign key. This is all working correctly, by putting a method getPreferredAddressId on Customer that returns the integer key of the referenced address field. The ListGridField is configured like this (image 00):

                  Code:
                  ListGridField preferredAddressField = new ListGridField("preferredAddressId");
                  preferredAddressField.setDisplayField("preferredAddress");
                  preferredAddressField.setCellFormatter(new CellFormatter() {
                  	@Override
                  	// the "value" for this formatter is a JS Record object containing the "preferredAddress"
                  	public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                  		if(!(value instanceof JavaScriptObject))
                  			return String.valueOf(value);
                  		Record addressRecord = Record.getOrCreateRef((JavaScriptObject) value);
                  		if(addressRecord == null)
                  			return null;
                  		// this static method constructs a string out of the Record's attributes
                  		return Formats.physicalAddressOneLine(addressRecord);
                  	}
                  });
                  However, the underlying field for the ListGrid column is still of type INTEGER. If I use the default editor element, the edit dropdown looks like image 01: It displays the actual integer FK for the current selection, but all the dropdown elements are blank. Obviously, we need to put on a custom formatter because of the odd use case, so following the directions in the ListGridField Javadoc (rather an odd place), we set an optionDataSource on the form editor element so that it will match up the FK to the appropriate entry in the physicalAddress DataSource:

                  Code:
                  SelectItem addressSelector = new SelectItem();
                  addressSelector.setOptionDataSource(DataSource.getDataSource("physicalAddress"));
                  preferredAddressField.setEditorType(addressSelector);
                  This has exactly the same results: shows the FK for the current selection and blanks for everything else. The expected behavior would be to either use the physicalAddress titleField, which is the first address line, or pass in the physicalAddress Record, which would show up as "[object Object]". This is what I get if I explicitly set the displayField to null (image 02):

                  Code:
                  addressSelector.setDisplayField(null);

                  This is progress, but what I want to display is the same formatted address that we see in the other address lines. In order to do this, the SelectItem's formatter needs to be able to read each Record that the SelectItem is trying to display. The value that's passed into the formatter isn't the actual physicalAddress Record object, like for the CellFormatter above, but rather the single line1 String. Using getSelectedRecord in the formatter returns the Record that's currently in use for the row being edited, but not the record corresponding to the dropdown entry (image 03).

                  What I've been asking for is a way for the FormItemValueFormatter, which is supposed to format each dropdown entry individually, to access the Record object corresponding to the respective entry. The preferred way would be to have the Record objects from the optionDataSource passed into formatValue as the "value" parameter, just like they are to the CellFormatter above. Alternately, when the "value" parameter is the integer representing the FK (i.e., when a displayField isn't set at all), it would be acceptable to be able to look up the Record from the optionDataSource by primary key, using something like this:

                  Code:
                  Record addressRecord = DataSource.getDataSource("physicalAddress").getRecord(value);
                  return Formats.physicalAddressOneLine(addressRecord);
                  It doesn't currently appear like either option is possible, but it's really hard to tell from the Javadocs important details like how to know what is going to be passed into formatValue as the "value" parameter (a Record object, an Integer FK, a String for the titleField, etc.), and given the flexibility in the rest of SmartGWT, it seems like it should be possible.

                  When I don't
                  Attached Files
                  Last edited by chrylis; 4 Nov 2010, 20:57.

                  Comment


                    #24
                    Read the docs for ListGridField.optionDataSource. Unless the number of addresses is reliably small, you want to deliver the display value for the address as an additional field - the displayField.

                    This sample shows how you could do this with a SQL join.

                    Comment


                      #25
                      Originally posted by Isomorphic
                      Read the docs for ListGridField.optionDataSource. Unless the number of addresses is reliably small, you want to deliver the display value for the address as an additional field - the displayField.
                      I had read the documentation, and that's why, as recommended, I wasn't using ListGridField.optionDataSource; I was using SelectItem.optionDataSource. The reason I would prefer not to use the displayField is precisely that all of the information in the record is used in that field, and I was trying to avoid round-tripping.

                      This sample shows how you could do this with a SQL join.
                      I examined this, and it's something of a last-resort option. At this point, I'm almost more interested in this as a framework issue: Since the CellFormatter gets the Record as the "value" parameter, why is is that the FormItemValueFormatter gets null with the exact same setup? Why not pass in the JS handle to the Record?

                      Comment


                        #26
                        This is a straightforward use case that we implement all the time and which is demonstrated in multiple samples. It's not clear what's wrong for you, but you do seem to keep expecting APIs to do things they are not documented to do, so let's recap..

                        For display in the grid, use a server-side join, valueXPath, or whatever other server-side means such that each record contains both the stored FK value and a corresponding display value in the displayField. There is no other way for this to work. The stored and displayed value either has to be present in each record or the framework will need to fetch all data via listGridField.optionDataSource.

                        The FormItemValueFormatter will be passed the record being edited as the Record param, and has access to the related record via getSelectedRecord() as well. If you're seeing a blank list of options, that's either blank data being returned (check the RPC tab in the Developer Console), mismatched field names, or some similar problem.

                        Fundamentally you can see that this whole pattern works fine, because it's shown in several samples, and the only variation you've got is your custom backend code, which doesn't affect client-side behavior once data has been delivered to the browser.

                        Comment


                          #27
                          Have you been able to get this work yet? If you have any doubts about framework APIs, you can always add a custom FormItemValueFormatter and getSelectedRecord() call to the Large Value Map sample, so that you can see that it works fine as soon as your backend is delivering the expected data.

                          Comment

                          Working...
                          X