---
sidebar_navigation:
title: Inline editing
description: Get an overview of how inline-editing of resources works
robots: index, follow
keywords: development concepts, inline editing, edit forms
---
# Development Concept: Inline Editing
Inline editing is a core functionality of work packages and other attributes.
![Inline editing overview in the single view of a work package](single-view-inline-editing.gif)
## Key takeaways
*Inline editing ...*
- wraps HTML or complex form elements (such as the WYSIWYG editor)
- has two modes: **Display** (inactive, show mode) and **Edit** (Active, input mode)
- uses a resource and its schema to determine what kind of form element to show
- can be used for work packages and other HAL resources that have a schema
- `DisplayField` and `EditField` are two separate sets of classes to render attributes based on their schema type
## Prerequisites
In order to understand Inline Editing, you will need the following concepts:
- [HAL resources](TODO:hal-resource)
- [Schemas](../resource-schemas)
- [Changesets](../resource-changesets)
## Components overview
In order to understand the different modes of the inline edition functionality, we will first look at the components and code that handle displaying of resource attributes, the display fields.
### Display fields
The display fields handle showing read-only representation of a resource's attribute. For example, the work package table may contain very different kinds of attributes: A progress bar, bare text fields, formatted date fields and the like.
Since OpenProject can also have dynamic custom fields with varying formats, the frontend cannot know all potential attribute names and their types. Instead, the available attributes of a resource are retrieved from its associated [schema resource](#TODO). For display fields, the important part of the schema definition for an attribute is its `type` attribute. Take a look at the JSON schema response for projects at the community: https://community.openproject.com/api/v3/projects/schema. For the sake of brevity, the following JSON will only show two of the returned attributes: The name and status attribute description:
```json
"name": {
"type": "String",
"name": "Name",
"required": true,
"hasDefault": false,
"writable": true,
"minLength": 1,
"maxLength": 255,
"options": {}
},
"status": {
"type": "ProjectStatus",
"name": "Status",
"required": false,
"hasDefault": false,
"writable": true,
"options": {}
},
...
```
The `type` property will decide that for name, we're looking for a display field that can handle `String` type, while for the project `status` attribute, we're looking for a specific display type called `ProjectStatus`.
In the frontend, multiple display field classes exist and the [`DisplayFieldService`](https://github.com/opf/openproject/blob/dev/frontend/src/app/modules/fields/display/display-field.service.ts) resolves what display field class is going to be used for which attribute. [There is a mapping file](https://github.com/opf/openproject/blob/dev/frontend/src/app/modules/fields/display/display-field.initializer.ts) between the types returned by the API and the used display field class. In some cases such as for `id`, a specific field is set based on the attribute name, not its type.
With a resource and its schema present, there are multiple ways to render a display field for a given attribute.
#### Rendering in plain JavaScript: `DisplayFieldRenderer`
Since parts of the application are rendered in plain JavaScript (such as the work package table), most display fields are actually rendered explicitly to a DOM element through the [`DisplayFieldRenderer#render`](https://github.com/opf/openproject/blob/dev/frontend/src/app/modules/fields/display/display-field-renderer.ts) method. You will only need the resource with its schema loaded and the attribute name.
The `DisplayFieldRenderer` requires the Angular injector for injecting services such as the `DisplayFieldService`. It's instance will cache field types instantiated for performance reasons in large rendering contexts, such as the work package table.
Minimal example, rendering the status attribute of a work package to the element of the exemplary component:
```typescript
@Component({ selector: 'minimal-example', template: '' })
export class ExampleComponent implements OnInit {
// The work package to render
@Input() workPackageId:string;
constructor (private elementRef:ElementRef,
private injector:Injector,
private apiV3Service:ApiV3Service) {}
ngOnInit() {
this
.apiV3Service
.work_packages
.id(this.workPackageId)
.get()
.subscribe(workPackage => {
const fieldRenderer = new DisplayFieldRenderer(injector, 'table');
const displayElement = fieldRenderer.render(workPackage, 'status', null);
this.elementRef.nativeElement.appendChild(displayElement);
});
}
}
```
The third parameter of the `render` method is to provide a changeset. This allows to render the value notfrom the pristine work package, but from a temporary changeset of the work package:
```typescript
// Assuming we changed the subject property
const changeset = new WorkPackageChangeset(workPackage);
```
#### Rendering as angular component: `DisplayFieldComponent`
To render an attribute of a HAL resource explicitly, a display field can be used through the [`DisplayFieldComponent`](https://github.com/opf/openproject/blob/dev/frontend/src/app/modules/fields/display/display-field-renderer.ts). In most cases, you will not render the display field alone as it would expect to inline editable. This is why when it's used, it often explicitly disables the writable CSS styles with `writable: false` set.
Minimal example, rendering the status attribute of a work package
```html