-
Notifications
You must be signed in to change notification settings - Fork 151
Inline Creation in Views
Currently, when viewing a resource with related items displayed via ldh:template views, users cannot create new related items inline. They must navigate away to create items separately and then link them back.
Example: Viewing a SKOS Concept with a "Narrower Concepts" view requires leaving the page to create narrower concepts and manually linking them.
This breaks context and creates poor UX for enterprise CRUD applications.
Enable [Add New] buttons within views that open modal forms for creating related items inline, with automatic linking.
ldh:property a owl:ObjectProperty ;
rdfs:domain ldh:View ;
rdfs:range rdf:Property ;
rdfs:label "Linking property" ;
rdfs:comment "The RDF property used to link the parent resource to items created from this view" .Purpose: Specifies which property links the parent resource to newly created items.
Example:
:NarrowerConcepts a ldh:View ;
ldh:property skos:narrower .Creates triple: <parent> skos:narrower <new-item> when item is created.
ldh:container a owl:ObjectProperty ;
rdfs:domain ldh:View ;
rdfs:range rdfs:Resource ;
rdfs:label "Container for creating items" ;
rdfs:comment "The container URI where new items from this view will be created via HTTP PUT" .Purpose: Specifies where to create new documents (PUT target base URL).
Example:
:NarrowerConcepts a ldh:View ;
ldh:container </concepts/> .Creates new items at: PUT /concepts/{user-provided-slug}
ldh:targetClass a owl:ObjectProperty ;
rdfs:domain ldh:View ;
rdfs:range rdfs:Class ;
rdfs:label "Target class for creation" ;
rdfs:comment "Override for the RDF class of items created from this view. Only needed when range inference fails or needs override." .Purpose: Explicit class specification when rdfs:range lookup fails or needs override.
Example:
:ComplexView a ldh:View ;
ldh:property ex:complexProperty ;
ldh:targetClass ex:ConcreteClass . # Override abstract rangeldh:showWhenEmpty a owl:DatatypeProperty ;
rdfs:domain ldh:View ;
rdfs:range xsd:boolean ;
rdfs:label "Show when empty" ;
rdfs:comment "If false, the view is not rendered when the query returns no results. Default is true (show even when empty)." .Purpose: Controls whether a view is displayed when its query returns no results.
Default: true (show even when empty)
- Shows empty state message
- [Add New] button remains visible
- User knows the section exists
When set to false: View is hidden if query returns no results
- Cleaner UI for conditional sections
- Useful for VIP features, optional content
Example:
# Only show VIP benefits if customer has them
:VIPBenefits a ldh:View ;
spin:query :SelectVIPBenefits ;
ldh:showWhenEmpty false .
# Always show orders section (even if empty)
:Orders a ldh:View ;
spin:query :SelectOrders ;
ldh:showWhenEmpty true ; # Default behavior
ldh:property sales:customer ;
ldh:container </orders/> .Goal: Minimize metadata required by inferring from SPARQL query and ontology.
Algorithm:
-
Parse SPARQL query for pattern:
$about <property> ?variableSELECT ?narrower WHERE { $about skos:narrower ?narrower . }
-
Extract property:
skos:narrower -
Lookup range in ontology:
skos:narrower rdfs:range skos:Concept .
-
Result:
- Link via:
skos:narrower - Create:
skos:Conceptinstances
- Link via:
Works for ~80% of cases with simple property patterns.
When inference fails (property paths, UNIONs, multi-hop relations, missing range), use explicit metadata:
:ComplexView a ldh:View ;
spin:query :ComplexQuery ;
ldh:property ex:relation ;
ldh:targetClass ex:ItemClass ;
ldh:container </items/> .@prefix : <https://example.com/packages/skos#> .
# Attach view to class
skos:Concept ldh:template :NarrowerConcepts .
# View with inline creation
:NarrowerConcepts a ldh:View ;
dct:title "Narrower concepts" ;
spin:query :SelectNarrowerConcepts ;
ac:mode ac:TableMode ;
# Inline creation metadata
ldh:property skos:narrower ;
ldh:container </concepts/> .
:SelectNarrowerConcepts a sp:Select ;
sp:text """
SELECT DISTINCT ?narrower WHERE {
$about skos:narrower ?narrower .
}
ORDER BY ?prefLabel
""" .-
View concept at
/concepts/animals - See "Narrower Concepts" view with [Add New] button
- Click [Add New] → Modal form opens
-
Form shows:
- Slug field (required)
- Fields from
skos:Concept spin:constructor - Parent link pre-filled (hidden):
skos:narrower→/concepts/animals#this
-
User enters:
- Slug:
"mammals" - Label:
"Mammals"
- Slug:
-
Submit → PUT to
/concepts/mammals:<> a dh:Item ; foaf:primaryTopic <#this> . <#this> a skos:Concept ; skos:prefLabel "Mammals"@en . # Linking triple from parent </concepts/animals#this> skos:narrower <#this> .
- Modal closes, view refreshes, shows new narrower concept
- ✅ Stay in context (no navigation away)
- ✅ Automatic linking (no manual triple creation)
- ✅ Immediate feedback (view refreshes)
- ✅ Minimal metadata (property + container)
- ✅ Inference handles common cases
- ✅ Reuses existing constructors
- ✅ Consistent with LinkedDataHub patterns
- ✅ Standard HTTP PUT for creation
- ✅ Works with existing security model
- Add
ldh:propertyto ldh.ttl ontology - Add
ldh:containerto ldh.ttl ontology - Add
ldh:targetClassto ldh.ttl ontology (optional)
- Pattern inference logic (parse SPARQL, extract property)
- Range lookup (query ontology for rdfs:range)
- [Add New] button rendering (when metadata present)
- Modal form generation (from spin:constructor)
- Pre-fill linking property in form
- Modal open/close logic
- Form submission handler
- PUT request to {container}{slug}
- Include linking triple in payload
- View refresh after creation
- Handle PUT with linking triples
- Validate parent resource exists
- Validate property domain/range
- Return created resource URI
LinkedDataHub uses Saxon-JS for client-side XSLT execution, which includes ixsl: extensions for browser interactivity. This enables building fully interactive components entirely in XSLT without separate JavaScript files.
Interactive UI components can be built using XSLT + ixsl: event handlers:
Tab Containers
- Click tabs to switch views
- Pure XSLT with
ixsl:onclickhandlers - Manages CSS classes to show/hide panes
Sortable Tables
- Click column headers to sort
- Client-side sorting with
xsl:perform-sort - No server round-trip needed
Filterable Lists
- Type in filter inputs
-
ixsl:onkeyuphandlers filter rows - Show/hide based on text match
Bulk Operations
- Checkboxes for multi-select
- Bulk delete/export actions
- Selection state managed in XSLT
Modal Dialogs
- Dynamic modal forms
- Fetch and render constructor forms
- Submit via ixsl: fetch API
Status Workflows
- Status badges with colors
- Transition buttons
- PATCH requests to update state
Drag-and-Drop
- Kanban boards
- Reorderable lists
-
ixsl:ondragstart,ixsl:ondrophandlers
vs. React/Vue/JavaScript:
- ✅ No build step (beyond SEF generation)
- ✅ Unified language (XSLT for both server + client)
- ✅ RDF-native (templates match RDF structures naturally)
- ✅ Server/client code reuse (same templates work both sides)
- ✅ Type-safe (XSLT 3.0 type system)
- ✅ LLMs can generate XSLT (declarative, structured)
Component Library Pattern:
/static/xsl/components/
├── tabs.xsl # Tab containers
├── table.xsl # Interactive tables with sort/filter
├── modal.xsl # Modal dialogs
├── form.xsl # Dynamic forms from constructors
├── badges.xsl # Status indicators
├── bulk-ops.xsl # Bulk selection and actions
└── dashboard.xsl # Widget layouts
Each component is:
- Declarative (XSLT templates)
- Interactive (ixsl: event handlers)
- Reusable (import into packages)
- Themeable (CSS classes)
With Saxon-JS + proposed view features, you can build enterprise CRUD applications (CRM, ERP, order management, issue tracking) using:
Schema.org vocabulary for domain entities:
-
schema:Organization,schema:Person(contacts/accounts) -
schema:Order,schema:OrderItem(orders) -
schema:Invoice(billing) -
schema:Event(scheduling)
XSLT components for interactive UIs:
- Tabbed organization detail pages
- Sortable/filterable order tables
- Bulk operations on records
- Inline contact creation
- Status workflow transitions
Result: Functional enterprise applications with modern UX, fully declarative architecture, and LLM-friendly codebase.
The UI may not have the visual polish of modern SaaS (which uses component libraries like Material/Ant Design), but provides all essential functionality for internal business applications, MVPs, and enterprise tools.
Support inverse linking direction:
ldh:inverseProperty skos:broader .Creates: <new-item> skos:broader <parent> instead of <parent> skos:narrower <new-item>
Allow creating multiple items at once from view.
Edit items directly in view (not just creation).
Multiple views with tabs/switcher UI (requires additional metadata like ldh:label, ldh:priority, ldh:default).
Develop reusable XSLT component library:
- Tab containers
- Interactive tables
- Modal dialogs
- Bulk operation patterns
- Status badges and workflows
- Dashboard layouts
Agreed Vocabulary:
-
ldh:property- Linking property (required for inline creation) -
ldh:container- Container URL for PUT (required for inline creation) -
ldh:targetClass- Target class override (optional) -
ldh:showWhenEmpty- Control view visibility when no results (optional, default: true)
Approach:
- Pattern inference for simple cases
- Explicit metadata for complex cases
- Modal form with slug for PUT creation
- Conditional view rendering based on results
This enables inline creation in views with minimal metadata while maintaining LinkedDataHub's declarative architecture.