# Viewplex
A convenience library that allows you to further separate your views into scoped components.
## Installation
Add `viewplex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:viewplex, "~> 0.2.0"}
]
end
```
Documentation can be be found at [https://hexdocs.pm/viewplex](https://hexdocs.pm/viewplex).
## Usage
### Configuration
Configure where your components are located with:
```elixir
config :viewplex,
path: "lib/my_app_web/components"
```
### Components
Create new components by `use`-ing the `Viewplex.Component` module and creating a corresponding template with the same name.
**lib/my_app_web/components/label_component.ex:**
```elixir
defmodule LabelComponent do
use Viewplex.Component
end
```
**lib/my_app_web/components/label_component.html.eex:**
```html
Hello World!
```
You can also use inline templates:
```elixir
defmodule LabelComponent do
use Viewplex.Component
def call(_module, assigns), do: ~E"Hello World"
end
```
Then, call your component inside a template:
```elixir
<%= component LabelComponent %>
```
> Remember to `import Viewplex.Helpers` in your views.
### Naming Conventions
Components should follow the same convention as Elixir modules.
So, if you have a `MyAppWeb.Components.LabelComponent` module, the file should be located at: `my_app_web/components/label_component.ex`. We also support (and encourage) prefixing the component module name with "Component", following the existant naming convention for Phoenix's views and controllers.
### Feature folders
Intead of placing your components in the root folder, you can also group your components like this:
```
my_app_web/components/label
my_app_web/components/label/label_component.ex
my_app_web/components/label/label_component.html.eex
```
#### Filtering assigns
Components can also define fields:
```elixir
defmodule LabelComponent do
use Viewplex.Component, [:message]
end
```
```elixir
<%= component LabelComponent, message: "Hello World" %>
```
Fields defined in the component will be available as an assign in your template:
```
<label><%= @message %></label>
```
This should allow you to simply pass down a map of assigns that will be filtered before actually invoking the component:
```
<%= component LabelComponent, assigns %>
```
#### Content blocks and slots
You can also pass content to your components:
Inline:
```elixir
<%= component LabelComponent, "Hello World" %>
```
Or using do blocks:
```elixir
<%= component LabelComponent do %>
Hello World
<% end %>
```
This will be available as an assign under the `:content` key:
```html
<label><%= @content ></label>
```
Using named slots:
```elixir
<%= component LabelComponent do %>
<% slot(:message, "Hello World") %>
<% end %>
```
```html
<label><%= @slots.message ></label>
```
> Notice that we are using `<%` instead of `<%=`. This is necessary because if you output the return of the `slot/2` function, you'll actually be rendering its content inside the component's block.
> This happens because Viewplex will scan the component block for `slot/2` function calls and then extract the slot content so it is available in the template.
#### Mouting
You can also customize how components are mounted by overriding the `mount/1` function:
```elixir
defmodule LabelComponent do
use Viewplex.Component, [:message]
def mount(assigns) do
user = Users.get_user(assigns.user_id)
{:ok, Map.put(assigns, :user, user)}
end
end
```
Or disabling the component entirely by returning an `{:error, reason}` tuple:
```elixir
defmodule LabelComponent do
use Viewplex.Component, [:message]
def mount(assigns) do
case Users.get_user(assigns.user_id) do
nil -> {:error, "could not fetch user"}
user -> {:ok, Map.put(assigns, :user, user)}
end
end
end
```
---
## Todo's
- [ ] Improve documentation
- [ ] Add real use-cases as examples
- [ ] Improve function typespecs
- [x] Support defining components inside group folder (aka "feature folder")
- [x] Publish to Hex
- [ ] Scaffold and setup tasks
- [ ] Component documentation generation
- [ ] Get default component path based on app name
- [ ] Add test samples on README
- [ ] Log mount errors using Logger