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.
Related
We've just moved over from bootstrap to Vuetify, but i'm struggling with something.
We have some updates sent (over signalR) that update a list of jobs, i'd like to be able to target a job that has been changed and change the row color for that particular job for a few seconds so the operator can see its changed.
Has anyone any pointers on how we can do this on a Vuetify v-data-table
Thanks
I ran into the same problem. This solution is a bit crude and a bit too late, but may help someone else.
In this example I change the colour of the row permanently until the page reloads. The problem with a temporary highlight is that if the table is sorted there is no way to put the row in the visible part of the table - v-data-table will put it where it belongs in the sort, even if it's out of the view.
Collect the list of IDs on initial load.
Store the list inside data of the component.
Use a dynamic :class attribute to highlight rows if the ID is not in the list (added or edited rows)
Solution in detail
1. Use TR in the items template to add a conditional class.
<template slot="items" slot-scope="props">
<tr :class="newRecordClass(props.item.email, 'success')">
<td class="text-xs-center" >{{ props.item.email }}</td>
:class="newRecordClass(props.item.email, 'success')" will call custom method newRecordClass with the email as an ID of the row.
2. Add an additional array to store IDs in your data to store
data: {
hydrated: false,
originalEmails: [], <--- ID = email in my case
3. Populate the list of IDs on initial data load
update(data) {
data.hydrated = true; // data loaded flag
let dataCombined = Object.assign(this.data, data); // copy response data into the instance
if (dataCombined.originalEmails.length == 0 ) {
// collect all emails on the first load
dataCombined.originalEmails = dataCombined.listDeviceUsers.items.map( item => item.email)
}
return dataCombined;
}
Now the instance data.originalEmails has the list of IDs loaded initially. Any new additions won't be there.
4. Add a method to check if the ID is in the list
newRecordClass(email, cssClass) {
// Returns a class name for rows that were added after the initial load of the table
if (email == "" || this.data.originalEmails.length==0) return "" // initial loading of the table - no data yet
if (this.data.originalEmails.indexOf(email) < 0 ) return cssClass
}
:class="newRecordClass(..." binds class attribute on TR to newRecordClass method and is being called every time the table is updated. A better way of doing the check would be via a computed property (https://v2.vuejs.org/v2/guide/computed.html#Computed-Properties). Vue would only call it when the underlying data changed - a method is called every time regardless.
Removing the highlight
You can modify newRecordClass method to update the list of IDs with new IDs after a delay to change the colour to normal.
#bakersoft - Did you find a solution? I suspect there is an easier way to skin this cat.
I have a couple of text fields that are filled from the database when the value of a select list changed.
I added another action to the list change dynamic action to execute PL/SQL code:
IF :P2_SELECT_LIST1 LIKE '%ABC%' AND :P2_NAME = 'WWW' THEN
:P2_NAME = NULL;
END IF;
Nothing happend on the page when I change the value of the select list, but the session value of P2_NAME gets cleared.
I also tried:
IF :P2_SELECT_LIST1 LIKE '%ABC%' AND :P2_NAME = 'WWW' THEN
:P2_NAME = '';
END IF;
But gotten the same result
In this dynamic action there are two fields next to your pl/sql code:
Items to submit: list the items here that you use in your pl/sql to get the values from session (if necessary, the values are usually already in session).
Items to return: list the items here that you need to refresh the value on the HTML page after some change in your pl/sql
I think this solves the problem.
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.
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"
We are trying to optimize our views and on a page where 40 pictures are loaded with the following code :
= image_tag(product.pictures.first.data.url(:gallery))
We have a load time of 840ms, if we change it to the following code :
= image_tag("http://bucketname.s3.amazonaws.com/products/#{product.pictures.first.id}/gallery.jpg?1325844462"
We become a load time of 220ms.
It means the interpolation of s3_path_url is very slow. Somebody else is expecting the same problems? For the moment I created a helper that generates my urls :
def picture_url(picture, style)
"http://bucketname.s3.amazonaws.com/products/#{picture.id}/#{style}.jpg"
end
Only problem I have here is that the cache key is not there and also the extension not.
Is there always just one image of each product shown on the gallery page?
What about a cache column in your database. Whenever you create or update an image you could save this image_url as gallery_picture_url in your database and call it directly like
= image_tag(product.gallery_picture_url)
class Product < ActiveRecord::Base
after_commit: :update_gallery_picture_url
def update_gallery_picture_url
self.update(gallery_picture_url: self.pictures.first.data.url(:gallery)) if self.gallery_picture_present?
end
def gallery_picture_present?
(self.pictures.first.data.url(:gallery) rescue false).present?
end
end