I have a field called paused_at on one of my Models.
I want users to toggle a boolean checkbox called paused in the frontend, but then save the time at which they paused it.
So far I have the following:
in the model:
schema "tablename" do
# ... code before here omitted for brevity
field :paused_at, Ecto.DateTime
field :paused, :boolean, virtual: true
end
# ... code omitted for brevity
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> set_paused_at
end
def set_paused_at(changeset = %Ecto.Changeset{changes: %{paused: nil}}) do
changeset
|> put_change(:paused_at, Ecto.DateTime.from_erl(:erlang.universaltime))
end
def set_paused_at(changeset = %Ecto.Changeset{}), do: changeset
Storing the correct value now works. But where should I add code that adds the proper boolean value to paused whenever the model is queried?
Can you show your template/html also please? I if understood correctly you have field paused :: boolean and paused_at :: Ecto.Datetime and when the user clicks the checkbox paused == true and paused_at == current_time, right?
This will depend how you’re doing things exactly but from your code my initial suggestion would be updating the paused value in side the set_paused_at. If you’re already inserting the current time value then it means it was also stopped and therefore it makes sense to also update it in the same place.
Otherwise in your template you may have an hidden_input that send the correct boolean value to be updated in the controller. But again, this largely depends on how your code is behaving and organized.
The main problem I now have, is how to show again a boolean in the User Interface.
Right now I have a function called Subscription.paused?/1 that checks the paused_at, but what I’d rather want to do, is to set the boolean correctly when retrieving the struct from the database.
@Qqwy I’m having a hard time understanding your problem You mean you want to render the paused value in your eex/html file? You usually do that with something like this: <%= subscription.paused %>. If you want to use other values instead of true and false you can do <%= if subscription.paused do "Paused" else "Unpaused" end.
I want to set the virtual field before returning the %Subscription{} struct to wherever it is used (in the html view, in the json api). This is logic that belongs in the model, but as functions like Repo. all(Subscription) are called in the controller, I am not sure how to do that.
In an OOP-language I would simply add a method to my model class, because data and code is intertwined there. In functional land, however, this is not possible.
Instead of using the Repo in the model (which indeed is easy), I want to use the virtual field inside the controllers/views.
Let me describe my thought process from a slightly higher level.
As most example code only talks to the Repo in the controller, I see the following options:
Use the Repo in the model, only use models made methods(that internally define the correct value for the virtual field) in the controllers. (ˋSubscription.allˋ instead of ˋRepo.all(Subsciption)ˋ. This would take a lot of work, and I am not sure if this extra abstraction layer is good, or moves the DB related stuff to a place it doesn’t belong.
Do not use virtual fields this way. Instead of reading values from a struct, have methods in your model that return values on the Struct (ˋSubscription.name(subscription)ˋ instead of ˋsubscription.nameˋ ). This means that it would be easier to in the future maybe wrap some of the other fields as well, but it would be a lot more verbose.
Override the functions in the Repo with new variants that in the case of a single (or a few) models do extra behaviour on top of what Ecto.Repo provides. Seems like a hackish idea, and also a lot of work. The advantage would of course be that you are able to use exactly the same code to look up a model as before. But this might get the easy vs simplicity thing wrong. (It is very much the Activerecord style solution)