Loop through all fields of a Grails Objects of unknown properties - loops

I currently have an ask to provide a "Lookup Table Management" interface to super users of an app I'm working on. They want to make changes to lookup tables that are used throughout the app (they won't be able to change IDs, of course).
I'd like to come up with a way to generically handle this without having to create a form for each LUT. Or if there is a plugin I don't know about that would be even better
All the LUTS are vastly different from each other in terms of fields. I'm trying to write a loop to loop through all the fields of a LUT object and then spit out the values for each object of that LUT.
Example:
DrinksLut
String name
Double price
Integer numSold
PetsLut
String name
Integer age
String breed
String size
Boolean fixed
I'm trying to provide a table output that lists the field properties as headers as well as the individual object values for rows depending on the LUT they choose to edit (Pets, Drinks, etc.)
Current Code
def resultList() {
def selectedLut = grailsApplication.getClassForName(params.lutFilter?.replace('class ', ''))
/* get field names, types and a "user friendly" name to use in the table header */
def objProps = new DefaultGrailsDomainClass(selectedLut)?.persistentProperties.collect {
[
name: it.name,
type: it.type,
friendlyName: Helper.splitCamelCase(it.name)
]
}
/* get all rows for the chosen LUT */
def results = selectedLut.getAll()
render(template: '../lookupTable/resultList', model:[results: results, objProps: objProps])
}
...
<table id="lutTable" class="col-xs-12">
<thead>
<tr>
<g:each in="${objProps}">
<th>${it.friendlyName}</th>
</g:each>
</tr>
</thead>
<tbody>
<g:each in="${results}">
<tr>
<td>${it}</td> <!-- this currently is the object of a LUT and I need to get each of its field properties and values to spit out here -->
</tr>
</g:each>
</tbody>
</table>
I'd greatly appreciate any assistance or ideas! The app s using Grails 2.5.5

Related

visualforce emailtemplate issue referencing a list in an Apex class

I suffer an error trying to implement a visualforce emailtemplate. The dev console gives me the following problem on the VF component: Unknown property 'String.Name' (line 0). I can't figure out how to resolve this. Consequently the email template doesnt work & gives me the error: <c:ProductOrderItemList can only contain components with an access level of global.
I aim to send an email from a Product Order (parent). In the email I want to include child records (product order items).
Apex class:
public class ProductOrderTemplate
{
public Id ProductorderID{get;set;}
public List<Product_Order_Item__c> getProductorderItems()
{
List<Product_Order_Item__c> toi;
toi = [SELECT Product_Type__r.Name, Quantity__c FROM Product_Order_Item__c WHERE Product_Order__c =: ProductorderID];
return toi;
}
}
The visualforce component:
<apex:component controller="ProductOrderTemplate" access="global">
<apex:attribute name="ProductOrdId" type="Id" description="Id of the Product order" assignTo="{!ProductorderID}"/>
<table border = "2" cellspacing = "5">
<tr>
<td>Name</td>
<td>Quantity</td>
</tr>
<apex:repeat value="{!ProductorderID}" var="toi">
<tr>
<td>{!toi.Name}</td>
<td>{!toi.Quantity}</td>
</tr>
</apex:repeat>
</table>
</apex:component>
And the email:
<messaging:emailTemplate subject=“Product Order” recipientType="User" relatedToType=“Productorder”>
<messaging:htmlEmailBody >
Hi,<br/>
Below is the list of ... for your Product order {!relatedTo.Name}.<br/><br/>
<c:ProductOrderItemList ProductOrderId="{!relatedTo.Id}" /><br/><br/>
<b>Regards,</b><br/>
{!recipient.FirstName}
</messaging:htmlEmailBody>
</messaging:emailTemplate>
Any help much appreciated! Quite stuck on this. I'm new to visualforce and apex.
​​​​​​​
You might need just a VF email template without component and apex class. Go to Product_Order_Item__c object, find Product_Order__c field definition. Write down the "Child Relationship Name".
Let's say it'll be "Line_Items". You need to add "__r" to the end (why?) and then you can do something like that:
<messaging:emailTemplate subject="Product Order" recipientType="User" relatedToType="Productorder">
<messaging:htmlEmailBody >
<p>here are your line items</p>
<table>
<apex:repeat value="{!relatedTo.Line_Items__r}" var="item">
<tr>
<td>{!item.Name}</td>
<td>{!item.Quantity}</td>
</tr>
</apex:repeat>
</table>
</messaging:htmlEmailBody>
</messaging:emailTemplate>
If you still think you want Apex and component (maybe you want to filter some records out, maybe you want to have them sorted or have a reusable piece of markup to use in multiple emails)...
... the error you're getting is because ProductorderID is a single String (well, Id) variable. But you've used it in a repeat that expects a list/array. A reference to <apex:repeat value="{!ProductorderItems}" var="toi"> should call your getProductorderItems() and work better.

How to display xml2json data

I am using xml2json to translate a xml file from medline. I am stuck on using ng-repeat to display the information I need. I need to display the title,url and see-reference. When i have one health-topic in the xml file The title and url display fine. When I add more health-topics it does not work. any help is appreciated, thanks
plunkr
<table>
<tr ng-repeat="topic in topics">
<td>{{topic._title}}</td>
<td>{{topic._url}}</td>
<td>{{topic.see-reference}}</td>
</tr>
</table>
<pre>{{topics | json}}</pre>
The structure of your XML is like this:
<health-topics total="1909" date-generated="01/03/2015 02:30:35">
<health-topic title="Abdominal Pain">
</health-topic>
<health-topic title="Abdominal Pain">
</health-topic>
<health-topic title="Abdominal Pain">
</health-topic>
xml2json creates an object for every element. The properties of the object are the element's attributes (starting with an underscore) and the child elements. In your case:
//health-topics
{
_total: "1909",
_date-generated: "01/03/2015 02:30:35",
health-topic: [
{
_title: Abdominal Pain"
...
health-topic is an array containing the topics. If you have a single topic wrapped with health-topics it would be:
//health-topics
{
_total: "1909",
_date-generated: "01/03/2015 02:30:35",
health-topic: {
_title: Abdominal Pain"
...
health-topic is now an object. But maybe a single topic wouldn't even be wrapped with a health-topics element. That's something only you know.
All in all the data structure is different depending on the number of topics. You would need to check and adapt to that. The code working for multiple topics would be
$scope.topics = $scope.dom['health-topics']['health-topic']

How do I do reference data in AngularJS?

Is it possible in AngularJS, while in a nested ng-repeat, to access data from a third table?
Here's an example... let's say I want to create a table which lists actors and actresses in the first row, and then the first column would list movies. In the cells where actor and movie intersect, we might display a rating for how well that actor performed in that film. However, since only a few actors may appear in each film, more often than not those intersecting cells would be blank. If we have roughly 120 actors and 600 films, then we're talking about 72,000 possible intersections, although our dataset may only actually have about 5000 entries.
I'm using three JSON files to do this; actors.json, movies.json, and ratings.json. The first two files contain lists of the actors or movies, and extended data for each; ie: year, genre, director, or age, nationality, etc. The last file; ratings.json, contains simply the ID of the movie, the name of the actor (our data doesn't contain IDs for the actors), and the rating.
Setting up my table, in the first row I ng-repeat through the actors file to display my header row, with the names and information about each actor. I skip the first cell, as it's the top of my header column.
Next, I ng-repeat through my movies data, opening a new row for each, and then within each row, I ng-repeat through my actors data again to create the ratings td's themselves.
All of this is working, but this is where I go off the rails. I cannot figure out an efficient way to grab the ratings data easily. I did figure out HOW to make it work, by basically looping through the entire ratings table, and filtering for a match, but I know that can't be the right way to do it.
Here's some sample code:
<table>
<tr>
<td></td>
<td ng-repeat="movie in movies>
<p>{{movie.name}}</p>
<p>{{movie.year}}</p>
</td>
</tr>
<tr ng-repeat="actor in actors>
<td>{{actor.name}}</td>
<td ng-repeat="movie in movies">
<span ng-repeat="rating in ratings | filter: { id: movie.id } : true | filter: { actor: actor.name } : true">
{{rating.rating}}
</span>
</td>
</tr>
</table>
As you can imagine, this method is really (REALLY) slow.
If this were php or something, I'd just grab the data from an array (ie: $rating[$movie_id][$actor_name]), simple as pie, but for the life of me I can't figure out how to do this with AngularJS, not at all. I just want to reference the proper bit of ratings data directly.
As requested, here's a sample of ratings.json.
[
{
actor: "Brad Pitt",
id: "13",
rating: "3"
},
{
actor: "Jennifer Anniston",
id: "8",
rating: "7"
},
...
]
Thanks,
m.
You would need to pre-process your ratings.json array into a nested hash.
var ratings = {};
for (var i=0; i < ratingsArray.length; i++){
var ratingEntry = ratingsArray[i];
var id = ratingEntry.Id, actor = ratingEntry.actor;
rating[id] = rating[id] || {};
rating[id][actor] = ratingEntry.rating;
}
Then you could just do exactly what you would expect had it been PHP:
<tr ng-repeat="actor in actors>
<td>{{actor.name}}</td>
<td ng-repeat="movie_id in movies">
{{rating[movie_id][actor]}}
</td>
</tr>

Pagination using PageObject Watir Webdriver [duplicate]

Using page-object and watir-webdriver how can I click a link in a table, based on the row text as below:
The table contains 3 rows which have names in the first column, and a corresponding Details link in columns to the right:
DASHBOARD .... Details
EXAMPLE .... Details
and so on.
<div class="basicGridHeader">
<table class="basicGridTable">ALL THE DETAILS:
....soon
</table>
</div>
<div class="basicGridWrapper">
<table class="basicGridTable">
<tbody id="bicFac9" class="ide043">
<tr id="id056">
<td class="bicRowFac10">
<span>
<label class="bicDeco5">
<b>DASHBOARD:</b> ---> Based on this text
</label>
</span>
</td>
<td class="bicRowFac11">
....some element
</td>
<td class="bicRowFac12">
<span>
<a class="bicFacDet5">Details</a> ---> I should able click this link
</span>
</td>
</tr>
</tbody>
</table>
</div>
You could locate a cell that contains the specified text, go to the parent row and then find the details link in that row.
Assuming that there might be other detail links you would want to click, I would define a view_details method that accepts the text of the row you want to locate:
class MyPage
include PageObject
table(:grid){ div_element(:class => 'basicGridWrapper')
.table_element(:class => 'basicGridTable') }
def view_details(label)
grid_element.cell_element(:text => /#{label}/)
.parent
.link_element(:text => 'Details')
.click
end
end
You can then click the link with:
page.view_details('DASHBOARD')
Table elements include the Enumerable module, and I find it very useful in cases like these. http://ruby-doc.org/core-2.0.0/Enumerable.html. You could use the find method to locate and return the row that matches the criteria you are looking for. For example:
class MyPage
include PageObject
table(:grid_table, :class => 'basicGridTable')
def click_link_by_row_text(text_value)
matched_row = locate_row_by_text(text_value)
matched_row.link_element.click
#if you want to make sure you click on the link under the 3rd column you can also do this...
#matched_row[2].link_element.click
end
def locate_row_by_text(text_value)
#find the row that matches the text you are looking for
matched_row = grid_table_element.find { |row| row.text.include? text_value }
fail "Could not locate the row with value #{text_value}" if matched_row.nil?
matched_row
end
end
Here, locate_row_by_text will look for the row that includes the text you are looking for, and will throw an exception if it doesnt find it. Then, once you find the row, you can drill down to the link, and click on it as shown in the click_link_by_row_text method.
Just for posterity, I would like to give an updated answer. It is now possible to traverse through a table using table_element[row_index][column_index].
A little bit more verbose:
row_index could also be the text in a row to be matched - in your case - table_element['DASHBOARD']
Then find the corresponding cell/td element using either the index(zero based) or the header of that column
table_element['DASHBOARD'][2] - Selecting the third element in the
selected row.
Since you do not have a header row (<th> element) you can filter the cell element using the link's class attribute. Something like this
table_element['DASHBOARD'].link_element(:class => 'bicRowFac10').click
So the code would look something like this:
class MyPage
include PageObject
def click_link_by_row_text(text_value)
table_element[text_value].link_element(:class => 'bicRowFac10').click
end
end
Let me know if you need more explanation. Happy to help :)

CakePHP, number of views

Suppose, this is a db table in my project :
<table>
<tr>
<th>id</th>
<th>title</th>
<th>description</th>
<th>created</th>
<th>modified</th>
<th>........</th>
</tr>
<tr>
<td>1</td>
<td>Some Title</td>
<td>Some Description</td>
<td>7/8/2013 8:50</td>
<td>7/8/2013 8:50</td>
<td>........</td>
</tr>
</table>
Now, I want to get the number how many times record 1 (id=1) has been seen. And that is in CakePHP. Is there any easy way to get this information ? I know created and modified are 2 fields whose values are automatically generated by CakePHP. So, is there any field or something that can be used here ? I marked this as ......... . Or, is there any other way to do this ? Thanks.
There's nothing built-in, but it's not hard to make yourself. (The hard part is to not increment the counter when search engine bots access the page if it's reachable to them.)
In the database create a field viewcount INT DEFAULT 0 for example. In the controller, assuming the model is called Model and action is view (replace with actual names), add the code that increments the counter when the page is viewed:
function view( $id ) {
// ....
$this->Model->updateAll(
array( 'Model.viewcount', 'Model.viewcount + 1' ),
array( 'Model.id', $id )
);
}
CakePHP doesn't have an "automatic" viewed field. If you'd like this functionality you have to implement it yourself with the updateAll method:
$this->Model->updateAll(
array('Model.views', 'Model.views + 1'),
array('Model.id', $id)
);

Resources