User Tools

Site Tools


dev:browser_staff:examples

Examples of developing for the Web client

Angular

Minimum Working Example: Basic screen

Create a new component

Components are the building blocks of the Angular staff client. Each one contains some TypeScript and some view code (called a template, which may or may not be broken up into a separate file).

To start with, we will create a super simple component that just displays the words "Evergreen is good at math". To do this, create a new folder called Open-ILS/src/eg2/src/app/staff/math.

Inside your folder, create a file called eg-math.component.ts with the following content:

import { Component } from '@angular/core';

@Component({
  template: '<p>Evergreen is good at math</p>',
})
export class EgMathComponent {
}

Add your component to a module

Next, components have to be grouped into modules – basically collections of similar components. Let's create a math module inside the same folder. Create a file called math.module.ts with the following content:

import {NgModule} from '@angular/core';
import {EgMathComponent} from './eg-math.component';
import {RouterModule, Routes} from '@angular/router';


const routes: Routes = [{
  path: 'eg',
  component: EgMathComponent
}];


@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  declarations: [EgMathComponent]
})
export class MathModule { }

Add your module to the Staff router

Finally, it's time to let the rest of Evergreen's Angular client know where to find your code. Add this to Open-ILS/src/eg2/src/app/staff/routing.module.ts, within the routes constant:

{
    path: 'math',
    loadChildren: '@eg/staff/math/math.module#MathModule'
  },

Re-compile, the client, and you should see your brand new screen at https://[yourdomain.edu]/eg2/en-US/staff/math/eg

Add another component with an OpenSRF call

Create a new .ts file in Open-ILS/src/eg2/src/app/staff/math, called adder.component.ts:

import {Component, OnInit} from '@angular/core';
import {NetService} from '@eg/core/net.service';

@Component({
  selector: 'eg-adder',
  templateUrl: './adder.component.html',
})
export class AdderComponent implements OnInit {
  sum: number = 0;
  addTwoNumbers: (first: number, second: number) => void;

  constructor(
    private net: NetService
  ){}

  ngOnInit() {
    this.addTwoNumbers = (first: number = 0, second: number = 0) => {
      this.net.request(
        'opensrf.math',
        'add',
        first, second)
      .subscribe(response => this.sum = response);
    }
  }
}

This has some differences from our first one. First of all, it has a selector, which means that we can include it in other components by saying <eg-adder></eg-adder>. Secondly, it has its template in a separate file, Open-ILS/src/eg2/src/app/staff/math/adder.component.html. In fact, let's create that now:

<label i18n>First Number: <input #firstNumber type="number"></label>
<label i18n>Second Number: <input #secondNumber type="number"></label>
<button (click)="addTwoNumbers(firstNumber.value, secondNumber.value)">Add</button>
<p>Sum: {{sum}}</p>

Let's update our module to let it know about the new component:

import {NgModule} from '@angular/core';
import {EgMathComponent} from './eg-math.component';
import {AdderComponent} from './adder.component';
import {RouterModule, Routes} from '@angular/router';


const routes: Routes = [{
  path: 'eg',
  component: EgMathComponent
}];


@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  declarations: [EgMathComponent, AdderComponent]
})
export class MathModule { }

Finally, let's update our first component's template to include a reference to our new component. To do this, just change the line template: '<p>Evergreen is good at math</p> to template: '<p>Evergreen is good at math</p><eg-adder></eg-adder>'

AngularJs

Minimum Working Example: Screen that gets data from OpenSrf

Planning

Decide on the requirements and UI of your new screen

Our example will ask the user for two numbers, then add them when the user presses a button.

Decide which Evergreen module your screen is part of

For example, is it part of circ? booking? cataloging? Today's example is under cataloging module.

Creating your screen

Add a route for your screen

Find the app.js for the appropriate module. The app.js files are in Open-ILS/web/js/ui/default/staff in the git repository, in /openils/var/web/js/ui/default/staff for installed systems.

Today, we'll add it to web/js/ui/default/staff/cat/catalog/app.js:

    $routeProvider.when('/cat/catalog/math', {
        templateUrl: './cat/catalog/t_add',
        controller: 'MathematicsCtrl',
        resolve : resolver
    });
Create a controller for your screen

This can be in the same js file as your route was. Here's one to add to web/js/ui/default/staff/cat/catalog/app.js:

.controller('MathematicsCtrl', ['$scope', 'egCore', function($scope, egCore) {
    $scope.firstNumber = 1;
    $scope.secondNumber = 6;
    $scope.add_things = function() {
        egCore.net.request('opensrf.math', 'add', $scope.firstNumber, $scope.secondNumber
        ).then(
            function(response) {
                $scope.sum = response;
            });
    }
}])
Create a tt2 template for your screen

Look for an index.tt2 file that includes the JS file you were working on. Create a new file that begins with t_ in the same directory. Starting the template with t_ will add the Evergreen Web client header and other goodies from the index.tt2 file in the same directory. This includes the controller that you created during the last step.

The name and path to your new tt2 file should match the templateUrl you entered in your route. For example, if your templateUrl was ./cat/catalog/t_add, create a tt2 file at Open-ILS/src/templates/staff/cat/catalog/t_add.tt2 in the git repo or /openils/var/templates/staff/cat/catalog/t_add.tt2 in an installed system.

<form ng-submit="add_things()">
<label>[% l('First Number:')%] <input type="text" ng-model="firstNumber"></label>
<label>[% l('Second Number:')%] <input type="text" ng-model="secondNumber"></label>
<button type="submit" value="Add" class="btn btn-success" id="submit">[% l('Add')%]</button>
Sum: {{sum}}
</form>
Test your screen

Add a popup modal to your screen

This example builds on the previous example of an addition screen by adding a button, which, when pressed, opens up a modal asking the user to confirm.

Edit the controller

First of all, edit the controller to make sure it can access the egAlertDialog factory:

controller('MathematicsCtrl', ['$scope', 'egCore', 'egAlertDialog', function($scope, egCore, egAlertDialog) {

Then add a function to the controller that makes use of the factory:

$scope.provide_feedback = function() {
  egAlertDialog.open(egCore.strings.SHARE_YOUR_OPINION);
}

Add your string to egCore.strings

We want to be able to translate the egAlertDialog text to the language that the user prefers. To do this, we will define it in the parent .tt2 page (in this case, templates/staff/cat/catalog/index.tt2).

To do this, look for the area in between <script>angular.module('egCoreMod').run(['egStrings', function(s) { and }])</script>, where other egCore.strings are defined. Then add:

s.SHARE_YOUR_OPINION = "[% l('I think so too!') %]";

Edit the tt2 template

Here, we'll just add a simple button outside of the <form> that, when clicked, causes our alert to display:

<button ng-click="provide_feedback()" class="btn btn-default" id="feedback">
  [% l('I think Evergreen is cool!') %]
</button>

Note: Evergreen has several other built-in types of modals, such as egConfirmDialog, egPromptDialog, and egSelectDialog. You can find a complete list, as well as usage documentation, in this file: https://github.com/evergreen-library-system/Evergreen/blob/main/Open-ILS/web/js/ui/default/staff/services/ui.js

Add an egGrid to your screen

One of the best ways to present tabular data in Evergreen is to use an egGrid. This example builds on the previous two examples by adding an egGrid that displays all the contents of a specific table in the database. Our grid will display a very simple table called biblio.peer_type.

Edit the controller

First of all, edit the controller to make sure it can access the egGridDataProvider factory:

controller('MathematicsCtrl', ['$scope', 'egCore', 'egAlertDialog', 'egGridDataProvider', function($scope, egCore, egAlertDialog, egGridDataProvider) {

Then configure your grid using the gridControls hash. This is where we will set a query indicating which rows we want to fetch from the database to populate our egGrid. Without this query, egGrid won't know what to fetch, and our grid will be empty.

    $scope.gridControls = {
        setQuery : function() {
            return { 'id' : { '!=' : null } };
        },
    }

The query is in a specific JSON syntax that represents an SQL query. This tutorial for a related (but slightly different) JSON syntax should get you started.

Edit the tt2 template

Here, we'll add an eg-grid directive with some additional configuration options:

<eg-grid
    idl-class="bpt"
    auto-fields="true"
    grid-controls="gridControls"
    persist-key="cat.math.add">
</eg-grid>

In this example, the idl-class and auto-fields options work together to automatically fetch the columns from biblio.peer_type and identify which column is an ID. bpt is an abbreviation for biblio.peer_type; such abbreviations can be found in the IDL. Bill Erickson has documented several ways to access the IDL to find such information.

auto-fields doesn't work for every single table in the database. If the IDL doesn't have a permacrud section defining the appropriate retrieve permissions for the table, for example, the column names won't automatically generate. The retrieve permissions defined in the permacrud section of the IDL also affect which users are able to see data in the egGrid. For example, if you change bpt to cbho, users with the ADMIN_HOLD_CAPTURE_SORT permission will see a grid fully populated with the contents of config.best_hold_order, but users without that permission will only see the column names and an empty grid.

The grid-controls option simply refers back to the gridControls hash we established in our controller. Finally, the persist-key allows users to change various settings on the grid and save those preferences.

Populate a dropdown menu using Permacrud

Edit the controller

This example uses a simple `retrieveAll` call. If you want to be more selective about the records you pull in, you can put filters, sorting, fleshing, etc. inside the second argument. In this example, we will retrieve all the contents of the `config.best_hold_order` table. Add the following to your controller:

    egCore.pcrud.retrieveAll('cbho', {}, {atomic : true}).then(
        function(list) {
            $scope.best_hold_order_list = list;
        });

Edit the tt2 template

Add the following to your template:

<select ng-model="selected_hold_order" ng-options="hold_order.id() as hold_order.name() for hold_order in best_hold_order_list"></select>
dev/browser_staff/examples.txt · Last modified: 2023/06/01 13:20 by dyrcona

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

© 2008-2022 GPLS and others. Evergreen is open source software, freely licensed under GNU GPLv2 or later.
The Evergreen Project is a U.S. 501(c)3 non-profit organization.