Home

Awesome

<p align="center"> <img width="20%" height="20%" src="./logo.svg" alt="project logo"> </p> <br />

MIT commitizen PRs styled with prettier All Contributors ngneat spectator

A flexible and unopinionated edit in place library for Angular applications

Edit in place is a complete solution for switching modes between static content and an editable control that allows editing it.

Following open/closed principle, the library focuses on the switch mechanism, giving you full control of the data you want to update and the content you want to display and how to edit it.

alt-text

Demo

Features

Installation

npm install @ngneat/edit-in-place

Usage

version 1.9 and above

The project is now using the Angular Standalone API and support SSR. It requires Angular 16+

Imports the EditableComponent, EditModeDirective and ViewModeDirective in the ìmports array of your NgModule or your standaloneComponent:

import { EditableComponent, EditModeDirective, ViewModeDirective } from '@ngneat/edit-in-place';

@NgModule({
  ...
  standalone: true,
  imports: [EditableComponent, EditModeDirective, ViewModeDirective],
})
export class AppComponent {}

version 1.6 and below

Add the EditableModule to your AppModule.

import { EditableModule } from '@ngneat/edit-in-place';

@NgModule({
  declarations: [AppComponent],
  imports: [EditableModule],
  bootstrap: [AppComponent]
})
export class AppModule {}

Now you can use the <editable> component:

@Component({
  template: `
    <editable (save)="update()" (cancel)="cancel()">
      <ng-template viewMode>{{ value }}</ng-template>

      <ng-template editMode>
        <input editableOnEnter editableOnEscape [formControl]="control" />
      </ng-template>
    </editable>
  `
})
class MyComponent {
  value = 'foo';
  control = new FormControl(this.value);

  update() {
    this.value = this.control.value;
  }

  cancel() {
    this.control.setValue(this.value);
  }
}

For more complex examples, check out the playground.

Changing the Active Mode

Click on the viewMode template to switch it to editMode or click outside the editable component to switch back to viewMode.

You can customize the switch trigger which set to click by default by providing a MouseEvent type:

<editable openBindingEvent="dblclick"
          closeBindingEvent="dblclick">
    ...
</editable>

You can also set this value globally by providing it in the EDITABLE_CONFIG provider:

@NgModule({
  ...
  providers: [
    {
      provide: EDITABLE_CONFIG, 
      useValue: {
        openBindingEvent: 'dblclick',
        closeBindingEvent: 'dblclick',
      } as EditableConfig
    }
  ]
})
export class AppModule {}

Handle Events Manually

You can use the editableOnUpdate and editableOnCancel directives to trigger the update or the reset of the value on chosen elements.

<editable (save)="updateField()" (cancel)="resetField()">
  <ng-template viewMode>...</ng-template>

  <ng-template editMode>
    <input formControlName="name">
    <button editableOnSave>Save</button>
    <button editableOnCancel>Cancel</button>    
  </ng-template>
</editable>

Track event changes

You can use the modeChange event to know what is the state of a given EditableComponent.

<editable (modeChange)="doWhatever()">
  <ng-template viewMode>...</ng-template>

  <ng-template editMode>
    <input formControlName="name">
    <button editableOnSave>Save</button>
    <button editableOnCancel>Cancel</button>    
  </ng-template>
</editable>

Handle Focus

As a focusable form tag might be nested or custom, it isn't focused by default when the editMode is displayed.

To make it focusable, you can add the editableFocus directive on the input:

<editable>

  <ng-template viewMode>
    ... 
  </ng-template>

  <ng-template editMode>
    <input editableFocusable formControlName="name">   
  </ng-template>
</editable>

Events

Add the (save) event binding to handle the update of the content.

<editable (save)="updateField()">
   ...
</editable>

The following actions will trigger this event:

Optionally you can add the (cancel) event binding to handle the reset of the value of a formControl:

<editable (cancel)="resetField()">
  ...
</editable>

The following actions will trigger this event:

Inputs

@InputTypeDescriptionDefault
openBindingEventstringThe MouseEvent type to display the editModeclick
closeBindingEventstringThe MouseEvent type to display the viewModeclick
enabledbooleanAllows or forbids edit mode (doesn't switch to it)true

Outputs

@OutputTypeDescription
savevoidtriggered by the editableOnSave and editableOnEnter directives and the MouseEvent on closeBindingEvent @Input
cancelvoidtriggered by the editableCancel and editableOnEscape directives
modeChangeedit or viewtriggered when the mode changes

Directives

editableFocusable

import { EditableFocusableDirective } from '@ngneat/edit-in-place';

Focus the host element when switching to editMode (for nested inputs).

editableOnEnter

import { EditableOnEnterDirective } from '@ngneat/edit-in-place';

Listen to keyup enter to switch to viewMode and update the value of the viewMode host element.

editableOnEscape

import { EditableOnEscapeDirective } from '@ngneat/edit-in-place';

Listen to keyup escape to switch to viewMode without updating the value of the viewMode host element.

editableOnSave

import { EditableOnSaveDirective } from '@ngneat/edit-in-place';

Listen to a MouseEvent on ths host element in order to switch to viewMode and udpate the value of the content of the viewMode*host element.

@InputTypeDescriptionDefault
saveEventstringThe MouseEvent type used to trigger the @Output() saveclick

editableOnCancel

Listen to a MouseEvent on ths host element in order to trigger to switch to viewMode without updating the value of the viewMode host element.

@InputTypeDescriptionDefault
cancelEventstringThe MouseEvent type used to trigger the @Output() cancelclick

Multiple Forms Usage

edit-in-place also supports switching between modes for multiple components at once.

Add the editableGroup directive on a parent html tag of your editable components:

<section editableGroup>
  <editable></editable>
  <editable></editable>
  <editable></editable>
</section>

Changing the Active Mode

Unlike using a single editable component, an editableGroup doesn't support MouseEvent events on the component to switch modes.

You can switch modes by using dedicated directives on html button tag to switch mode for the whole group:

<section editableGroup>
  <button editableGroupEdit>Edit</button>
  <button editableGroupSave>Save</button>
  <button editableGroupCancel>Cancel</button>
  <editable></editable>
  <editable></editable>
  <editable></editable>
</section>

Add the (editableModeChange) event binding to keep track of the active mode.

It's triggered by the editableGroupEdit, editableGroupSave and editableGroupCancel directives.

<section (editableModeChange)="handleModeChange($event)">
  <editable></editable>
  <editable></editable>
  <editable></editable>
</section>

Add the (save) event binding to handle the update of the group.
It's triggered by the editableGroupSave directive:

<section (save)="updateGroup()">
  <editable></editable>
  <editable></editable>
  <editable></editable>
</section>

Optionally you can add the (cancel) event binding to handle the reset of the value of the group.

It's triggered by the editableGroupCancel:

<section (cancel)="cancelUpdate()">
  <editable></editable>
  <editable></editable>
  <editable></editable>
</section>

Directives

editableGroup

import { EditableGroupDirective } from '@ngneat/edit-in-place';

Overcharges the behavior of children editable Components to work as one entity.

@OutputTypeDescription
savevoidtriggered by the editableGroupSave directive
cancelvoidtriggered by the editableGroupCancel directive
editableModeChange'view' or 'edit'triggered by the editableGroupEdit, editableGroupSave and editableGroupCancel directives when switching modes

editableGroupEdit

import { EditableGroupEditDirective } from '@ngneat/edit-in-place';

Listen to a click MouseEvent to switch to editMode.

editableGroupSave

import { EditableGroupSaveDirective } from '@ngneat/edit-in-place';

Listen to a click MouseEvent to switch to viewMode and update the value of the group.

editableGroupCancel

import { EditableGroupCancelDirective } from '@ngneat/edit-in-place';

Listen to a click MouseEvent to switch to viewMode without updating the value of the group.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> <!-- prettier-ignore-start --> <!-- markdownlint-disable --> <table> <tr> <td align="center"><a href="https://gerome-dev.netlify.com/"><img src="https://avatars0.githubusercontent.com/u/32737308?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gérôme Grignon</b></sub></a><br /><a href="https://github.com/@ngneat/edit-in-place/commits?author=geromegrignon" title="Code">💻</a> <a href="https://github.com/@ngneat/edit-in-place/commits?author=geromegrignon" title="Documentation">📖</a> <a href="#ideas-geromegrignon" title="Ideas, Planning, & Feedback">🤔</a></td> <td align="center"><a href="https://www.netbasal.com/"><img src="https://avatars1.githubusercontent.com/u/6745730?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Netanel Basal</b></sub></a><br /><a href="#blog-NetanelBasal" title="Blogposts">📝</a> <a href="https://github.com/@ngneat/edit-in-place/commits?author=NetanelBasal" title="Documentation">📖</a> <a href="#ideas-NetanelBasal" title="Ideas, Planning, & Feedback">🤔</a></td> <td align="center"><a href="https://github.com/itayod"><img src="https://avatars2.githubusercontent.com/u/6719615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Itay Oded</b></sub></a><br /><a href="https://github.com/@ngneat/edit-in-place/commits?author=itayod" title="Code">💻</a></td> <td align="center"><a href="https://medium.com/@overthesanity"><img src="https://avatars.githubusercontent.com/u/7337691?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artur Androsovych</b></sub></a><br /><a href="https://github.com/@ngneat/edit-in-place/commits?author=arturovt" title="Code">💻</a></td> <td align="center"><a href="https://github.com/baxyz"><img src="https://avatars.githubusercontent.com/u/7852177?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bérenger</b></sub></a><br /><a href="https://github.com/@ngneat/edit-in-place/commits?author=baxyz" title="Code">💻</a></td> </tr> </table> <!-- markdownlint-restore --> <!-- prettier-ignore-end --> <!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the all-contributors specification. Contributions of any kind welcome!

Logo made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon"> www.flaticon.com</a>