Giter VIP home page Giter VIP logo

rummage_phoenix's Introduction

Rummage.Phoenix

Build Status Hex Version hex.pm downloads Hex docs docs MIT licensed

Rummage.Phoenix is a support framework for Phoenix that can be used to manipulate Phoenix collections and Ecto models with Search, Sort and Paginate operations.

It accomplishes the above operations by using Rummage.Ecto, to paginate Ecto queries and adds Phoenix and HTML support to views and controllers. For information on how to configure Rummage.Ecto visit this page.

The best part about rummage is that all the three operations: Search, Sort and Paginate integrate seamlessly and can be configured separately. To check out their seamless integration, please check the information below.

NOTE: Rummage is not like Ransack, and doesn't intend to be either. It doesn't add functions based on search params. If you'd like to have that for a model, you can always configure Rummage to use your Search module for that model. This is why Rummage has been made configurable.


Search, Sort and Paginate seamlessly in Phoenix!

phoenix all together


Installation

This is available in Hex, the package can be installed as:

  • Add rummage_phoenix to your list of dependencies in mix.exs:

    def deps do
      [
        {:rummage_phoenix, "~> 1.2.0"}
      ]
    end

Blogs

Current Blogs:

Coming up next:

  • Using Rummage.Phoenix 2
  • Using the Rummage Search hook
  • Using the Rummage Sort hook
  • Writing a Custom Rummage.Ecto Hook
  • Writing a Custom Rummage.Phoenix HTML helper
  • Using Rummage with other Libraries: Kerosene
  • Using Rummage with other Libraries: Scrivener

Configuration (Optional, Not the preferred way to set default_per_page)

Rumamge.Phoenix can be configured globally with a default_per_page value (which can be overriden for a model). This is NOT the preferred way to set default_per_page as it might lead to conflicts. It is recommended to do it per model as show below in the Initial Setup section. If you want to set default_per_page for all the models, add it to model function in web.ex

  • Add rummage_phoenix config to your list of configs in dev.exs:

    config :rummage_phoenix,
      Rummage.Phoenix,
      default_per_page: 5

Usage (The screenshots correspond to version 0.6.0, soon there will be screenshots for version 1.0.0)

Initial Setup

  • Use Rummage.Controller in to controller module:
defmodule MyApp.ProductController do
  use MyApp.Web, :controller
  use Rummage.Phoenix.Controller

  # More code below....
end
  • Change the index action in the controller:
def index(conn, params) do
  {query, rummage} = Product
    |> Rummage.Ecto.rummage(params["rummage"])

  products = Repo.all(query)

  render conn, "index.html",
    products: products,
    rummage: rummage
end
  • if using Search feature, define a search path in the router.ex (no need to define the action):
scope "/", MyApp do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  resources "/products", ProductController
end

Doing this itself will allow you to search, sort and paginate by updating params on the request. Please check the screenshots below for details

Using Rummage.ViewHelpers

  • Use Rummage.View in to a view module:
defmodule MyApp.ProductView do
  use MyApp.Web, :view
  use Rummage.Phoenix.View

  # More code below...
end

Note: If you get a "MyApp.Router.Helpers is not available" exception, you can provide your router with:

defmodule MyApp.ProductView do
  use MyApp.Web, :view
  use Rummage.Phoenix.View, helpers: MyApp.Web.Router.Helpers

  # More code below...
end

or through the config:

config :rummage_phoenix, Rummage.Phoenix, [
  default_helpers: MyApp.Web.Router.Helpers,
]

Note: If the path helper name is incorrect, you can specify it with:

defmodule MyApp.ProductView do
  use MyApp.Web, :view
  use Rummage.Phoenix.View, struct: "special_product" # will become special_product_path

  # More code below...
end
  • Pagination:

Add this at the bottom of index.html.eex to render Rummage pagination links (Make sure that you pass rummage to the views from the index action in the controller) :

<%= pagination_link(@conn, @rummage) %>

Reload and this is how your page should look:

phoenix pagination

  • Sorting:

Replace table headers on the index.html.eex with sort links (Make sure that the headers are actual columns in the table in the database.)

Replace this:

  <th>Name</th>
  <th>Price</th>
  <th>Category</th>

With:

  <th><%= sort_link @conn, @rummage, [field: :name, ci: true] %></th>
  <th><%= sort_link @conn, @rummage, [field: :price] %></th>

OR for Sort by associations:

  <th><%= sort_link @conn, @rummage, [field: :name, name: "Category Name", assoc: ["category"]] %></th>

Reload and this is how your page should look with sortable links instead of just table headers:

phoenix sorting

NOTE: Currently working on adding better elements to the views, soon the text arrow in the sort links will be replaced by an icon

  • Searching:

Add a search form in the index.html.eex with searchable fields:

<%= search_form(@conn, @rummage, [fields:
[
  name: %{label: "Search by Product Name", search_type: "ilike"},
  price: %{label: "Search by Price", search_type: "eq"},
], button_class: "btn",
]) %>

OR for Search by associations:

<%= search_form(@conn, @rummage, [fields:
[
  name: %{label: "Search by Category Name", search_type: "ilike", assoc: ["category"]}
], button_class: "btn",
]) %>

Reload and your page should look somewhat like this: phoenix searching

  • ALL TOGETHER:

The best part about Rummage is that all the three hooks/operations integrate seamlessly without affecting each other's functionality and therefore, you have a page looking somewhat like this:

phoenix all together

More Screenshots

Before rummage

before pagination

After Pagination:

  • Default pagination:

after pagination

  • Custom pagination params:

custom pagination params custom pagination page

After Pagination View:

  • Default after pagination

  • Custom pagination params phoenix pagination

After Sort:

custom sort params asc custom sort page asc

custom sort params desc custom sort page desc

After Sort View:

phoenix sorting

After Search:

custom search params custom search page

After Search View:

phoenix searching

rummage_phoenix's People

Contributors

danielfoxp2 avatar ericsullivan avatar iv-3an-ev avatar jekku avatar mrkaspa avatar nickurban avatar sinourain avatar thebugcatcher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rummage_phoenix's Issues

Shortening very long pagination bars

Hi, I checked the code but maybe overlooked it. I have a big amount of data I paginate, in this case it's 600 pages with 25 entries each. The pagination bar renders "Previous 1 2 3 4 5 6 7 8 9 10 11 etc etc 600 Last", which of course in case of 600 pages is sort of ugly. Is there a way to render "Previous 1 2 3 ... 598 599 600 Last" ? So basically it's about how to shorten the pagination bar.

Cheerio,
Christoph

Default pagination not biting through controller

As suggested, I have already set my config vars into setting the default_per_page as I like

config :rummage_ecto, Rummage.Ecto
  default_repo: MyApp.Repo,
  default_per_page: 3

I confirmed that this configuration has been applied by checking IEx

Rummage.Ecto.Config.default_per_page() //returns 3

However when I make a rummage query:

def index(conn, params) do
  {query, rummage} = Something.rummage(Something, params['rummage'])
  #more code here
end

The value of rummage returned by the function is:

%{"paginate" => %{"max_page" => "1", "page" => "1", "per_page" => "10",
    "total_count" => "4"}, "search" => %{}, "sort" => %{}}

Whereas I already set the default_per_page as 3.

Any idea why this happens???

Support for Phoenix 1.3 and elixir 1.5.1

Not working with current versions. Here is the error

== Compilation error in file lib/my_backend/segment/page_url.ex ==
** (UndefinedFunctionError) function Rummage.Ecto.__using__/1 is undefined or private
    (rummage_ecto) Rummage.Ecto.__using__([])
    lib/my_backend/segment/page_url.ex:3: (module)
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (stdlib) erl_eval.erl:122: :erl_eval.exprs/5
    (elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1

Any suggestions on how to make it work ?

trouble with nested resource url helpers

Hi!
Can the mix support nested resouce links like group_post_path(@conn, :index, group.id)?

In routes.ex it looks like

  resources "/groups", GroupController do
       resources "/posts", PostController
  end

Thanks!

Struct opts for namespace does not support Nested namespace objects

I read the code for the pagination macros and this is what I found out:

The namespace being followed when passing a struct opt to the view is de-atomized into a string and converted to a URL helper

  use Rummage.Phoenix.View, helpers: Backroom.Router.Helpers, struct: :order

In turn, it will construct a function in the pagination_link/2 that tries to call a router helper by appending _path:

  Backroom.Router.Helpers.order_path/4

However, what I want to call is:

  Backroom.Router.Helpers.backroom_merchant_order_path/4

Replacing the struct with something does not exist does not work as well, even if does call the function I need.

  use Rummage.Phoenix.View, helpers: Backroom.Router.Helpers, struct: :backroom_merchant_order

The error of the above code results into Phoenix.Param not accepting maps, but only structs.

Any workaround for this?

protocol Ecto.Queryable not implemented

I'm getting the following error "protocol Ecto.Queryable not implemented for Magnify.Magnets, the given module does not provide a schema. This protocol is implemented for: Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple"

I followed the setup guide.

Here is my controller

defmodule MagnifyWeb.MagnetController do
  use MagnifyWeb, :controller
  use Rummage.Phoenix.Controller

  alias Magnify.Magnets
  alias Magnify.Magnets.Magnet

  def index(conn, _params) do
    {query, rummage} = Magnets
                       |> Rummage.Ecto.rummage(_params["rummage"])

    magnets = Repo.all(query)

    render conn, "index.html",
           magnets: magnets,
           rummage: rummage

    #magnets = Magnets.list_magnets()
    #render(conn, "index.html", magnets: magnets)
  end

  def new(conn, _params) do
    changeset = Magnets.change_magnet(%Magnet{})
    render(conn, "new.html", changeset: changeset)
  end

  def create(conn, %{"magnet" => magnet_params}) do
    case Magnets.create_magnet(magnet_params) do
      {:ok, magnet} ->
        conn
        |> put_flash(:info, "Magnet created successfully.")
        |> redirect(to: magnet_path(conn, :show, magnet))
      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

  def show(conn, %{"id" => id}) do
    magnet = Magnets.get_magnet!(id)
    render(conn, "show.html", magnet: magnet)
  end

  def edit(conn, %{"id" => id}) do
    magnet = Magnets.get_magnet!(id)
    changeset = Magnets.change_magnet(magnet)
    render(conn, "edit.html", magnet: magnet, changeset: changeset)
  end

  def update(conn, %{"id" => id, "magnet" => magnet_params}) do
    magnet = Magnets.get_magnet!(id)

    case Magnets.update_magnet(magnet, magnet_params) do
      {:ok, magnet} ->
        conn
        |> put_flash(:info, "Magnet updated successfully.")
        |> redirect(to: magnet_path(conn, :show, magnet))
      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "edit.html", magnet: magnet, changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    magnet = Magnets.get_magnet!(id)
    {:ok, _magnet} = Magnets.delete_magnet(magnet)

    conn
    |> put_flash(:info, "Magnet deleted successfully.")
    |> redirect(to: magnet_path(conn, :index))
  end
end

And I don't have any issues with the following

def index(conn, _params) do
    magnets = Magnets.list_magnets()
    render(conn, "index.html", magnets: magnets)
  end

Just when I try to add rummage as per example?

pagination problem: url always is module's path for :index

Hi
Thank you for the great module.
I face a problem. The url of the pagination always return about the module's path.
Can it return the current url?
for example:
MyApp.Router.Helpers.url(conn) <> conn.request_path which would return a result like "http://localhost:4000/categories/4/products".

When I do category that will be very helpful. This url will get the products that belong to the category. The view shares with the product view. Thus, the template will render the products.

https://github.com/vinsol/nectarcommerce/blob/master/apps/nectar/web/templates/category/products.html.eex

I try to change the linker in paginate_view.ex as following.

querystr= Plug.Conn.Query.encode(%{rummage: Map.put(rummage, "paginate", %{"per_page"=> per_page, "page"=> x})})
raw_link(x, conn.request_path <> "?" <> querystr )

support action: in sort_link

Is that possible to support override :index default action with action: opts as an example below?

<%= sort_link @conn, @rummage, [field: :price, struct: :my_path, action: :my_action] %>

Currently it only support struct: option but always routed to the action :index which is not desired for my_path in my application. Any existing solution already? Please advise. Thanks.

Make work with phoenix >1.3.0

# marcos @ marcos-ThinkPad-T440 in ~/xxx/apps/tools_feed_web on git:master x [10:22:27] C:1
$ mix deps.get
Running dependency resolution...

Failed to use "phoenix" (version 1.3.0-rc.1) because
  phoenix_live_reload (version 1.0.8) requires ~> 1.0 or ~> 1.2-rc
  rummage_phoenix (version 1.0.0) requires ~> 1.2.1
  mix.lock specifies 1.3.0-rc.1

assoc with sort and search

I encountered a problem when sort and search same association
When i search user email and then sort user email, phoenix will error

flow is the error message

deps/rummage_ecto/lib/rummage_ecto/hooks/sort.ex:247: could not find association `user` on schema Aquarium.Accounts.User in query:
from p in Aquarium.Exam.Puzzle,
  join: u0 in assoc(p, :user),
  join: u1 in assoc(u0, :user),
  where: like(u0.email, ^"%ar%"),
  order_by: [asc: fragment("lower(?)", u1.email)],
  limit: ^10,
  offset: ^0,
  select: p

and flow is the params["rummage"]

%{"paginate" => %{"max_page" => "3", "page" => "1", "per_page" => "10",
    "total_count" => "25"},
  "search" => %{"email" => %{"assoc" => ["user"], "search_term" => "ar",
      "search_type" => "like"}},
  "sort" => %{"assoc" => ["user"], "field" => "email.asc.ci"}}

sort_link assumes index

If you have a show page where you want to rummage a child collection sort_link won't work. If you could pass a url to sort_link that'd work, or have sort_link always call the current url.

Select field in search form

Hi,

I would like to have a select field in the search form with some predefined options. I have been looking into the implementation of search_form but it does not seem to be possible at the moment. Unless I am missing something?

If it is possible (maybe through a workaround) I would be helpful for any pointers on how to achieve this and may add some documentation for it when I get it to work.

If it is not possible I would possibly work on implementing this. Would you be interested in merging such a PR? It would be interesting to get a hint on how you would like this to be implemented in that case.

Thanks!

Tudo.Issue.rummage/2 is undefined or private

rummage_phoenix seems to work well with version 1.0.0 but as soon as I upgrade to 1.1.0 or 1.2.0 I get an error about using use Rummage.Ecto in the models :

function Rummage.Ecto.__using__/1 is undefined or private

Changing use to import gets me passed that error but gives me:

function Tudo.Issue.rummage/2 is undefined or private

We'd like to use version 1.2 as our pagination will need to cope with a large amount of pages and therefore we would need maximum page links in 1.1.0

Code is here: https://github.com/dwyl/tudo/tree/rummage-import

any help on direction would be great!

Limit pagination links to X number of items at once

Would it be possible if not already existing to be able to limit pagination_link(@conn, @rummage) to only show X number of pages at a time with links at either end of the links to quickly jump to the start or end?

Below is an example of what happens if you have a lot of data currently.
screen shot 2017-05-02 at 12 29 52 pm

Error Unknown column - Repo.preload

I have currently inserted the following line in my index.html

<%= search_form(@conn, @rummage, [fields:
[ city: %{label: "Search City", search_type: "like"}], button_class: "btn"]) %>

But it returns an error
Error:
(1054): Unknown column 'd0.city' in 'where clause'
How can I solve this problem?
Note:
I use, |> Repo.preload(:city) to recognize "id" in "name"

Model 1>

defmodule Relat.Disp do
  use Relat.Web, :model
          use Ecto.Schema
          use Rummage.Ecto


  schema "disps" do
    field :disponib, :float
    field :date, Ecto.Date
    belongs_to :city, Relat.Local

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:disponib, :date, :city_id])
    |> validate_required([:disponib, :date, :city_id])
  end
end

Model 2 >

defmodule Relat.Local do
  use Relat.Web, :model

  schema "locais" do
    field :city, :string

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:city])
    |> validate_required([:city])
  end
end

Getting errror on pagination when model doesn't match controller

** (UndefinedFunctionError) function MyApp.Router.Helpers.adminvideo_path/3 is undefined or private. Did you mean one of:

      * admin_path/2
      * admin_path/3
      * admin_video_path/2
      * admin_video_path/3
      * admin_video_path/4

My controller is MyApp.AdminVideoController and my model is MyApp.Video

Unable to run in Phoenix 1.3

I'm trying to run this in a phoenix app 1.3.2 app, but it seems no matter what I do, I keep having an issue. Can I get a bit of direction, please? :)

Using master asks me to use rummage_ecto 1.2, which says I should "use ~>Elixir 1.3.4" as a warning. Then When I try to start the app, it tells me I need postgrex (which I cannot because I'm using MSSQL due to a requirement).

I managed to somehow get it working by using master branch of rummage_ecto, but the sort_link function fails because the rummage I get back from querying my DB is using string keys, instead of atom keys.

Am I doing something wrong?

function nil.all/1 is undefined or private

Hi, I try to use with Phoenix 1.3, Elixir 1.5.2
get (UndefinedFunctionError) function nil.all/1 is undefined or private nil.all(#Ecto.Query<from p in subquery(from p in BpPro.Accounts.Profile), select: count(p.id)>)
when call Rummage.Ecto.rummage(Profile, params["rummage"])

I did as shown in #39
Any idea?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.