Home > How to create your own widget

How to create your own widget

Sparnatural offers a range of different widgets for sparql value selection. Further Widget can be added by implementing extending the AbstractWidget class. Let’s create the BooleanWidget to demonstrate the implementation process:

Create new widget Class

Go to the widget folder ./src/sparnatural/components/widgets/ We will create a new Class extending from Abstract Widget:

import { Pattern } from "sparqljs";
import { AbstractWidget, WidgetValue } from "./AbstractWidget";
export class BooleanWidget extends AbstractWidget {
    getRdfJsPattern(): Pattern[] {
        throw new Error("Method not implemented.");
    }
    parseInput(value: { label: string; }): WidgetValue {
        throw new Error("Method not implemented.");
    }
}

With the help of some intellisense we can see that the two methods getRdfJsPattern and parseInput must be implemented.

getRdfJsPattern: Returns an Array of type Pattern[]. This is based on the sparqljs datamodel. This Pattern[] is what will be converted to sparql.

parseInput: Returns a WidgetValue and has two functions. It is used to parse the input given by the user and it parses string input when a query is imported.

Create the HTML rendering of the widget

Let’s first proceed with rendering the html of the widget. The AbstractWidget itself inherits from the HTMLComponent class which is the top class for all Sparnatural Components. Each component can therefore implement the render() method to render some HTML.

super.render();
    let trueSpan = $(
      `<span class="boolean-value">${getSettings().langSearch.true}</span>'`
    );
    let orSpan = $(`<span class="or">${getSettings().langSearch.Or}</span>`);
    let falseSpan = $(
      `<span class="boolean-value"">'${getSettings().langSearch.false}</span>`
    );
    this.html.append(trueSpan).append(orSpan).append(falseSpan);
    trueSpan[0].addEventListener("click", (e) => {
      let widgetValue: BooleanWidgetValue = new BooleanWidgetValue({
        label: getSettings().langSearch.true,
        boolean: true,
      });
      this.renderWidgetVal(widgetValue);
    });
    falseSpan[0].addEventListener("click", (e) => {
      let widgetValue: BooleanWidgetValue = new BooleanWidgetValue({
        label: getSettings().langSearch.false,
        boolean: false,
      });
      this.renderWidgetVal(widgetValue);
    });
    return this;

This renders to the following and has some clickListener to listen for the click:

Define a data class

Noticable above is the widgetValue creation inside the click listener. This leads us to the next requirement SparnaturalWidgets have. Each Sparnatural widget SHOULD come with a “data class”. This data class needs to inherit from the WidgetValue Interface:

export class BooleanWidgetValue implements WidgetValue {
  value: {
    label: string;
    boolean: boolean;
  }
  key():string {
    return this.value.boolean.toString();
  }
  constructor(v:BooleanWidgetValue["value"]) {
    this.value = v;
  }
}

Implement parseInput

parseInput is used for 2 things : parsing the value that has been entered by the user, and parsing the value stored in the JSON data structure of saved queries.

TODO

Implement getRdfJsPattern

TODO

Adjust SPARQL generation by “blocking” default generation

TODO

2. Add a case for when the new Widget should be used

In components/builder-section/groupwrapper/criteriagroup/edit-components/WidgetWrapper.ts, method #createWidgetComponent() you will see a “switch” directive that tests on the widgetType, that is on the URI of a property type coming from the Sparnatural configuration ontology :

    switch (widgetType) {
      case Config.LITERAL_LIST_PROPERTY: {
        // ...
      }
      case Config.LIST_PROPERTY:
        // ...
      }
      case Config.AUTOCOMPLETE_PROPERTY:
        // ...
      }
      // ...
      default:
        throw new Error(`WidgetType for ${widgetType} not recognized`);
    }

This is where you can add a case to the swicth and call the constructor of your new widget.