Explore the OData hyperlink within BreezeJS

I've been working on integrating the BreezeJS library with an SAP OData service. I have successfully managed to read entities, but I'm facing issues when trying to resolve linked objects.

The EntityType I am dealing with is OrgObject.

<EntityType Name="OrgObject" sap:content-version="1">
  <!-- ... -->
  <NavigationProperty Name="Children" Relationship="ZGW_ORGSTRUCTURE.OrgObject_To_Children" FromRole="FromRole_OrgObject_To_Children" ToRole="ToRole_OrgObject_To_Children"/>
</EntityType>

There is a link available to resolve all linked OrgObjects (named Children).

<Association Name="OrgObject_To_Children" sap:content-version="1">
  <End Type="ZGW_ORGSTRUCTURE.OrgObject" Multiplicity="1" Role="FromRole_OrgObject_To_Children"/>
  <End Type="ZGW_ORGSTRUCTURE.OrgObject" Multiplicity="*" Role="ToRole_OrgObject_To_Children"/>
</Association>

Despite this, the breeze query works:

var query = new breeze.EntityQuery().from("OrgObjects");
manager.executeQuery(query).then(function(data) {
  data.results.forEach(function(item) {
    console.log(item);
  });
}).fail(/*...*/);

But how can I access the 'children' from this object?

Attempt 1:

var query = new breeze.EntityQuery().from("OrgObjects");
manager.executeQuery(query).then(function(data) {
  data.results.forEach(function(item) {
    console.log(item);
    // ...
    var Children = item.Children();
    // ...
  });
}).fail(/*...*/);

This attempt leads to an error:

message: "Object [object Object] has no method 'children'"

Attempt 2:

var query = new breeze.EntityQuery().from("OrgObjects");
manager.executeQuery(query).then(function(data) {
  data.results.forEach(function(item) {
    console.log(item);
    // ...
    item.entityAspect.loadNavigationProperty("Children").then(function(data) {
      console.log(data.results);
      data.results.forEach(function(item) {
        console.log(item);
      });
    }).fail(function(e) {
      console.log(e);
    });
    // ...
  });
}).fail(/*...*/);

This approach results in another error:

The 'propertyOrExpr' parameter must be a 'string'

Attempt 3:

var query = new breeze.EntityQuery().from("OrgObjects").expand("Children");
manager.executeQuery(query).then(function(data) {
  data.results.forEach(function(item) {
    console.log(item);
    // ...
    console.log(item.Children);
    console.log( item.Children.length );
    // ...
  });
}).fail(/*...*/);

Result: item.Children is an object. However, item.Children.length = 0. Although the children are fetched from the server according to the http response, they are not available in the item.Children object.

Console output:

Finance Department
[parentEntity: Object, navigationProperty: ctor, arrayChanged: ctor, _addsInProcess: Array[0], push: function…]
    _addsInProcess: Array[0]
    _getEventParent: function () {
    _getPendingPubs: function () {
    arrayChanged: ctor
    length: 0
    load: function (callback, errorCallback) {
    navigationProperty: ctor
    parentEntity: Object
    pop: function () {
    push: function () {
    shift: function () {
    splice: function () {
    unshift: function () {
    __proto__: Array[0]
0

Could someone provide assistance? Is there something lacking in my OData service configuration?

Answer №1

The issue has been marked as "resolved".

In the realm of Breeze, one-to-many relationships can only be utilized if the inverse property (many-to-one) is explicitly defined. However, it appears that in SAP Gateway, defining an inverse relationship is not supported. In order to investigate this limitation, I established 2 associations:

<Association Name="OrgObject_To_Children" sap:content-version="1">
    <End Type="ZGW_ORGSTRUCTURE.OrgObject" Multiplicity="1" Role="FromRole_OrgObject_To_Children"/>
    <End Type="ZGW_ORGSTRUCTURE.OrgObject" Multiplicity="*" Role="ToRole_OrgObject_To_Children"/>
</Association>

<Association Name="OrgObject_To_Children_inverse" sap:content-version="1">
    <End Type="ZGW_ORGSTRUCTURE.OrgObject" Multiplicity="*" Role="FromRole_OrgObject_To_Children_inverse"/>
    <End Type="ZGW_ORGSTRUCTURE.OrgObject" Multiplicity="1" Role="ToRole_OrgObject_To_Children_inverse"/>
</Association>

Subsequently, adjustments were made within the Breezejs framework:

function updateCrossEntityRelationship(np) {
    // Function logic for updating cross-entity relationships
}

Despite these modifications, further changes were necessary to achieve full functionality. Whether this discrepancy is attributable to a bug or SAP Gateway compatibility remains to be determined.

function mergeRelatedEntitiesCore(rawEntity, navigationProperty, parseContext) {
    // Function logic for merging related entities
}

For now, these alterations have been validated solely for read operations, with plans to evaluate their impact on update functionalities at a later stage.

Regards, Joachim

Answer №2

Although I don't have a deep understanding of Breeze, I can provide some insights into what might be going on.

Based on information from the Breeze documentation regarding navigation properties, it appears that lazy-loading of navigation properties is not supported. This means that if the data for item.Children() isn't already present locally, it won't automatically retrieve the data. To have this information available locally, you must explicitly request the Children data to be included in the payload using inline retrieval methods. In general OData terms, this can be achieved by adding the $expand query parameter in the URL request. As outlined in the Breeze documentation I referenced earlier, in Breeze it can be accomplished using the .expand() method like this:

var query = new breeze.EntityQuery()
              .from("OrgObjects")
              .expand("Children");

Answer №3

Hey @Jowa, big shoutout to you for your hard work in trying to bridge the gap between SAP and BreezeJS.

Your groundbreaking discovery of using expand to eagerly load the children of the parent entity OrgObject is truly impressive.

While it's possible to load them on-demand with loadNavigationProperty, doing so individually for each query result would be highly inefficient. This is where utilizing expand becomes essential.

Breeze seems to be facing challenges with your navigation property due to its inability to identify the foreign key within the child entity that establishes the relationship.

In scenarios involving a 1..m relationship, it's not mandatory to have navigation properties defined on both sides. Typically, the 1->N side (parent->child) property is the one overlooked - like Gender.Person being omitted to prevent male.Persons from cluttering the collection.

In your particular scenario, if I grasp it correctly, you believe that the 1->N relationship (OrgObject.Children) is configured but not the N->1 navigation (Child.OrgObject). And hey, that arrangement should still function seamlessly!

Nevertheless, to enable navigation properties in either direction, the metadata must accurately define the Foreign Key (FK) property within the child entity type.

It appears that Breeze is currently lacking this vital information for some reason.

Although resorting to modifying BreezeJS directly may seem like a clever workaround, ideally, it shouldn't be required.

If there's anything we can do to assist, kindly furnish us with the following details:

  • Detailed server-side class definition of Child showcasing its OrgObject FK property.
  • The metadata related to OrgObject and Child stored in the metadataStore object.
  • The JSON pertaining to OrgObject and Child from the metadata file transmitted over the network.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Converting Promises to Observables

Struggling with the syntax as I delve into learning Angular, I need to transform a promise into an Observable. Let me share what I've encountered: In the function getCountries (subscribed by another utility), there is a call required to fetch a list ...

Efficiently Minimize Bootstrap Components Upon Clicking the Link

I've successfully created a navigation menu that expands and collapses without using a dropdown feature. However, I'm encountering an issue where I can't seem to toggle the div when clicking on a menu link. I attempted to use JavaScript to c ...

Creating a Cubic Bezier Curve connecting two points within a 3D sphere using three.js

I'm currently working on a project where the user can click on two points on a sphere and I want to connect these points with a line along the surface of the sphere, following the great circle path. I have managed to obtain the coordinates of the sele ...

Triggering an event through a shared messaging service to update the content of a component

I'm looking for a simple example that will help me understand how I can change the message displayed in my component. I want to trigger a confirmation box with *ngIf and once I confirm the change, I want the original message to be replaced with a new ...

Element UI: Triggering an event when the sort caret is clicked

Is it possible to trigger an event when the sorting carets are clicked on a table with sortable columns, ideally with the same parameters as the header-click event? I am able to emit an event by clicking on the header of any sortable column (header-click) ...

How to format numbers with commas in AngularJS to make them easier to read

One of my variables looks like this: $scope.numbers = 1234567. When I apply the filter {{numbers| number : 0}}, the result is 1,234,567. Is it possible to get a result like 12,34,567 instead? Thank you in advance. ...

Guidelines for validating email input using jQuery

Although I am not utilizing the form tag, you can still achieve form functionality using jQuery Ajax. <input type="email" placeholder="Email" name="email" /> <input type="password" placeholder="Password ...

Looping through elements using .each in jQuery and accessing their values in the following iteration

Here is the code snippet I've been working on: var index=0; var load=0; $("td.load_ads").each(function(){ var loading=$(this); $.post('/self_coded_helpers/jpost_get_ads.php',{index:index,type:'fetch_id' ...

Preventing bots and spiders from infiltrating the ad network. Stepping up efforts to block unwanted traffic

We are facing a constant battle against bots and spiders with our in-house ad system, striving for 100% valid impressions. To achieve this goal, I conduct experiments on a specific ad zone that is only displayed on one page of our site. By comparing the G ...

"Emphasize menu items with an underline as you navigate through the

I am using Gatsby with React and have a navigation menu with links. I would like to make it so that when a link is clicked, a border bottom appears to indicate the current page, rather than only on hover. <ul className="men" id="menu"> ...

What is the process for transferring npm package files to my local project?

Despite my efforts to find the answer, I couldn't locate it and so here I am asking again. What I'm looking for: I need to move a file from node_modules to my project in order to work on it. First attempt: I moved the file I wanted to edit An ...

code, scripting - modal pop-up

My script currently has a popup window using window.open, but most browsers block this type of popups. I now want to change it to another popup that includes a php script, similar to what I've seen on other sites. It seems like they are using ajax. Ca ...

Tips for incorporating a favicon in a React application

Having trouble adding a favicon to my React application. I followed the instructions in this post, but it's not working for me. I placed the favicon.ico file inside the public folder where index.html resides. This is how my directory structure looks ...

When a webpage reload triggers a 404 error, my website's iframe with the source "videoUrl | sanitize" will display

I am attempting to retrieve a videoUrl from a database and set it to the iframe [attr.src] in order to display a YouTube video. It is imperative that the data originates from the database, as there are limitations preventing us from directly uploading and ...

What is the best way to merge setInterval with mouseenter events?

I have successfully implemented code that refreshes a div using ajax. However, I am looking to add functionality so that the div only refreshes every 30 seconds when the tab is active. It seems that setInterval currently refreshes the div regardless of tab ...

How to display an element using an if statement in Next.js

I am attempting to incorporate a parameter into a nextJS component that will only display if a certain condition is met. At the moment, my code looks like this: return ( <div role="main" aria-label={this.props.title} classN ...

Ways to assign the output of a function to a variable in JavaScript

I am looking to update the value of my function to a variable. As an example, I have the following: <input v-model="search.steering" @input="onChange('steering')"/> This means that I want to insert the steering every time I input text. ...

What is the best way to secure the installation of python packages for long-term use while executing my code within a python shell on NodeJS?

I have been encountering difficulties while attempting to install modules such as cv2 and numpy. Although I have come across a few solutions, each time the shell is used the installation process occurs again, resulting in increased response times. Below i ...

Is it considered safe to modify variables by using this[varName] = something within a function that includes varName as a parameter?

As I continue working on this function, a question arises regarding the safety of changing variables in this manner. In my Angular service, I utilize utility functions where context represents this from the component calling the function. The code snippet ...

How can I center align my loader inside app-root in Angular2+?

I've successfully added a basic spinner to my <app-root> in the index.html file. This gives the appearance that something is happening behind the scenes while waiting for my app to fully load, rather than showing a blank white page. However, I& ...