The Angular client doesn't talk to the database directly, it instead communicates with OpenSRF to get the information and perform the actions it needs. To do this, it uses the NetService
, which is itself a wrapper around the OpenSRF javascript code.
The Angular client uses two services to interacting with OpenSRF on database-related matters:
NetService
, which directly calls a specific OpenSRF method that you specify in your code.PcrudService
, which provides a one-size-fits-all interface for most database operations, and uses the NetService
internally.Calling a specific OpenSRF method is preferred in cases in which:
The PcrudService is great for other cases, especially since it automatically checks the logged-in user's permissions and only provides the access that they are authorized for.
Since both services are used widely in the angular client, we will provide an example of each for each CRUD action below:
Let's start out with a simple PcrudService request:
/* Create a new object in memory with the required information * * The "ahrn" class is mapped to the action.hold_request_note * table in the database. These mappings can be found in * the fm_IDL.xml file. */ const note = this.idl.create('ahrn'); note.staff('t'); note.hold(12); note.title('My note'); note.body('Once upon a time, there was something interesting about this hold'); note.slip('t'); note.pub('f'); // Save the object to the database this.pcrud.create(note) /* Pcrud calls are asynchronous, so that they don't * block other work that the browser needs to do in * the staff client. * * The following alert will happen once the PcrudService * receives a message back from OpenSRF */ .subscribe(() => { alert('You did it!') });
The above assumes that you have injected the IdlService
as this.idl
and the PcrudService
as this.pcrud
earlier in the file.
Here's an example that uses the NetService
to create a database object instead, since the work we are doing is more complex and better suited to the Perl layer (reading from one database object, then creating a new database object in a totally different table with certain data taken from the first object):
this.net.request( 'open-ils.booking', 'open-ils.booking.resources.create_from_copies', /* This OpenSRF method takes two parameters: the user's token and * an array of item IDs to make bookable */ this.auth.token(), [1, 2, 3]) // Like the PcrudService, the NetService is also asynchronous... .subscribe(() => { alert('Those items are bookable now')}); /* Since the previous alert has to wait until a communication * comes back from OpenSRF, the following alert will almost * certainly appear to the user before the * 'Those items are bookable now' alert. */ alert('loading...');
The above assumes that you have injected the AuthService
as this.auth
and the NetService
as this.net
.
this.pcrud.search( // The IDL class we want to search: this is shelving locations 'acpl', // The filters we want to pass (the SQL WHERE clause). // This uses the {opac_visible: 't'} ).subscribe((location) => console.log(location.name()));
Each shelving location is considered to be a different RxJS event, so the above code will log each shelving location to the console separately. If you want to wait for them to finish and log them all at once in an array, you can do so with the RxJS toArray operator:
this.pcrud.search( 'acpl', {opac_visible: 't'} ).pipe(toArray() ).subscribe((locations) => { // Only log the name field, not the whole object console.log(locations.map((location) => location.name())) });
More information about the JSON query syntax is available in the tutorial.
The above assumes that you have injected the PcrudService
as this.pcrud
earlier in the file.
Some interfaces need data in very specific formats, or need to use complicated JOINs when querying the database. In these cases, a nice approach is to create a custom OpenSRF method in Perl and use the angular NetService to call it. In this example, we're calling the 'open-ils.acq.purchase_order.retrieve' OpenSRF method to gather a ton of information about the purchase order with ID #1. Refer to the OpenSRF method's documentation to know which parameters you need to send with your request.
this.net.request( 'open-ils.acq', 'open-ils.acq.purchase_order.retrieve', this.auth.token(), 1, { flesh_provider: true, flesh_notes: true, flesh_po_items: true, flesh_po_items_further: true, flesh_price_summary: true, flesh_lineitem_count: true } ).subscribe(po => console.log(po));
The above assumes that you have injected the NetService
as this.net
and the AuthService
as this.auth
earlier in the file.
The basic workflow for updating data in the database is to:
update
method to save the change to the database.The following example retrieves the item with ID #1, changes its barcode to 1234567 in memory, then saves the updated barcode to the database:
this.pcrud.retrieve('acp', 1) .pipe(switchMap((item) => { item.barcode('1234567'); return this.pcrud.update(item); })).subscribe();
Notice that we use the rxjs switchMap
operator to switch our observable halfway through, from a `retrieve` call to an `update` call. This helps us to avoid nesting RxJs subscriptions within each other, which can get very complicated to troubleshoot.
Here is an example of refreshing the contents of an automatically-generated carousel (the one with ID 201):
this.net.request('open-ils.actor', 'open-ils.actor.carousel.refresh', this.auth.token(), 201).subscribe(() => { alert('I feel so refreshed now!'); });
As with updating objects, you will need to retrieve an object and have a copy in memory before you can delete it. The following example searches for all shelving locations called "Microfilm" and then deletes them, one by one:
this.pcrud.search('acpl', {name: 'Microfilm'}) .pipe(switchMap((location) => { return this.pcrud.remove(location); })).subscribe();
Here's an example of deleting record bucket #13 from the database, along with all its bucket entries (assuming the currently logged in user has the DELETE_CONTAINER permission):
this.net.request('open-ils.actor', 'open-ils.actor.container.full_delete', this.auth.token(), 'biblio', 13).subscribe(() => { alert('Your bucket is gone!'); });
When troubleshooting an angular screen that makes OpenSRF calls, it can be helpful to use your browser's dev tools to see exactly which request the Angular code made, and exactly what response it got back. In both Firefox and Chrome, this can be done via the Network tab in your devtools.