dile-crud-single
The dile-crud-single component is designed to display the details of a record from a REST API, along with the available actions for it and any related resources.
Installation
npm i @dile/crud
Usage
Import the dile-crud component.
import '@dile/crud/components/single/crud-single.js';
Use the component.
<dile-crud-single
relatedId="1"
.config="${this.config}"
></dile-crud-single>
Properties
- config: Object, the configuration object that customizes the behavior and appearance of the
dile-crud-singlecomponent. - relatedId: String, the identifier of the specific record that is being displayed and managed by the component.
- element: Object, the item object containing the details of the record to be displayed.
- actionIds: Array, containing the identifier of the item being managed in the
dile-crud-singlecomponent. This identifier is used to trigger batch actions in the CRUD system. Although this component displays a single record, an array is used for actions, with a single identifier in the array. - language: String, the interface and feedback messages language. Available 'en', 'es'. Fallback to 'en'.
Methods
- refresh(): Reloads the current data for the record being displayed. This method is used to update the view after changes have been made to the record.
- edit(): Opens the form to edit the current record being displayed by the
dile-crud-singlecomponent. - triggerAction(actionName, initialData): Programmatically triggers an action. The optional
initialDataparameter allows you to pre-populate the action's form with initial values. This is useful when you need to open an action with some data already filled in. The data will only be applied for that particular opening; after the action is completed or cancelled, the form will be reset on the next opening. - triggerDirectAction(actionName, initialData): An alias for
triggerAction()that triggers direct actions defined in the configuration.
Triggering Actions with Initial Data
You can programmatically trigger an action and pre-populate its form with initial data. This is particularly useful when an action needs to create something related to the current item.
For example, if you have a "Create Invoice" button next to each habitual product, you can open the action with the product already selected:
const crudSingle = document.querySelector('dile-crud-single');
crudSingle.triggerAction('CreateInvoiceDraftFromHabitualProductsAction', {
products: [{ product_uuid: uuid, units: 1 }],
});
The form will be populated with the provided data and displayed to the user. After the action completes (either successfully or via cancellation), the form will reset on the next opening, ensuring a clean state for new operations.
Events
This component is built on multiple components from the @dile/crud catalog. As a result, it can dispatch a large number of events, such as those detailed in dile-crud, dile-crud-update, dile-crud-item-delete, and others.
Action events:
The dile-crud-single component includes actions functionality via dile-crud-single-actions. The following events are dispatched by the actions system and propagate through the component hierarchy:
- crud-action-requested: Dispatched when the user requests to open an action form. The event detail includes the
actionproperty with the name of the action requested. - crud-action-start: Dispatched when an action is about to be sent to the server. The event detail includes
action(the name of the action) andactionIds(the ID of the item). - crud-action-success: Dispatched when an action succeeds. The detail of this event includes:
actionname of the actionmsgmessage sent from the backenddatawith any additional data that the backend may have sent as a response.
- crud-action-error: Dispatched when an action encounters an error. The event detail includes
msg(message from the server response) anderrors(validation errors). The error messages will appear next to their corresponding elements in the action form.
Other useful events:
- crud-item-detail-loaded: This event is dispatched when the item details have been successfully loaded from the API. The event detail includes the loaded data.
- crud-item-detail-load-error: This event is dispatched when the item details can't be loaded.
- crud-update-success: Dispatched when an update operation is successfully completed. It sends the same detail that is received from the
dile-ajax-formcomponent.
Refer to the documentation of other components to discover many more events available in dile-crud-single.
CSS Custom Properties
You can customize it using CSS Custom Properties.
| Custom property | Description | Default |
|---|---|---|
| --dile-primary-dark-color | Actions bar background color | #d7d353 |
| --dile-box-selector-item-text-align | Text alignment for box selector items | center |
| --dile-box-selector-item-font-weight | Font weight for box selector items | bold |
| --dile-card-border | Border style for action card | none |
| --dile-card-background-color | Background color for action card | var(--dile-secondary-light-color, #beed8e) |
| --dile-card-text-color | Text color for action card | var(--dile-on-secondary-light-color, #303030) |
| --dile-box-selector-item-background-color | Background color for box selector items on hover | var(--dile-primary-light-color, #fbfbe9) |
| --dile-box-selector-item-color | Text color for box selector items on hover | var(--dile-on-primary-light-color, #303030) |
| --dile-box-selector-item-border | Border for box selector items on hover | 4px solid var(--dile-primary-color, #f3f3ae) |
| --dile-crud-action-color | Color for action buttons and icons | #888 |
| --dile-on-crud-action-color | Text/foreground color for action buttons | #fff |
The dile-crud-detail component, used internally to display the details of the element, has additional CSS custom properties available for customization.
Generating Single Components with the CLI
You can use the Dile Components CLI to create the scaffolding for single components from @dile/crud that are already configured for use with dile-crud-single. To do this, run the following command:
dile g-single post/post-single
This command requires that the resource configuration file has been generated previously, which you can do using the CLI command
g-resource-config. More information is available on the resource configuration page.
If you want to see the help with the complete options for this command, run:
dile g-single --help
Configuration
Please refer to the general documentation to find the established mechanisms for configuring the dile-crud component.
Examples
Configuration object
Find the guides for creating the configuration object on the resource configuration page.
<script type="module">
import { html } from 'lit';
import { CrudConfigBuilder } from '@dile/crud/lib/CrudConfigBuilder';
import '@dile/ui/components/pages/pages';
// For the correct functioning of this declaration in the demo system, we have defined the variable with the configuration object globally. Normally, it would be created in a module and exported.
window.countryConfig = new CrudConfigBuilder('https://timer.escuelait.com/api/countries', {
templates: {
item: (country) => html`<demo-country-item .country=${country}></demo-country-item>`,
insertForm: () => html`<demo-country-form id="insertform"></demo-country-form>`,
updateForm: () => html`<demo-country-form id="updateform"></demo-country-form>`,
help: () => html`<p>This is the help provided to the countries resource.</p>`,
detail: (country) => html`<demo-country-detail .country="${country}"></demo-country-detail>`,
relations: (country) => html`<demo-country-relations .country=${country}></demo-country-relations>`,
formSingleActions: (actionName, country) => html`
<dile-pages attrForSelected="action" selected="${actionName}">
<demo-set-europe-as-continent-action action="SetEurope" .country=${country}></demo-set-europe-as-continent-action>
<demo-set-asia-as-continent-action action="SetAsia" .country=${country}></demo-set-asia-as-continent-action>
</dile-pages>
`,
},
customization: {
disablePagination: true,
disableHelp: false,
disableKeywordSearch: false,
disableSort: false,
disableFilter: false,
},
actions: {
single: [
{
name: "SetEurope",
label: "Set Europe as continent"
},
{
name: "SetAsia",
label: "Set Asia as continent"
},
]
},
labels: {
helpTitle: 'Country help',
},
sort: {
options: [
{
name: 'name',
label: 'Name',
direction: 'desc'
},
],
initialSortField: 'name',
},
availableFilters: [
{
name: 'continent',
label: 'Continent',
active: false,
value: false,
type: 'select',
options: [
{
value: 'Europe',
label: 'Europe'
},
{
value: 'Africa',
label: 'Africa'
},
{
value: 'Asia',
label: 'Asia'
},
]
},
],
});
</script>
Resource form component
The resource form provides the necessary fields for editing the record.
Instructions on how to create these forms can be found in the dile-crud-insert and dile-ajax-form component documentation.
<script type="module">
import { LitElement, html, css } from 'lit';
import '@dile/ui/components/input/input.js';
import '@dile/ui/components/select/select.js';
import { DileForm } from '@dile/ui/mixins/form'
export class DemoCountryForm extends DileForm(LitElement) {
static styles = [
css`
:host {
display: block;
}
`
];
render() {
return html`
<dile-input label="Country name" name="name" id="name" hideErrorOnInput></dile-input>
<dile-input label="Slug" name="slug" id="slug" hideErrorOnInput></dile-input>
<dile-select name="continent" id="continent" label="Continent" hideErrorOnInput>
<select slot="select">
<option value="">Select...</option>
<option value="Europe">Europa</option>
<option value="South America">América del Sur</option>
<option value="North America">Norte América</option>
<option value="Asia">Asia</option>
<option value="Africa">Africa</option>
<option value="Oceania">Oceania</option>
</select>
</dile-select>
`;
}
}
customElements.define('demo-country-form', DemoCountryForm);
</script>
<demo-country-form></demo-country-form>
Detail template component
Technically, it's not necessary to build a component to define the details of the item you want to display, as a simple template would be enough. However, to keep things organized and reusable, you would typically use a component dedicated to displaying the details, which also allows you to include additional functionality if needed.
<script type="module">
import { LitElement, html, css } from 'lit';
export class DemoCountryDetail extends LitElement {
static styles = [
css`
:host {
display: block;
color: #303030;
}
p {
margin: 0 0 1rem;
}
.last {
margin-bottom: 0;
}
`
];
static get properties() {
return {
country: { type: Object }
};
}
render() {
return html`
<p>Name: ${this.country.name}</p>
<p class="last">Continent: ${this.country.continent}</p>
`;
}
}
customElements.define('demo-country-detail', DemoCountryDetail);
</script>
Action components
Some actions that can be performed from the crud-single component include:
<script type="module">
import { LitElement, html, css } from 'lit';
import { DileForm } from '@dile/ui/mixins/form';
class DemoSetEuropeAsContinentAction extends DileForm(LitElement) {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
country: { type: Object }
};
}
render() {
return html`
<p>Do you really want to set Europe as continent of ${this.country?.name}?</p>
`;
}
}
customElements.define('demo-set-europe-as-continent-action', DemoSetEuropeAsContinentAction);
class DemoSetAsiaAsContinentAction extends DileForm(LitElement) {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
country: { type: Object }
};
}
render() {
return html`
<p>Do you really want to set Asia as continent of ${this.country?.name}?</p>
`;
}
}
customElements.define('demo-set-asia-as-continent-action', DemoSetAsiaAsContinentAction);
</script>
Component for Relationships
The single pages of a CRUD can display records of related entities using dile-crud components, filtered by the belongsTo and relationId properties.
For this relationship to work, we need the components required by the board games CRUD itself.
Configuration object
Find the guides for creating the configuration object on the resource configuration page.
<script type="module">
import { html } from 'lit';
import { CrudConfigBuilder } from '@dile/crud/lib/CrudConfigBuilder';
import { ResponseApiAdapter } from '@dile/crud/lib/ResponseApiAdapter';
class BoardGameResponseApiAdapter extends ResponseApiAdapter {
getElementList() {
return this.response.data.result.data;
}
}
// For the correct functioning of this declaration in the demo system, we have defined the variable with the configuration object globally. Normally, it would be created in a module and exported.
window.boardGameConfig = new CrudConfigBuilder('https://timer.escuelait.com/api/board-games', {
customization: {
hideCountSummary: false,
hideCheckboxSelection: false,
disablePagination: false,
disableHelp: true,
disableKeywordSearch: false,
disableSort: false,
disableFilter: false,
},
sort: {
options: [
{
name: 'name',
label: 'Name',
direction: 'asc'
},
{
name: 'year',
label: 'Year',
direction: 'desc'
},
],
initialSortField: 'year',
},
availableFilters: [
{
name: 'essential',
label: 'Is essential',
active: false,
value: false,
type: 'boolean',
},
],
responseAdapter: new BoardGameResponseApiAdapter(),
actions: {
list: [
{
label: 'Delete board games',
name: 'DeleteAction'
},
{
label: 'Change Essential',
name: 'DemoChangeEssentialAction'
},
{
label: 'Change Name',
name: 'DemoChangeNameAction'
},
],
},
templates: {
item: (boardGame) => html`<demo-board-game-item .boardGame=${boardGame}></demo-board-game-item>`,
insertForm: (belongsTo, relationId) => html`<demo-board-game-form id="insertform" belongsTo="${belongsTo}" relationId="${relationId}"></demo-board-game-form>`,
updateForm: () => html`<demo-board-game-form id="updateform"></demo-board-game-form>`,
formActions: (actionName, actionIds) => html`
<dile-pages attrForSelected="action" selected="${actionName}">
<dile-crud-delete-action action="DeleteAction"></dile-crud-delete-action>
<demo-change-essential-action action="DemoChangeEssentialAction" .actionIds=${actionIds}></demo-change-essential-action>
<demo-change-name-action action="DemoChangeNameAction" .actionIds=${actionIds}></demo-change-name-action>
</dile-pages>
`,
},
});
</script>
Item component
The item component to display each of the elements in the list.
<script type="module">
import { LitElement, html, css } from 'lit';
export class DemoBoardGameItem extends LitElement {
static styles = [
css`
:host {
display: block;
color: var(--dile-on-background-color, #303030);
}
`
];
static get properties() {
return {
boardGame: { type: Object }
};
}
render() {
return html`
${this.boardGame.name} - (${this.boardGame.year})
`;
}
}
customElements.define('demo-board-game-item', DemoBoardGameItem);
</script>
Resource form component
The resource form.
<script type="module">
import { LitElement, html, css } from 'lit';
import '@dile/ui/components/input/input.js';
import '@dile/ui/components/input/input-integer.js';
import '@dile/ui/components/checkbox/checkbox.js';
import '@dile/crud/components/ajax-select-crud/ajax-select-crud';
import { DileForm } from '@dile/ui/mixins/form'
export class DemoBoardGamesForm extends DileForm(LitElement) {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
belongsTo: { type: String },
relationId: { type: String },
};
}
firstUpdated() {
if(this.belongsTo == "country" && this.relationId) {
this.shadowRoot.getElementById('countryselect').value = this.relationId;
}
}
render() {
return html`
<dile-input label="Nombre" name="name" id="name" hideErrorOnInput></dile-input>
<dile-input label="Slug" name="slug" id="slug" hideErrorOnInput></dile-input>
<dile-input-integer name="year" label="Year" hideErrorOnInput id="year"></dile-input-integer>
<dile-ajax-select-crud
id="countryselect"
idProperty="id"
name="country_id"
label="País"
endpoint="https://timer.escuelait.com/api/countries"
queryStringVariable="keyword"
placeholder="Buscar país"
resultDataProperty="data"
displayProperty="name"
selectDefaultPlaceholder="Seleccionar país..."
></dile-ajax-select-crud>
<p><dile-checkbox name="essential">Essential</dile-checkbox></p>
`;
}
}
customElements.define('demo-board-game-form', DemoBoardGamesForm);
</script>
<demo-board-game-form></demo-board-game-form>
Action component
The board game custom batch action.
<script type="module">
import { LitElement, html, css } from 'lit';
import { DileForm } from '@dile/ui/mixins/form';
import '@dile/ui/components/select/select.js';
export class DemoChangeEssentialAction extends DileForm(LitElement) {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
actionIds: { type: Array }
};
}
constructor() {
super();
this.actionIds = [];
}
render() {
return html`
<p>Change essential game state of ${this.actionIds.length} elements.</p>
<dile-select name="essential">
<select slot="select">
<option value="0">Not Essential</option>
<option value="1">Essential</option>
</select>
</dile-select>
`;
}
}
customElements.define('demo-change-essential-action', DemoChangeEssentialAction);
</script>
Single relations component
This is the component that allows you to define relationships. In this component, we use a dile-crud, but technically you could include any type of content.
<script type="module">
import { LitElement, html, css } from 'lit';
export class DemoCountryRelations extends LitElement {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
boardGameConfig: { type: Object },
country: { type: Object },
};
}
constructor() {
super();
this.boardGameConfig = boardGameConfig.getConfig();
}
firstUpdated() {
console.log(this.country);
}
render() {
return html`
${this.country
? html `
<dile-crud
title="Juegos de ${this.country.name}"
.config="${this.boardGameConfig}"
belongsTo="country"
relationId="${this.country.id}"
></dile-crud>
`
: ''
}
`;
}
}
customElements.define('demo-country-relations', DemoCountryRelations);
</script>
Crud single Component
<script type="module">
import { LitElement, html, css } from 'lit';
import '@dile/crud/components/single/crud-single'
export class DemoCountrySingle extends LitElement {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
config: { type: Object },
};
}
constructor() {
super();
this.config = window.countryConfig.getConfig();
}
render() {
return html`
<dile-crud-single
relatedId="1"
.config="${this.config}"
></dile-crud-single>
`;
}
}
customElements.define('demo-country-single', DemoCountrySingle);
</script>
<demo-country-single></demo-country-single>
dile-components