I've started to teach myself Phoenix Framework and their documentation is pretty good. I have however hit a stumbling block when it comes to specifying optional routing parameters. The Phoenix Framework Routing Documentation makes no mention of this as a feature, so I'm assuming the onus falls on the dev to come up with a solution.
I'll lay out my use case:
/page/test
, a custom Plug then implements some code to find or assign a locale
to the connection.:locale
parameter in the path, the default is used as per the line in my pipeline, which is plug HelloPhoenix.Plugs.Locale, "en"
./fr/page/test
, and the same code gets executed in the pipeline, except time as the :locale
parameter is present in the route, the custom Plug (HelloPhoenix.Plugs.Locale
).Now from a routing perspective, if I can't specify that the :locale
parameter is optional, I end up with double the number of routes, e.g.:
scope "/", HelloPhoenix do
use_pipeline :browser
plug HelloPhoenix.Plugs.Locale, "en"
# Route without locale
get "/page/:slug", PageController, :show
# Route with locale
get "/:locale/page/:slug", PageController, :show
end
as you can tell, this could quickly become very arduous and repetitive without the ability to specify an optional routing parameter.
No I do have a workaround, which I'll post in an answer, but I'm not sure if it's (a) right, and (b) the easiest solution, since I'm new to Erlang, Elixir and Phoenix (I'm coming from a background in Ruby & PHP OOP).
Routers are the main hubs of Phoenix applications. They match HTTP requests to controller actions, wire up real-time channel handlers, and define a series of pipeline transformations scoped to a set of routes.
Optional route parameters aren’t supported explicitly by Blazor, but the equivalent can be easily achieved by adding more than one @page declaration on a component. For example, alter the standard Counter.razor page to add an additional URL.
We have seen complete process of Routing with Parameters in SAPUI5 Application, by using routing with parameters we can pass data from one view to another view, it is Hash based navigation which is used at the runtime to change the URL of the particular screen. Kindly leave any doubts or questions in the comments below.
There are 5 types of Parameter routing 1.String (Hardcoded) parameter 2.Mandatory parameter 3.Optional parameter
You could have a simple plug like:
defmodule MyApp.SetLocale do
@locales ~w{en fr}
def init(opts), do: opts
def call(conn, _opts) do
case conn.path_info do
[locale | rest] when locale in @locales ->
%{conn | path_info: rest}
|> Plug.Conn.assign(:locale, locale)
_ -> Plug.Conn.assign(conn, :locale, "en")
end
end
end
Then place this plug before your router in endpoint.ex
plug MyApp.SetLocale
plug MyApp.Router
end
This way you can be confident the locale has been set before you even get to the router. You don't need to mention it in your router at all.
This technique will 404 if you enter a locale that is not in the @locales
though.
As mentioned in my question, I've come up with a solution which works in my case, but I'm not sure it's right or the easiest solution (especially if routing gets more complex)...
My solution uses Enum.each
to loop over a list of prefixes, and then the routes only need to be specified once. This seems to work:
scope "/", HelloPhoenix do
use_pipeline :browser
plug HelloPhoenix.Plugs.Locale, "en"
# Loop over list containing prefix :locale and no prefix.
Enum.each ["/:locale", "/"], fn prefix ->
# No need to duplicate routes
get prefix <> "/page/:slug", PageController, :show
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With