diegomvh / angular-odata

Client side OData typescript library for Angular
https://www.npmjs.com/package/angular-odata
MIT License
50 stars 14 forks source link

"this.options.helper.context is not a function" #66

Closed rolfen closed 1 year ago

rolfen commented 2 years ago

Unable to consume oData, with error "this.options.helper.context is not a function"

o-data-backend-service.ts

import { Injectable } from '@angular/core';
import { ODataClient, ODataServiceFactory } from "angular-odata";
import { Observable } from 'rxjs';

@Injectable({providedIn: 'root'})
export class ODataBackendService {
  constructor(private factory: ODataServiceFactory) { }
  query() {
    // Use OData Service Factory
    let dataService = this.factory.entitySet(
      "Products"
      // ,"Microsoft.OData.SampleService.Models.TripPin.Airport"
    );
    let data = dataService.entities();
    data.fetch().subscribe(({ entities }) => { 
      console.log(entities); 
    });
  }
}

Call in app-component.ts:

export class AppComponent {
  constructor(oDataBackendService : ODataBackendService) { 
    oDataBackendService.query();
  }
  // ...
}

Init in app-module.ts

const oDataSettings: ApiConfig = {
  serviceRootUrl: 'https://services.odata.org/V2/(S(cnqkn3twkxnupuslkhmbwq1h))/OData/OData.svc/',
  version: '2.0'
};

Testing in Edge with "Allow CORS: Access-Control-Allow-origin" extension because I was getting CORS errors with oData version 2.

node v16.17.0 (Windows 10) running from inside Visual Studio Code

Node modules used in the project (output of npm ls):

├── __ngcc_entry_points__.json@ extraneous
├── @angular-devkit/build-angular@14.2.3  
├── @angular/animations@14.2.2
├── @angular/common@14.2.2
├── @angular/compiler-cli@14.2.2
├── @angular/compiler@14.2.2
├── @angular/core@14.2.2
├── @angular/forms@14.2.2
├── @angular/platform-browser-dynamic@14.2.2
├── @angular/platform-browser@14.2.2
├── @angular/router@14.2.2
├── @types/jasmine@4.0.3
├── @ui5/webcomponents@1.7.1
├── angular-odata@0.105.0
├── cors-anywhere@0.4.4 (git+ssh://git@github.com/Rob--W/cors-anywhere.git#70aaa22b3f9ad30c8566024bf25484fd1ed9bda9)
├── jasmine-core@4.1.1
├── karma-chrome-launcher@3.1.1
├── karma-coverage@2.2.0
├── karma-jasmine-html-reporter@1.7.0
├── karma-jasmine@5.0.1
├── karma@6.3.20
├── rxjs@7.5.6
├── tslib@2.4.0
├── typescript@4.7.4
└── zone.js@0.11.8

Fragment of the received oData (From the network tab in the browser console)

{
"d" : {
"results": [
{
"__metadata": {
"uri": "https://services.odata.org/(S(cnqkn3twkxnupuslkhmbwq1h))/V2/OData/OData.svc/Products(0)", "type": "ODataDemo.Product"
}, "ID": 0, "Name": "Bread", "Description": "Whole grain bread", "ReleaseDate": "\/Date(694224000000)\/", "DiscontinuedDate": null, "Rating": 4, "Price": "2.5", "Category": {
"__deferred": {
"uri": "https://services.odata.org/(S(cnqkn3twkxnupuslkhmbwq1h))/V2/OData/OData.svc/Products(0)/Category"
}
}, "Supplier": {
"__deferred": {
"uri": "https://services.odata.org/(S(cnqkn3twkxnupuslkhmbwq1h))/V2/OData/OData.svc/Products(0)/Supplier"
}
}
}, {
"__metadata": {
"uri": "https://services.odata.org/(S(cnqkn3twkxnupuslkhmbwq1h))/V2/OData/OData.svc/Products(1)", "type": "ODataDemo.Product"
}, "ID": 1, "Name": "Milk", "Description": "Low fat milk", "ReleaseDate": "\/Date(812505600000)\/", "DiscontinuedDate": null, "Rating": 3, "Price": "3.5", "Category": {
"__deferred": {
"uri": "https://services.odata.org/(S(cnqkn3twkxnupuslkhmbwq1h))/V2/OData/OData.svc/Products(1)/Category"
}
}, "Supplier": {
"__deferred": {
"uri": "https://services.odata.org/(S(cnqkn3twkxnupuslkhmbwq1h))/V2/OData/OData.svc/Products(1)/Supplier"
}

The preceding error (may be relevant):

app.module.ts:30 
 [LocaleData] Supported locale "en_GB" not configured, import the "Assets.js" module from the webcomponents package you are using.
o-data-backend.service.ts:18 

The error (from the browser console):

 ERROR TypeError: this.options.helper.context is not a function
    at get context [as context] (angular-odata.mjs:6762:49)
    at ODataInMemoryCache.tags (angular-odata.mjs:157:29)
    at ODataInMemoryCache.putResponse (angular-odata.mjs:312:25)
    at Object.next (angular-odata.mjs:278:26)
    at source.subscribe.isUnsub (tap.js:17:81)
    at OperatorSubscriber._next (OperatorSubscriber.js:13:21)
    at OperatorSubscriber.next (Subscriber.js:31:18)
    at map.js:7:24
    at OperatorSubscriber._next (OperatorSubscriber.js:13:21)
    at OperatorSubscriber.next (Subscriber.js:31:18)
handleError @   core.mjs:7635
next    @   core.mjs:26915
next    @   Subscriber.js:91
_next   @   Subscriber.js:60
next    @   Subscriber.js:31
(anonymous) @   Subject.js:34
errorContext    @   errorContext.js:19
next    @   Subject.js:27
emit    @   core.mjs:22742
(anonymous) @   core.mjs:26257
invoke  @   zone.js:372
run @   zone.js:134
runOutsideAngular   @   core.mjs:26130
onHandleError   @   core.mjs:26257
handleError @   zone.js:376
runTask @   zone.js:181
invokeTask  @   zone.js:487
ZoneTask.invoke @   zone.js:476
data.args.<computed>    @   zone.js:2385
setTimeout (async)      
scheduleTask    @   zone.js:2387
scheduleTask    @   zone.js:393
onScheduleTask  @   zone.js:283
scheduleTask    @   zone.js:386
scheduleTask    @   zone.js:221
scheduleMacroTask   @   zone.js:244
scheduleMacroTaskWithCurrentZone    @   zone.js:683
(anonymous) @   zone.js:2429
proto.<computed>    @   zone.js:973
setTimeout  @   timeoutProvider.js:7
reportUnhandledError    @   reportUnhandledError.js:4
handleUnhandledError    @   Subscriber.js:158
error   @   Subscriber.js:109
_error  @   Subscriber.js:64
error   @   Subscriber.js:40
_error  @   Subscriber.js:64
error   @   Subscriber.js:40
OperatorSubscriber._next    @   OperatorSubscriber.js:16
next    @   Subscriber.js:31
(anonymous) @   map.js:7
OperatorSubscriber._next    @   OperatorSubscriber.js:13
next    @   Subscriber.js:31
(anonymous) @   filter.js:6
OperatorSubscriber._next    @   OperatorSubscriber.js:13
next    @   Subscriber.js:31
subscribe.innerComplete @   mergeInternals.js:25
OperatorSubscriber._next    @   OperatorSubscriber.js:13
next    @   Subscriber.js:31
onLoad  @   http.mjs:1844
invokeTask  @   zone.js:406
onInvokeTask    @   core.mjs:26218
invokeTask  @   zone.js:405
runTask @   zone.js:178
invokeTask  @   zone.js:487
invokeTask  @   zone.js:1661
globalCallback  @   zone.js:1704
globalZoneAwareCallback @   zone.js:1725
load (async)        
customScheduleGlobal    @   zone.js:1809
scheduleTask    @   zone.js:393
onScheduleTask  @   zone.js:283
scheduleTask    @   zone.js:386
scheduleTask    @   zone.js:221
scheduleEventTask   @   zone.js:247
(anonymous) @   zone.js:1964
(anonymous) @   http.mjs:1930
_trySubscribe   @   Observable.js:37
(anonymous) @   Observable.js:31
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
doInnerSub  @   mergeInternals.js:19
outerNext   @   mergeInternals.js:14
OperatorSubscriber._next    @   OperatorSubscriber.js:13
next    @   Subscriber.js:31
(anonymous) @   innerFrom.js:51
_trySubscribe   @   Observable.js:37
(anonymous) @   Observable.js:31
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
mergeInternals  @   mergeInternals.js:50
(anonymous) @   mergeMap.js:13
(anonymous) @   lift.js:10
(anonymous) @   Observable.js:26
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
(anonymous) @   filter.js:6
(anonymous) @   lift.js:10
(anonymous) @   Observable.js:26
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
(anonymous) @   map.js:6
(anonymous) @   lift.js:10
(anonymous) @   Observable.js:26
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
(anonymous) @   tap.js:15
(anonymous) @   lift.js:10
(anonymous) @   Observable.js:26
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
(anonymous) @   map.js:6
(anonymous) @   lift.js:10
(anonymous) @   Observable.js:26
errorContext    @   errorContext.js:19
subscribe   @   Observable.js:22
query   @   o-data-backend.service.ts:18
AppComponent    @   app.component.ts:14
AppComponent_Factory    @   app.component.ts:15
getNodeInjectable   @   core.mjs:3523
instantiateRootComponent    @   core.mjs:12550
createRootComponent @   core.mjs:13993
create  @   core.mjs:13870
bootstrap   @   core.mjs:27300
(anonymous) @   core.mjs:26960
_moduleDoBootstrap  @   core.mjs:26960
(anonymous) @   core.mjs:26930
invoke  @   zone.js:372
onInvoke    @   core.mjs:26231
invoke  @   zone.js:371
run @   zone.js:134
(anonymous) @   zone.js:1275
invokeTask  @   zone.js:406
onInvokeTask    @   core.mjs:26218
invokeTask  @   zone.js:405
runTask @   zone.js:178
drainMicroTaskQueue @   zone.js:585
Promise.then (async)        
nativeScheduleMicroTask @   zone.js:561
scheduleMicroTask   @   zone.js:572
scheduleTask    @   zone.js:396
scheduleTask    @   zone.js:221
scheduleMicroTask   @   zone.js:241
scheduleResolveOrReject @   zone.js:1265
then    @   zone.js:1461
asyncGeneratorStep  @   asyncToGenerator.js:13
_next   @   asyncToGenerator.js:25
(anonymous) @   asyncToGenerator.js:32
ZoneAwarePromise    @   zone.js:1429
(anonymous) @   asyncToGenerator.js:21
(anonymous) @   Boot.js:32
ZoneAwarePromise    @   zone.js:1429
(anonymous) @   Boot.js:32
asyncGeneratorStep  @   asyncToGenerator.js:3
_next   @   asyncToGenerator.js:25
(anonymous) @   asyncToGenerator.js:32
ZoneAwarePromise    @   zone.js:1429
(anonymous) @   asyncToGenerator.js:21
boot    @   Boot.js:21
(anonymous) @   UI5Element.js:990
asyncGeneratorStep  @   asyncToGenerator.js:3
_next   @   asyncToGenerator.js:25
(anonymous) @   asyncToGenerator.js:32
ZoneAwarePromise    @   zone.js:1429
(anonymous) @   asyncToGenerator.js:21
define  @   UI5Element.js:989
4361    @   Label.js:154
__webpack_require__ @   bootstrap:19
6141    @   Avatar.js:420
__webpack_require__ @   bootstrap:19
5752    @   TabSeparator.js:90
__webpack_require__ @   bootstrap:19
9067    @   app.module.ts:30
__webpack_require__ @   bootstrap:19
6747    @   app.component.html:1
__webpack_require__ @   bootstrap:19
4431    @   environment.ts:16
__webpack_require__ @   bootstrap:19
__webpack_exec__    @   main.ts:12
(anonymous) @   main.ts:12
__webpack_require__.O   @   chunk loaded:23
(anonymous) @   main.ts:12
webpackJsonpCallback    @   jsonp chunk loading:34
(anonymous) @   main.js:2
diegomvh commented 2 years ago

Hi @rolfen

By default the cache storage is storing all requests. For each cached request, tags are generated based on the context of the odata response. For your issue in particular, the library's support for odata version 2 and 3 is basic, very basic. For odata version 2 there was no implemented way to get the context of the response. I recently added a dumb function that returns an empty context for version 2 of odata. Other solutions could be to disable caching using no-cache in the api configuration. Soon I will publish the fix (0.110.0), also I have to point out that the support for odata version 2 is very poor.

Regards