README.md

# PhxInPlace

**Inline editing package for the Phoenix Framework based on the rails gem best_in_place.**

Based on the Rails gem [best_in_place](https://github.com/bernat/best_in_place), phx_in_place enables unobtrusive, inline editing via Phoenix channels. The package consists of a view helper, javascript event listeners and a server side channel helper method that when set up will automatically update your application database and views whenever a user changes a field value.

Basic Example:
```javascript
<%= phx_in_place @product, :category %>
//<input class="pip-input" hash="<<hashed value here>>" name="category" value="251.00" style="background: initial;">
```

Optional parameters provide support for styling and formatting:
```javascript
<%= phx_in_place @product, :name, class="input-lg", display_as: :number_to_currency, display_options: [precision: 2, unit: "$"], type: "textarea" %>
//<textarea class="pip-input input_lg" display_type="number_to_currency" hash="<<hashed value here>>" name="name" value="$ 251.00" style="background: initial;">
```

<!-- Full documentation for the package is available [here](link to come when ready). -->

<!-- A demo of the solution in action is available [here](add url where ready) -->


## Installation

Adding phx_in_place to an existing application with one or more channels already set up is easy and straight forward. If you are unsure how to set up a Phoenix Channel, please see the [Phoenix Channels guide](https://hexdocs.pm/phoenix/channels.html#content) for more information.

Include [phx_in_place](https://github.com/cjwadair/phx_in_place) as a dependency in the mix.exs file:

```elixir
def deps do
  [
    {:phx_in_place, "0.1.2"}
  ]
end
```

and run `mix deps.get` to install dependencies:

```
  mix deps.get
```

then add the following to your package.json file:

```
"dependencies": {
    ...
    "phx_in_place": "file:../deps/phx_in_place"
  },
```

and run `npm install` from the same directory that your package.json file is located in (normally the /assets folder):

```
npm install
```

### Server-side set up

`import PhxInPlace` into your appName_web.ex file to make it available in all of your templates:

```elixir
defmodule YourAppNameWeb do
  ...
  def view do
    quote do
      #other import, use and alias statements
      import PhxInPlace
    end
  end
  ...
end
```

and `use PhxInPlace.ChannelManager` in your channels:

```elixir
  # in my_channel.ex
  defmodule AppName.MyChannel do
    use Phoenix.Channel
    use PhxInPlace.ChannelManager
  end
```

then add the following to your config.exs file:

```elixir
  config :phx_in_place,
    repo: AppName.RepoName,
    endpoint: AppNameWeb.Endpoint
```

### Client-side set up

`import phx_in_place` into your app.js after your import for your socket.js file:

```javascript
  import "phoenix_html"
  import socket from './socket'
  import * as pip from "phx_in_place"
```

and add the phx_in_place event listeners to your pages:

```javascript
  channel.join()
    .receive("ok", message => {
      pip.addListeners(channel);
    })
    .receive("error", resp => {
      console.warn("Unable to join", resp)
    });
```

## Usage

### phx_in_place (phx_in_place struct(), :field, options)

View helper for generating input fields for inline editing:

```elixir
 <%= phx_in_place @product, :name %>
 #<input class="pip-input" hash="SFMyNTY.g3QAAAACZAAEZGF0YWwAAAABaAJkACpFbGl4aXIuU2l0ZWxpbmVQaG9lbml4LlN1cHBsaWVycy5CY2xpc3RpbmdiAAAHTmpkAAZzaWduZWRuBgAvG0InYwE.aJPlnBRX1nuKx8Bdyo8P_UTpRYIyO24aQaYknQJ2Q50" name="name" value="251.00" style="background: initial;">
```

Hash value contains the name of the struct and the id of the record in question. Values are signed using Phoenix.Token to prevent tampering but are not encrypted. See the [Phoenix.Token](https://hexdocs.pm/phoenix/Phoenix.Token.html#content) module for details.

**Params:**

 - **struct (required)**: The Ecto query struct containing the data to be displayed and managed
 - **field (required)** : The name of the field to be displayed passed as an atom

**Options**

- **type (string)**: The name of the input type you want to create. Default to type="input".
- **class (string)**: A string containing the names of any additional classes to be added to the element being created.
- **display_as (atom)**: The name of the formatting helper to apply to the output value passed as an atom. Supported options include **:number_to_currency, :number_to_percentage, and :number_to_delimited**. See the [Number]() Hex Package for more details.
- **display_options (list)**: option values for the display_as field. See the Number Hex package for more details. Basic defaults have been set in the phx_in_place config.exs file and can be overridden in your apps config.exs file if required.


### phx_in_place_if (phx_in_place_if condition, struct, field, OPTIONS)

Similar to the phx_in_place helper but with a condition as the first parameter.

```elixir
  <%= phx_in_place_if @user.type=='admin', @product, :supplier_id %>
```

if `@user.type=='admin'` is false, a non-editable `<span></span>` tag is generated. Otherwise, the output is the same as the phx_in_place method above. This is useful for enforcing authorization rules.

### Post Update Callbacks

The event handlers that phx_in_place adds to your code will handle database updates and change the value of the input field automatically. For additional post-update event handling, you can listen to the `pip:update:success` and `pip:update:error` events as follows:

```javascript
  document.addEventListener('pip:update:success', function (e) {
      //add your success event callbacks here
  }, false);

  document.addEventListener('pip:update:error', function (e) {
      //add your failure event callbacks here
  }, false);
```

These callbacks can be used to trigger additional client side javascript updates or channel events for server side processing.

## Examples

### Using the :display_as option

The display_as option works with the formatting options provided through the Numbers Hex package. `:number_to_currency`, `:number_to_percentage`, and `:number_to_delimited` are currently supported.

```
  <%= phx_in_place @product, :name, display_as: :number_to_currency %>
```

Application wide defaults for Number formatting can be set in your config.exs file. See [Number documentation](https://hexdocs.pm/number/Number.html) for details on configuration options.

To override the defaults on a case by case basis, display_options: can be set directly within the phx_in_place tag:  

```
  <%= phx_in_place @product, :name, display_as: :number_to_currency, display_options: [precision: 2, unit: "$"] %>
```

### Adding custom classes

Using the class option will add a list of additional class names to the input tags generated by phx_in_place. This is useful for adding custom styles to your input tags or for working with 3rd party libraries and frameworks such as Bootstrap:

```
  <%= phx_in_place @product, :name, class="btn btn-lg" %>
```

### Setting tag type

Currently, phx_in_place supports input and textarea as field type options with input being the default:

**input tag examples**

```
  <%= phx_in_place @product, :name, class="btn btn-lg" %>
  <%= phx_in_place @product, :name, class="btn btn-lg", type: "input" %>
```

**textarea tag example**

```
  <%= phx_in_place @product, :name, class="btn btn-lg", type: "textarea" %>
```