<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap-tpls.min.js"></script>
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  <script src="example.js"></script>
</head>

<body ng-app="plunker">
  <script type="text/ng-template" id="customTemplate.html">
    <a>
      <div>{{match.label.code}}</div>
      <span ng-bind-html="match.label.display | uibTypeaheadHighlight:query"></span>
    </a>
  </script>
  <div class="container-fluid" style="margin-bottom:5rem;">
    <h2>Using the FHIR $translate operation</h2>
    <img style="float:right" src="https://ontoserver.csiro.au/docs/4.1/images/network-cloud-internet-small.png">
    <p>Example demonstrating FHIR's <tt><a href="http://hl7.org/fhir/conceptmap-operations.html#translate">ConceptMap/$translate</a></tt>.
    </p>
    <p>
    Select a <i>Map</i> from the drop-down, then search for a <i>Source</i> code to translate.
    </p>
  </div>
  <div ng-controller="Main as main">
    <!-- ENDPOINT -->
    <div class="container-fluid">
      <label class="label label-default" for="endpoint">Endpoint</label>
      <input class="form-control" id="endpoint" type="text" ng-model="endpoint" ng-init="endpoint='https://r4.ontoserver.csiro.au/fhir'" placeholder="FHIR Server base URL">
    </div>

    <!-- ConceptMap -->
    <div class="container-fluid">
      <label class="label label-default" for="conceptmap">Map</label>
      <select class="form-control" id="conceptmap" ng-model="selected" ng-options="item as item.name for item in conceptmaps(endpoint)">
      </select>
      <pre ng-show="selected.id">{{selected.description}}

From: {{selected.sourceUri}}
To:   {{selected.targetUri}}</pre>
    </div>

    <!-- SOURCE CODE -->
    <div class="container-fluid" ng-controller="TypeaheadCtrl">
      <label class="label label-default" for="source">Source : {{$parent.selected.sourceName}}</label>
      <input id="source" class="form-control" placeholder="Find codes..." type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" ng-model="result" uib-typeahead="suggestion for suggestion in concepts($viewValue)"
      typeahead-loading="loading" typeahead-template-url="customTemplate.html" typeahead-input-formatter="result.display" />
      <pre>{{loading && 'Searching...' || ''}} {{result | json}}</pre>

      <p><a href="{{endpoint}}/ConceptMap/$translate?url={{selected.url}}&target={{selected.targetUri}}&system={{result.system}}&code={{result.code}}" target="_blank"><tt ng-if="result.code">[base]/ConceptMap/$translate?url={{selected.url}}&amp;target={{selected.targetUri}}&amp;system={{result.system}}&amp;code={{result.code}}</tt></a></p>
      <!-- TRANSLATION -->
      <label class="label label-default" for="translation">Translation</label>
      <pre ng-show="result && translate(result.code, result.system).result" id="translation">{{result && translation | json}}</pre>

      <!-- ERRORS -->
      <table class="table table-sm" ng-if="error">
        <tr ng-repeat="item in error.issue">
          <td>{{item.code}}</td><td>{{item.diagnostics}}</td>
        </tr>
      </table>
    </div>

  </div>

  <nav style="position:fixed;bottom:0;width:100%;background-color:#eee;" class="navbar navbar-light bg-light">
    <div class="small">
      <a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons Licence" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a>
      <br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">This $validate-code exemplar</span> by <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">CSIRO</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
    </div>
  </nav>

</body>

</html>
angular
  .module('plunker', ['ui.bootstrap'])
  .controller('Main', ['$scope', '$http', function($scope, $http) {
    // console.log('EP', $scope.endpoint);

    function flattenP(params) {
      var m = {};
      params.forEach(function(p) {
        var v;
        if (p.part) {
          v = flattenP(p.part);
        } else {
          v = p.valueCode || p.valueCoding || p.valueString || p.valueBoolean;
        }
        if (m[p.name]) {
          if (!Array.isArray(m[p.name])) {
            m[p.name] = [m[p.name]];
          }
          m[p.name].push(v);
        } else {
          m[p.name] = v;
        }
      });
      return m;
    }

    $scope.selected = {}
    $scope.sourceName = undefined;

    var x = true,
        maps = [];

    $scope.conceptmaps = function(endpoint) {
      $scope.error = undefined;
      // console.log('EP', endpoint);
      if (x !== endpoint) {
        x = endpoint;
        maps.splice(0, maps.length);  // delete existing list
        $scope.selected = undefined;
        // return
        $http({
            method: 'GET',
            url: endpoint + '/ConceptMap/_search',
            params: {
              '_elements': 'id,name,url,source,target,description',
              '_count': 200,
              '_format': 'json'
            },
            responseType: 'json'
          })
          .then(function(response) {
            response.data.entry
              .map(function(e) {
                return e.resource;
              })
              .filter(function(e) {
                return !(false && e.sourceUri || '').startsWith('urn:') &&
                       (e.targetUri || '').startsWith('http://snomed.info/sct') && e.targetUri;
              })
              .forEach(e => maps.push(e));
            $scope.selected = maps[0] || {};
            // $scope.sourceName = $scope.resName('ValueSet',$scope.selected.sourceUri);
            return maps;
          })
          .then(function (maps) {
            maps.forEach(m => {
              sourceName = $scope.resName('ValueSet', m.sourceUri)
                .then(name => m.sourceName = name);
            });
          });
      }
      return maps;
    };

    var cache = {};
    $scope.translate = function(code, system) {
      console.log('TRANSLATE', code, system);
      if (code && system) {
        var key =
          $scope.selected.url + '|' + code + '|' + system + '|' + $scope.selected.targetUri;
        // console.log(code, system);
        return cache[key] || $http({
            method: 'GET',
            url: $scope.endpoint + '/ConceptMap/$translate',
            params: {
              'url': $scope.selected.url,
              'code': code,
              'system': system,
              'target': $scope.selected.targetUri,
              '_format': 'json'
            },
            responseType: 'json'
          })
          .then(function(response) {
            var params = flattenP(response.data.parameter);
            cache[key] = params;
            console.log('P', params);
            $scope.translation = params;
            return params.result;
          }, function (response) {
            console.log('ERROR', response);
            cache[key] = 'ERROR';
            $scope.translation = response.data;
          });
      }
    };
    
    $scope.resName = function(resourceType, system, version) {
      // selected.sourceUri
      console.log(resourceType, system);
      if (system) {
        return $http({
          method: 'GET',
          url: $scope.endpoint + '/' + resourceType,
          params: {
            'url': system,
            'version': version,
            '_format': 'json'
          },
          responseType: 'json'
        })
        .then(function (response) {
          var name = system;
          if (response.data.total && response.data.entry) {
            response.data.entry.forEach(e => {name = e.resource.name;})
          }
          console.log('Name', name);
          return name;
        }, function (response) {
          console.log('ERROR', response);
          return system;
        })
      }
    };
    
  }])
  .controller('TypeaheadCtrl', ['$scope', '$http', function($scope, $http) {
    $scope.concepts = function(term, endpoint) {
      // console.log(term);
      $scope.error = undefined;
      var valueSet = $scope.selected.sourceUri;
      endpoint = $scope.endpoint || 'https://tx.ontoserver.csiro.au/fhir';

      return $http({
          method: 'GET',
          url: endpoint + '/ValueSet/$expand',
          params: {
            'url': valueSet,
            'filter': term,
            'count': 15,
            '_format': 'json'
          },
          responseType: 'json'
        })
        .then(function(response) {
          // console.log(response.data.expansion);
          return response.data.expansion.contains || [{
            display: term
          }];
        }, function (response) {
          console.log('ERROR', response.status, JSON.stringify(response.data));
          var res = response.data;
          if (res && res.resourceType === 'OperationOutcome') {
            $scope.error = res;
          } else {
            $scope.error = JSON.stringify(response.data);
          }
        });
    };

  }]);
# An example for FHIR ConceptMap/$translate

This is a simple example of how to connect up UI Bootstrap's Typeahead control to the [ConceptMap/$translate](http://hl7.org/fhir/STU3/conceptmap-operations.html#translate) operation.

This provides a simple way to find and select a code in one CodeSystem / ValueSet, and then use a ConceptMap to translate it.

Uses a public sandbox [endpoint](https://stu3.ontoserver.csiro.au/fhir/) of [Ontoserver](https://ontoserver.csiro.au/).