propellerlabsio / meteor-ui5-website

This is the code for the Meteor UI5 Website, a website for demonstrating and documenting various Meteor UI5 packages.
4 stars 1 forks source link

Syntax for new "lookup" concept in property binding #5

Closed proehlen closed 8 years ago

proehlen commented 8 years ago

SAP OData models allow you to define navigation paths to quickly and easily lookup values in another entity. However they require these navigation properties to be defined beforehand in SEGW.

I've been experimenting with providing something similar in Meteor UI5 however I don't want to have to rely on a schema having to be predefined. I've come up with the following which would allow you to fetch a corresponding record from another collection provided you have a field with the id value (a pretty common scenario).

Example

Below is two sample documents from different collections, the XML with the lookup feature and the output it would produce.

Document in Orders collection

{
    "_id" : "10248",
    "OrderID" : 10248,
    "CustomerID" : "VINET",
    "EmployeeID" : 5,
    "ShipVia" : 3,
    "Freight" : "32.3800",
    "ShipCity" : "Reims",
    "ShipCountry" : "France",
    "Items" : [ 
        {
            "ProductID" : 11,
            "UnitPrice" : "14.0000",
            "Quantity" : 12
        }, 
        {
            "ProductID" : 42,
            "UnitPrice" : "9.8000",
            "Quantity" : 10
        }, 
        {
            "ProductID" : 72,
            "UnitPrice" : "34.8000",
            "Quantity" : 5
        }
    ]
}

Document in Customers collection

{
    "_id" : "VINET",
    "CustomerID" : "VINET",
    "CompanyName" : "Vins et alcools Chevalier",
    "ContactName" : "Paul Henriot",
    "ContactTitle" : "Accounting Manager",
    "Address" : "59 rue de l'Abbaye",
    "City" : "Reims",
    "Region" : null,
    "PostalCode" : "51100",
    "Country" : "France",
    "Phone" : "26.47.15.10",
    "Fax" : "26.47.15.11"
}

XML

  <ObjectHeader title="Order {_id}"
      binding="{/Orders(10248)}"
      number="{= ${Items}.length}"
      numberUnit="Items">
    <attributes>
      <ObjectAttribute title="Customer ID"
          text="{CustomerID}" />
      <ObjectAttribute title="Customer Name"
          text="{/Customers(CustomerID)/CompanyName}" />
      <ObjectAttribute title="City"
          text="{ShipCity}" />
      <ObjectAttribute title="Country"
          text="{ShipCountry}" />
    </attributes>
  </ObjectHeader>

Output

oneorder

All of the above property bindings already work and are standard/consistent with their OData and JSON model counterparts with the exception of the CompanyName which is fetched via a lookup on the Customers collection since the data isn't in the Orders collection. The temporary syntax I've come up with is:

{/Customers(CustomerID)/CompanyName}

Where:

For this particular scenario, I've debugged the model code and am confident that it is relatively easy to implement.

Pros

The proposed syntax is similar to the existing syntax for regular context binding (see the ObjectHeader binding property above.

Cons

Usually a leading forward slash has meaning in UI5 - i.e. it denotes a non-relative binding however we are using it within a relative binding.

I don't know if we're at risk of either a) it not working in every scenario, or, b) SAP making some change to their api which would break this syntax. I'm actually quite concerned about this since a lot of path parsing is already happening in the model (both my and SAP's code) and is relying on forward slashes.

Alternatives

We could use another character instead of the forward slash. E.g.:

We could change it so that the lookup becomes a sub property after the property we already have. E.g.:

Something else?

proehlen commented 8 years ago

Actually, we could use a "?" symbol. In regular urls it signifies a query parameter. If we use it to denote a lookup, conceptually it's similar.

proehlen commented 8 years ago

This is the info page I've put together for the Property Lookups demo. It's based on using the above '?' syntax but it can be easily changed. It probably explains the concept better:

Property lookups

Our Orders collection has the CustomerID property for each document, but supposed you'd rather show the customer name instead of the number. The UI5 standard OData model provides for this via the $expand keyword. However, for each individual use case, this needs to be enabled and coded on the server.

In Meteor UI5 we provide an alternative approach called Lookups whereby the UI5 developer can define lookup queries on related collections directly in the property binding.

Syntax

In our earlier 'One Order' example, we showed the customer id value using the property binding text="{CustomerID}".

Since we have the CustomerID field which uniquely indentifies a document in the Customers collection, we can instead do a lookup on that collection and return any property from the Customer document. To do that we would use the binding {?Customers(CustomerID)/CompanyName}.

The following table breaks down the components of this lookup:

Component Description
? The question mark in the first position denotes that the property value is derived from a lookup and not a property in the current context.
Customers The name of the Mongo database collection we wish to query.
(CustomerID) The property in the current context that has the unique id of the document that we want.
/CompanyName The property from the the Customers collection that we want to show.

Subscriptions

Note, if using Meteor publications and subscriptions, it is still necessary to ensure the front-end has the data it requires. Querying the Customers collection in a lookup will not return any data if the front-end doesn't have the relevant Customer document.

The reywood:publish-composite package can be useful for ensuring all related data is published at the same time. We use this in our demo server code to publish related Customer documents for each Order that we publish. Refer to the Publishing Relational Data section in the Meteor Guide for more information.

This demo

The code in this demo is nearly identical to the 'One Order' demo however, now, instead of showing the CustomerId value from the Order, we use a lookup to get the CompanyName from the Customers collection.

<ObjectHeader binding="{/Orders(10248)}">
  <attributes>
    <ObjectAttribute title="Customer"
        text="{?Customers(CustomerID)/CompanyName}" />
    <!-- Add additional ObjectHeader attributes as required. -->
  </attributes>
</ObjectHeader>
proehlen commented 8 years ago

@dguess Closing this since I pressed ahead with the syntax in the last comment and it works. Happy to change the syntax after you've had a chance to play with / use it and if you decide you don't like it. Can change it easily any time.