How to add nested form field into current form dynamically? - phoenix-framework

I have a many to many relationship between Users and Chatgroups. Intuitively, Users can have many Chatgroups and each Chatgroup can have many users.
I've implemented this with a join table called UserChatGroup.
In my Chatgroup controller, when routed to the new function, I can hardcode the number of members in a chatgroup by specifying userChatGroups:[%UserChatGroup{},%UserChatGroup{}] etc, where the hardcoded number is the size of this array.
For example, my controller for a new chat group hardcodes 1 user:
def new(conn, _params) do
changeset = ChatGroup.changeset(%ChatGroup{userChatGroups: [%UserChatGroup{}]})
render(conn, "new.html", changeset: changeset)
end
and in my template for new, it references a form. The relevent section of the form is below:
<%= inputs_for f, :userChatGroups, fn i -> %>
<div class="form-group">
<%= label i, :user_id, "User Id", class: "control-label" %>
<%= number_input i, :user_id, class: "form-control" %>
</div>
<% end %>
However I'd like to remove this hardcoded number of members in the chat group. Instead I'd like the user to be able to add members dynamically.
I'm having trouble with this however. The rails way of doing this is to call a function that accesses the form object and updates it. Then insert input fields with javascript. On submitting, the new input fields would be fields for the new form object.
However I can't figure out how to do this with the Phoenix Framework.
My first question:
How do I define functions in my view.html.eex so that they can be called on click and not simply rendered immediately? Seems like when I try using <%= functionFromMyView %> the function is rendered on initial load of my page. I want to call this function when I click an 'add' button so I need to figure out how to call this function only on click. Is that possible?
Question 2, more specific to my use case:
How do I add %UserChatGroup{} dynamically to my form?
I'm assuming that once I can figure out how to call a function from my view on a click, then I can use that function to insert a %UserChatGroup{} to my form and then using javascript, insert an input field that connects to the newly inserted nested UserChatGroup field.
Any guidance would be greatly appreciated.

I wasn't able to add a %UserChatGroup{} to my form.
I believe this is because the web is HTML, CSS, and JS, whereas the above was Elixir.
Therefore I assume my solution must be javascript.
I examined the html on the page which told me the %UserChatGroup{} input field had a name name="chat_group[userChatGroups][0][user_id]"
chat_group is the name of the model I am creating,
userChatGroups is the array I created to handle the members of the chat group,
[0] is the index of the input array and can go as big as you want a group to be
user_id is the field in my join table.
The index is the only value you need to change.
With javascript, I inserted input fields within my form, with a name="chat_group[userChatGroups][--indexnumber--][user_id]" and when submitting, the form would naturally also submit my newly added input. (replace indexNumber)
This way can handle adding as many chat group users as I needed.

Related

Passing id/object from one page/context to another in Phoenix 1.4

I have an Events page that is a list and I want a user to click on a specific Event to create a Request to be invited. I want the Request to be tied to that Event and want Event data (name, location, etc) to show on the new request form page.
Events are one type of "object" and the Requests are another. One Event can have many Requests, but not the other way (so one-to-many). I can do a has_many/belongs_to relationship if that makes it better, or I can simply put an event_id in the Requests - I don't have a preference. This is a Phoenix 1.4 project, nothing special or custom, backed by a PostgreSQL DB.
What I can’t figure out is how to pass the Event, or the event_id, to the New Request form. Most everything in the Phoenix framework is really great, but I can’t figure out the right way to do this one simple thing.
What I have right now is more or less based on the basic guides:
project/lib/project_web/templates/tms/event/index.html.eex (Events list)
<table><tbody><tr><td><%= link event.title, to: Routes.tms_request_path(#conn, :new) %></td></tr></tbody></table>
This goes to
lib/project_web/controllers/request_controller.ex (controller)
def new(conn, _params) do
changeset = TMS.change_request(%Request{})
render(conn, “new.html”, changeset: changeset)
end
This goes to
lib/project/tms.ex (context)
def change_request(%Request{} = request) do
Request.changeset(request, %{})
end
Which goes to
lib/project/tms/request.ex (schema)
def changeset(request, attrs) do
request
|> cast(attrs, [:name, :event_id, :email, :request_count]
|> validate_required([:name, :event_id, :email, :request_count]
lib/project_web/tms/request/new.html is a form with boxes for the attributes of the request
Everything with this works fine right now in that I can click on the link in the Events list and be taken to the New Requests form page and can save the request. Obviously, though there is no ID or association established this way.
Obviously I want to do this the Elixir way and not hack something together. I've tried several different ways to pass event_id around but none of them seem to work, so I know I'm missing something but at this point, I can't figure out what that is.
The event id can be passed through params as:
Routes.request_path(#conn, :new, event_id: event.id) # not tms_request_path
And receive in lib/project_web/controllers/request_controller.ex as event_id parameter in new function:
def new(conn %{"event_id" => event_id}) do
# the even_id can be passed directly to new form
render(conn, “new.html”, event_id: event_id)
end
Then you can acquire event_id in new form as #event_id parameter

ActiveAdmin AJAX autorefreshing partial

I just have a simple index page which shows all the items of an ActiveRecord.
What I'd like to have is that the table containing the items gets automatically refreshed every X seconds (i.e. loaded from the DB and rendered).
I already redefined the index action as a partial rendering
[app/admin/item.rb]
ActiveAdmin.register Item do
index do
render :partial => "items_list"
end
end
And then I have
[app/views/admin/items/_items_list.html.erb]
(I don't mind using ERB or ARB to write the partial)
The list table is rendered correctly when I first load the page.
I'm not sure which Javascript I should include in the page to refresh the list every X seconds. More specifically, which URL should be called by the Javascript command?
Do I need to define any custom action in the controller?
Thank you for any advice.
Thomas
I finally managed it.
[app/admin/item.rb]
ActiveAdmin.register Item do
...
index do
# do nothing; table will be filled with a partial via Javascript
end
collection_action :items_list do
#items = Item.all
render :partial => "items_list"
end
end
[app/views/admin/_items_list.html.arb]
table_for items do
column "attr_1"
column "attr_2"
column "attr_3"
end
[app/assets/javascripts/items.js]
$(document).ready( function() {
setInterval(function(){
$('#index_table_items').load('items/items_list');},1000);
})
finally append to app/assets/javascripts/active_admin.js the following
//= require inbox_files
This way I get the list table updated every second. Also the CSS doesn't get distorted.

Accessing a table within a table (Watir/PageObject)

I'm running Watir-webdriver with Cheezy's PageObject for Cucumber testing. I am new to ruby, testing, pageobjects, and watir.
I am trying to access a table. Specifically, I'm trying to click an image in the 7th column.
This is my code:
tables.downcase!
tables.gsub!(/\s+/, '_')
tables_elements = self.send("#{tables}s_elements")
the_table = tables_elements[table_number]
the_table[row_number][column_number].click
With this code running, it becomes apparent that it believes the table (columns, rows, everything) is one cell. After looking at the html, it appears there is another table within this one. The table is a gwt table of some sort. What this means is that the ID I have is not for the table I want, but for a table containing the table I want. Is there anyway to get what I want? For instance:
the_table[0].element[row_number][column_number]
Or do I have to manipulate the html directly? Because, sadly, there doesn't appear to be much to manipulate, and no way that I can see to set a class (multiple of these tables on every page) through the original java code. If you can think of another way to click the image, I'd be happy to hear it
Thank you in advance
Accessing table from table:
You can get the table within a table by chaining elements. Assuming that you want the first table within the table with an id, you can do:
your_table_element.table_element
For example, say the page html is:
<table id="known_id">
<tr>
<td>
<table>
<tr>
<td>A0</td>
<td>B0</td>
</tr>
</table>
</td>
</tr>
</table>
You can create a page object with a reference to the known table:
class MyPage
include PageObject
table(:your_table, :id => 'known_id')
end
The following would then give you the first row's second cell of the child table:
puts page.your_table_element.table_element[0][1].text
#=> "B0"
Note that you can pass additional locators to the table_element method. For example, if the child table is actually the second instead of the first table, you can do:
puts page.your_table_element.table_element(:index => 1)[0][1].text
#=> "B0"
Accessing child table directly:
Alternatively, assuming you always want the child table, you can change the page object to access it instead. This is done by pass a block to the accessor method:
class MyPage
include PageObject
table(:actual_table){ table_element(:id => 'known_id').table_element }
end
Which then means you do not need to call the extra table_element method:
puts page.actual_table_element[0][1].text
#=> "B0"

Value not persisting on Submit with validation error

I'm using MVC3 .NET4.0 (VB), and I'm seeing some strange behavior on a simple View. It's a Create view that is set up as:
Inherits="System.Web.Mvc.ViewPage(Of MyProject.MyTable)
The controller is pretty straightforward. It accepts the ID of the parent record to which this record is being added:
Function Create(parent As Integer) As ActionResult
Return View(New MyTable With {.parent_id = parent})
End Function
The View also accepts a date among other things, but it boils down to this:
<% Using Html.BeginForm()%>
<%=Html.ValidationSummary(True)%>
<%=Html.DisplayFor(Function(model) model.parent_id)%>
<%=Html.TextBoxFor(Function(model) model.start_date)%>
<%=Html.ValidationMessageFor(Function(model) model.start_date, "*")%>
<button type="submit" id="submitButton">Save</button>
<% End Using%>
I'm testing the handling of date errors, so right now my post controller is just checking for errors and not doing much else:
<HttpPost()>
Function Create(model As MyTable) As ActionResult
If ModelState.IsValid Then
Return RedirectToAction("Index")
Else
Return View(model)
End If
End Function
When I first load the view, I see the parent ID displayed on the form. If I put a bad date into the start date field and hit Submit, the form comes back with the invalid value highlighted, but the parent ID = 0. If I break the code in the post, I can see that "model" doesn't have the parent ID set. This obviously causes all kinds of problems, because I've essentially lost who the parent is. What am I doing wrong?
UPDATE
Per Darin's suggestion I changed DisplayFor to HiddenFor and didn't see any difference. So then I tried TextBoxFor and got stranger results. I still don't see the parent ID in the post function, but the value persists in the text box.
What am I doing wrong?
You are not including the parent_id as hidden field in your form. Inside your form you have a single input element which corresponds to the start_date field, so that's all that you can hope to get in your POST action.
So:
<%= Html.HiddenFor(Function(model) model.parent_id) %>
The DisplayFor that you used only displays the value, it doesn't emit any input field to transport this value to the server when the form is submitted.

Grails: can remoteField update multiple fields?

Assume i have a book entity with an isbn field.
When entered a isbn number, i want 2 fields to be updated: title and author.
My controller looks like this:
def ajaxGetBook = {
def book = Book.findByIsbn(params.isbn)
if(book==null) book = new Book()
render book as JSON
}
So my call works, and i get a full JSON Book.
Now i would like to update 2 texfields by the update attribute
<g:remoteField action="ajaxGetBook" update="title" name="isbn" value="${bookInstance?.book?.isbn}" paramName="isbn"/>
Now the title field gets updated with the full book object, so that doesn't work.
Is it possible to update field title with only the JSON book.title?
Is it possible to update more fields at once?
I could render book.title as JSON but that works for only one field.
Thank you
Well, the g:remoteField tag is explicitly for one field, so you can't use it to update more than one. So, you're left with 2 easy choices:
Use 2 g:remoteField calls... this isn't a bad option, as they would happen in near parallel, as they are Async calls.
Roll your own Ajax. use g:remoteFunction instead, and have the JS function that you place in the "success" attribute take your book, and update the appropriate HTML fields.

Resources