ember-rdfa-helpers
v0.7.0
Published
Helpers for outputting RDFa in EmberJS applications.
Downloads
12
Keywords
Readme
ember-rdfa-helpers
Provides helpers for adding RDFa in Ember apps. The predicates are obtained from a meta-model defined on the rendered objects, the output is tackled by using helper components.
Compatibility
- Ember.js v3.24 or above
- Ember CLI v3.24 or above
- Node.js v12 or above
Usage
Usage consists of two portions. On one side, the correct metamodel needs to be configured on the model class for which RDFa will be offered. For rendering the contents, contextual components need to be injected into the templates.
Metamodel
The metamodel is subject to change. As it stands, it is a simple key-value mapping for each of the possible properties. A special class
property is added to indicate the type of the resource.
It is assumed that each model also contains a uri
property which stores the URI of the specific resource. An example for a foaf user could look like this:
export default class PersonModel extends Model {
@attr uri;
@attr firstName;
@attr lastName;
@attr profilePicture;
@belongsTo('project') currentProject;
@hasMany('account') accounts;
rdfaBindings = {
class: 'http://schema.org/Person',
firstName: 'http://schema.org/givenName',
lastName: 'http://schema.org/familyName',
profilePicture: 'http://schema.org/image',
currentProject: 'foaf:currentProject',
accounts: 'foaf:holdsAccount'
};
}
For properties that must be annotated with a datatype, an object containing a property
and datatype
key can be passed in the mapping. The content of properties defined with datatype xsd:date
or xsd:dateTime
will be automatically formatted according to the ISO8601 standard.
E.g.
export default class SomethingModel extends Model {
@attr('string') uri;
@attr('string') title;
@attr('date') created;
rdfaBindings = {
class: 'http://schema.org/Something',
title: 'dc:title',
created: {
property: 'dc:created',
datatype: 'xsd:dateTime'
}
};
};
Component helpers (>= v0.2.0)
The component helpers have evolved between versions. The library currently supports a modernized and an older syntactic version.
The new version is described by:
with-rdfa-context
with nested componentsctx.get
,ctx.each.get
andctx.img
.rdfa/link-to
Both syntaxes cannot be mixed.
The dummy application of this addon contains plently of examples. To see it in action:
git clone <repository-url>
cd ember-rdfa-helpers
ember serve
with-rdfa-context
Sets up a new RDFa context. This is equivalent to the old with-rdfa-resource
.
This component takes the following arguments:
- required
model
: object for which the context will be set up. - optional
vocab
: Vocabulary to be used by default (for future use). - optional
tagName
: tagname used for the wrapping component. Defaults todiv
.
It returns the following:
ctx
: Context on which nested components are defined (see below).
Example:
<WithRdfaContext @model={{person}} @vocab="http://schema.org" as |ctx|>
...
</WithRdfaContext>
The following paragraphs explain the different ways of constructing RDFa data using the received context ctx
. We made the distinction between get
and other accessors that look like common HTML-tags such as div
and span
. get
doesn't create an HTML element, but trusts the user to use the {{rdfa}}
modifier on the correct tag in the body to add the RDFa attributes, while the other accessors do create the elements with attributes automatically. Keep in mind that modifiers do not run during Ember FastBoot, so using the {{rdfa}}
modifier does not work. Use the other accessors like div
and span
to create elements with RDFa properties during FastBoot. This does in fact create more nested components in Ember and could possibly negatively impact performance, compared to using the modifier.
ctx.get
and others
Gets a property/relation from the context and applies the right bindings.
The following can be supplied to ctx.get
:
- required
prop
: name of the JavaScript property of context which will be rendered. - optional
property
: override the property with a different semantic property. - optional
link={{true|false}}
(defaultfalse
): creates a link to the related resource. The related resource URI will be set ashref
attribute. This should be used for URLs outside your application. Otherwise, use thelink-to
orhref-to
option. - optional
link-to
: creates a link to the related resource using an Ember Route path. The related resource URI will be set asresource
attribute, while the passed route path, with the related resource id as argument, will be set ashref
attribute. - optional
href-to
: creates a link to the related resource using an Ember Route URL. The related resource URI will be set asresource
attribute, while the passed route URL will be set ashref
attribute. Use the{{href-to}}
helper of ember-href-to to construct the URL. - optional
useUri={{true|false}}
(defaultfalse
): only applicable iflink-to
orhref-to
are set. Sets the resource URI ashref
instead ofresource
attribute on the created link. - optional
overrideUri={{true|false}}
(defaultfalse
): use this option to replace the URI of the resource with thehref
supplied vialink-to
ofhref-to
. Use this to refer to a part of the web application as a resource. Most likely used with an overridenproperty
as well.
The component supports a block format as well as a non-block format. Only in case link-to
is used, a block must be passed.
Non-block format
The non-block format doesn't allow any customization of the rendered output (e.g. tag name, CSS classes).
Examples:
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.get @prop="name" />
</WithRdfaContext>
<WithRdfaContext @model={{account}} as |ctx|>
<ctx.get @prop="accountServiceHomepage" @link={{true}} />
</WithRdfaContext>
Alternatively, use one of the supported subcomponents to create an element other than a simple span. Supported subcomponents are span
, div
, link
, a
and p
.
Example:
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.div @prop="name" />
</WithRdfaContext>
This example will do exactly as above, but by creating a div
instead of an implicit span
. Note that when creating a link with @link={{true}}
, you should be carefull to use ctx.a
to explicitely create a link or ctx.get
to implicitely create a link. Other elements won't create a proper link.
Block format
ctx.get
The block format using ctx.get
receives the following params:
elements
: RDFa attributes to apply on a node using the{{rdfa}}
modifiervalue
: value of the propertyctx
: new context to create nested annotations (only passed in case the value is a resource, not a literal)
The block format with get
offers more flexibility in terms of layout and rendering, but the user is responsible to apply the RDFa attributes it receives as a param on a node in the block using the {{rdfa}}
modifier. If the elements
param is not applied on a node, the content will not be annotated.
Examples:
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.get @prop="name" as |elements value|>
<div {{rdfa elements}} class="some-custom-css-class">{{value}}</div>
</ctx.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.get @prop="birthDate" as |elements value|>
<span {{rdfa elements}}>{{format-date value}}</span>
</ctx.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.get @prop="homepage" @link={{true}} as |elements|>
<a {{rdfa elements}}>my homepage</a>
</ctx.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.get @prop="currentProject" as |elements value projectCtx|>
<div {{rdfa elements}}>
<projectCtx.get @prop="name" />
<projectCtx.get @prop="budget" as |elements value|>
<span {{rdfa elements}}>{{format-amount budget}}</span>
</projectCtx.get>
</div>
</ctx.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<!-- Link to the project detail pages on /projects/:id -->
<ctx.get @prop="currentProject" @link-to="projects.show" as |value|>
{{value.name}}
</ctx.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<!-- Link to the project detail pages on /persons/:person_id/projects/:project_code -->
<ctx.get @prop="currentProject" @href-to=(href-to "persons.person.projects.show" person.id person.currentProject.code) as |value|>
{{value.name}}
</ctx.get>
</WithRdfaContext>
Warning: Even though the elements
property can be renamed by the user, when using ctx.get
in its angle brackets form, don't replace it by attrs
.
This keyword is already used by the core code of components in Ember Octane.
ctx.span
, ctx.div
, ctx.p
, ctx.li
, ctx.a
, ctx.link
, ...
The block format using these subcomponents receives the following params:
value
: value of the propertyctx
: new context to create nested annotations (only passed in case the value is a resource, not a literal)
Note the missing elements
parameter from ctx.get
. This is because an HTML element is spawned with the correct RDFa properties attached and is does not make sense to place those properties more than once in the HTML.
The mechanism for all supported subcomponents is similar to get
. The following example shows two equivalent blocks, one using ctx.get
and one using ctx.div
to demonstrate their use.
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.get @prop="name" as |elements value|>
<div {{rdfa elements}} class="some-custom-css-class">The value is: {{value}}</div>
</ctx.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.div @prop="name" class="some-custom-css-class" as |value|>
The value is: {{value}}
</ctx.div>
</WithRdfaContext>
ctx.each.get
Allows looping over a relationship. The interface is very similar to ctx.get
.
The following can be supplied to ctx.each.get
:
- required
prop
: name of the JavaScript property of context which will be rendered. - optional
property
: override the property with a different semantic property. - optional
link={{true|false}}
(defaultfalse
): creates a link to the related resource. The related resource URI will be set ashref
attribute. This should be used for URLs outside your application. Otherwise, use thelink-to
orhref-to
option. - optional
link-to
: creates a link to the related resource using an Ember Route path. The related resource URI will be set asresource
attribute, while the passed route path, with the related resource id as argument, will be set ashref
attribute. - optional
href-to
: creates a link to the related resource using an Ember Route URL. The related resource URI will be set asresource
attribute, while the passed route URL will be set ashref
attribute. Use the{{href-to}}
helper of ember-href-to to construct the URL. - optional
useUri={{true|false}}
(defaultfalse
): only applicable iflink-to
orhref-to
are set. Sets the resource URI ashref
instead ofresource
attribute on the created link. - optional
overrideUri={{true|false}}
(defaultfalse
): use this option to replace the URI of the resource with thehref
supplied vialink-to
ofhref-to
. Use this to refer to a part of the web application as a resource. Most likely used with an overridenproperty
as well.
The component supports a block format as well as a non-block format.
Non-block format
The non-block format places a span
in the HTML with the proper RDFa properties attached, containing the value of the property.
Example:
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.each.get @prop="nicknames" />
</WithRdfaContext>
<WithRdfaContext @model={{person}} as |ctx|>
<ctx.each.get @prop="homepages" @link={{true}} />
</WithRdfaContext>
Block format
ctx.each.get
The block format using ctx.each.get
receives the following params:
elements
: RDFa attributes to apply on a node using the{{rdfa}}
modifiervalue
: value of the propertyctx
: new context to create nested annotations (only passed in case the value is a resource, not a literal)index
: index of the value in the array
The block format using ctx.each.get
offers more flexibility in terms of layout and rendering, but the user is responsible to apply the RDFa attributes it receives as a param on a node in the block using the {{rdfa}}
modifier. If the elements
param is not applied on a node, the content will not be annotated.
Examples:
<WithRdfaContext @model={{project}} @tagName="ul" as |ctx|>
<ctx.each.get @prop="funders" @link={{true}} as |elements funder|>
<li><a {{rdfa elements}}>{{funder.firstName}} {{funder.lastName}}</a></li>
</ctx.each.get>
</WithRdfaContext>
<WithRdfaContext @model={{person}} @tagName="ul" as |ctx|>
<ul>
<ctx.each.get @prop="accounts" as |elements account accountCtx|>
<li {{rdfa elements}}>
<accountCtx.get @prop="accountServiceHomepage" @link={{true}} />
</li>
</ctx.each.get>
</ul>
</WithRdfaContext>
ctx.each.span
, ctx.each.div
, ctx.each.p
, ctx.each.link
, ctx.each.a
, ctx.each.li
, ctx.each.lia
, ...
The block format using these subcomponents receives the following params:
value
: value of the propertyctx
: new context to create nested annotations (only passed in case the value is a resource, not a literal)index
: index of the value in the array
Note the missing elements
parameter from ctx.get
. This is because an HTML element is spawned with the correct RDFa properties attached and is does not make sense to place those properties more than once in the HTML.
The mechanism for all supported subcomponents is similar to get
. The following example shows two equivalent blocks, one using ctx.each.get
and one using ctx.each.div
to demonstrate their use.
<WithRdfaContext @model={{project}} @tagName="ul" as |ctx|>
<ctx.each.get @prop="funders" @link={{true}} as |elements funder|>
<li><a {{rdfa elements}}>{{funder.firstName}} {{funder.lastName}}</a></li>
</ctx.each.get>
</WithRdfaContext>
<WithRdfaContext @model={{project}} @tagName="ul" as |ctx|>
<ctx.each.lia @prop="funders" @link={{true}} as |funder|>
{{funder.firstName}} {{funder.lastName}}
</ctx.each.lia>
</WithRdfaContext>
ctx.img
Displays a property as an image and applies the right bindings. In case the value is a resource, the resource URI will be set as src
attribute. Otherwise the value itself will be set as src
.
The following can be supplied to ctx.img
:
- required
prop
: name of the JavaScript property of context which will be rendered. - optional
property
: override the property with a different semantic property.
The following arguments can be passed and will be applied on the img
tag: altName
, width
, height
, srcset
, sizes
Example:
<WithRdfaContext @model={{model.person}} as |ctx|>
<ctx.img @prop="profilePicture" alt="Profile pic" width=100 height=100 />
</WithRdfaContext>
rdfa/link-to
Creates an annotated link in the app.
The component takes the following arguments:
- required
value
: resource to link to - optional
link-to
: creates a link to the value using an Ember Route path. The value URI will be set asresource
attribute, while the passed route path, with the value id as argument, will be set ashref
attribute. - optional
href-to
: creates a link to the value using an Ember Route URL. The value URI will be set asresource
attribute, while the passed route URL will be set ashref
attribute. Use the{{href-to}}
helper of ember-href-to to construct the URL. - optional
useUri={{true|false}}
(defaultfalse
): Sets the value URI ashref
instead ofresource
attribute on the created link. - optional
overrideUri={{true|false}}
(defaultfalse
): use this option to replace the URI of the resource with thehref
supplied vialink-to
ofhref-to
. Use this to refer to a part of the web application as a resource. Most likely used with an overridenproperty
as well.
Example of creating links to projects from a list:
<ul>
{{#each model.projects as |project|}}
<li>
<Rdfa::LinkTo @link-to="projects.show" @value={{project}}>
{{project.name}}
</Rdfa::LinkTo>
</li>
{{/each}}
</ul>
Examples to demonstrate useUri
and overrideUri
(each example shows the template code and the produced HTML code):
Basic usage
<Rdfa::LinkTo @link-to="projects.show" @value={{project}}>
{{project.name}}
</Rdfa::LinkTo>
creates a link to an Ember route:
<a href="/projects/1234" typeof="http://xmlns.com/foaf/0.1/Thing" resource="https://github.com/lblod/ember-rdfa-helpers">
Project X
</a>
Using @useUri
<Rdfa::LinkTo @link-to="projects.show" @useUri={{true}} @value={{project}}>
{{project.name}}
</Rdfa::LinkTo>
creates a link to the URI of the project and leaves the redundant resource property:
<a href="https://github.com/lblod/ember-rdfa-helpers" typeof="http://xmlns.com/foaf/0.1/Thing">
Project X
</a>
Using @overrideUri
<Rdfa::LinkTo @link-to="projects.show" @overrideUri={{true}} @value={{project}}>
{{project.name}}
</Rdfa::LinkTo>
creates a link to an Ember route with the resource actually being part of the web application:
<a href="/projects/1234" typeof="http://xmlns.com/foaf/0.1/Thing" resource="/projects/1234">
Project X
</a>
Component helpers (v0.1.x)
In order to use the helpers, the first object's scope needs to be defined. This is done by use of the with-rdfa-resource
component. Other components can be used by depending on the context object retrieved from this component.
with-rdfa-resource
This is the main entrypoint for using RDFa in your templates.
{{#with-rdfa-resource resource=person vocab="http://schema.org" as |ctx|}}
...
{{/with-rdfa-resource}}
This component takes the following arguments:
- required
resource
: object for which the context will be set up. - optional
vocab
: Vocabulary to be used by default (for future use).
It returns the following:
ctx
: Context on which nested components are defined (see below).
ctx.prop
Renders an RDFa property with correct semantics. If a block is given, no content is rendered and only the semantic property is set.
{{#with-rdfa-resource resource=person as |ctx|}}
{{ctx.prop p="name"}}
{{/with-rdfa-resource}}
This component takes the following arguments:
- required
p
: JavaScript property of person which will be rendered. - optional
property
: Override the property with a different semantic property. - optional style: Style is set as an attribute on the tag.
- optional
block( value )
: If a block is given, it will be yielded rather than the contents. The block receives the value of the property as an argument.
ctx.each-as-link
Renders a has-many relationship as a link with correct semantics and yields a block for each of its values.
{{#with-rdfa-resource resource=person as |ctx|}}
{{ctx.prop p="name"}}
{{#ctx.each-as-link p="accounts" as |account actx|}}
{{actx.prop "name"}}
{{/ctx.each-as-link}}
{{/with-rdfa-resource}}
- required
p
: JavaScript property of person which will be rendered. - optional
property
: Override the property with a different semantic property. - optional style: Style is set as an attribute on the tag.
- required
block( value, context )
: A block must be given. It is yielded for each supplied entity of the relationship and receives the linked value, followed by a new context for that value. This context can be used to further create nested elements.
ctx.link
Renders a link to a different resource. Either because it links to a related object, or because the property contains a URL.
{{#with-rdfa-resource resource=person as |ctx|}}
{{#ctx.link p="profilePicture"}}my picture{{/ctx.link}}
{{/with-rdfa-resource}}
- required
p
: JavaScript property of person which will be rendered - optional
property
: Override the property with a different semantic property - optional style: Style is set as an attribute on the tag.
- optional
block( value, context )
: If a block is given, it is called with the supplied value and a context for further scoping. This only makes sense when used with a relationship.
ctx.src
Renders a relationship as an src property. This can be used to render images with the correct tag.
{{#with-rdfa-resource resource=person as |ctx|}}
{{#ctx.src tagName='img' p='profilePicture'}}{{/ctx.src}}
{{/with-rdfa-resource}}
- required
p
: JavaScript property of person which will be rendered - optional
property
: Override the property with a different semantic property - optional style: Style is set as an attribute on the tag.
- optional
block( value, context )
: If a block is given, it is rendered rather than injecting the contents of the property.
ctx.linked-resource
Sets up the context and scope for a resource in the relationship. Yields the resource and a new context for using the related elements.
{{#with-rdfa-resource resource=person as |ctx|}}
{{ctx.prop p="name"}}
{{#ctx.linked-resource p="idCard" as |card ictx|}}
{{ictx.prop p="number"}}
{{/ctx.linked-resource}}
{{/with-rdfa-resource}}
ctx.each-resource
Sets up the context and scope for each resource in the relationship. Yields the resource and a new context for using the related elements.
{{#with-rdfa-resource resource=person as |ctx|}}
{{ctx.prop p="name"}}
{{#ctx.each-resource p="accounts" as |account actx|}}
{{#actx.link p="accountPage"}}{{account.name}}{{/actx.link}} - {{actx.link p="serviceHomepage"}}
{{/ctx.each-resource}}
{{/with-rdfa-resource}}
ctx.with-each-context
Similar to ctx.each-as-link
, but only returns a context for setting new properties. This does not set any tags, hence the scope of the object isn't altered. You probably want to use ctx.each-resource
instead.
Development
Addon structure
The main entrypoint for the addon is the with-rdfa-context
component. This sets up the context for specific RDFa components which generate the necessary output, and creates a new scope for a specific resource.
Components which render output in a supplied context can be found in the rdfa
namespace. These components all inherit standard behaviour from the rdfa/standard
mixin. When these components are invoked, they receive the model
property (by using the parent context), and the prop
attribute which resembles the Ember model key which should be rendered. Additionally, they may all receive a property
attribute to override the RDFa property to be used.
If new forms of output should be supported, a new component should be made in this namespace and the yielded context should be updated in each of the related components.
Installation
git clone <repository-url>
this repositorycd ember-rdfa-helpers
npm install
Wishlist
- Support for inverse relations as RDFa
- Support for computed/composable values (e.g. sorted hasMany relation)