I have written a dynamic search page to search custom object records. I use a SOQL query and bind the results to a data table. I need to change the output text on one of the columns, based on the value returned.
Example: If the SOQL returned "Tiger", I need to display "Animal", Bird for eagle , etc....
So I guess my question is whether be can I use a Javascript function in the value attribute of Apex:column? Something like:
<apex:column value="renameObjectType({!mt.objectName__c})">
And the renameObjectType function is something like:
function renameObjectType(val)
{
var inputtextvalue=val.value;
if(inputtextvalue.length>0)
{
if(inputtextvalue=="Tiger")
return "Animal";
}
};
This is not working as I want to it to be...Is this even possible?
Thanks,
Calvin
This calls for the handy wrapper class. JavaScript can get tricky in Visualforce in terms of execution order, since there's so much happening behind the scenes anyway. If you haven't already, View Source on a standard contact detail page to see how much browser-side processing is going on.
The safest, fastest and most predictable path for things like these is Apex, imo.
One of the things to realize is that, every time you merge in a value from your controller (using the {!myApexVariable} syntax), Visualforce is actually calling the getter for that variable.
This means that, whenever you merge a variable in that way, you are already calling a function. In fact, you can "trick" Visualforce into simply displaying the value of a function using the following:
*My_VF_Page.page*
<apex:outputText value="{!MyFormattedValue}" />
*My_VF_Controller.apex*
public String getMyFormattedValue() {
if (someOtherValue== null || someOtherValue== '') {
return "N/A";
}
return someOtherValue;
}
What you will notice when loading the Visualforce page is that the getMyFormattedValue() is run every time the page needs to get information about MyFormattedValue. I have found this trick incredibly useful for outputting the values of functions.
Related
I'm not able to enter text into a specific input box as determined by a data attribute. There could be dozens of inputs with the same class, so I'd prefer not to add dusk='xxxx' all over the page.
For route and server-side efficiency, the AJAX function pulls a type from the array of inputs and routes to a function that branches the action.
Blade code:
{!! Form::text('question[]', null,
['class'=>'form-control actionChange', "data-id"=>$question->id, "data-type"=>'question']) !!}
The page starts with other questions with a different type, so I have tried using nth-child(x) to grab the selector within the modal, but no success. I've tried using $browser->script() as well.
Reading several similar questions, such as this one, it appears that a loop within the modal is likely the best way to go. This method correctly assigns the selector to the loop variable, $input. It correctly clear()s data, and I've tested similar code with click() and it successfully works. However, it will not enter data in the input. type() and keys() don't appear to work with the RemoteWebElement, so I believe my only choice to enter data is sendKeys().
Dusk test code:
$browser->assertPathIs('/notice')
->whenAvailable('.modal', function($modal) use($browser) {
$modal->assertSee('Survey for:')
->waitFor('#heading')
// WORKS fine
->keys('#heading', 'Edited Heading for Survey', '{enter}')
->waitFor('.actionSurvey');
// Edit a question -- NOT WORKING
foreach ($browser->elements('.actionChange') as $input) {
$dataType = $input->getAttribute('data-type');
if($dataType === 'question') {
$input->clear() // WORKS Fine
->sendKeys('Edited_Question') // NOT successful
break;
I've tried with and without the clear() method, as well as various selector choices both within and out of the modal loop. Same for script() Also, tried using the $modal variable to get the elements, but this was just a guess as I'm a bit out of my depth of understanding at this point.
I probably fubar'd something basic, but I don't understand why one method works, and another doesn't on the same handle.
I'm looking for a working example of how to sort my pages in the sitetree by title by default. Ideally I only want to sort child pages of a certain type. In my case I want all my Gallery pages under the parent Portfolio to be sorted Alphabetically by their title.
This is for easy searching in the backend CMS as I know how to sort them in the template.
I have found these examples but not enough to work this out for SS3.1+
http://www.ssbits.com/tutorials/2011/custom-sorting-in-the-cms-sitetree/
https://github.com/silverstripe/silverstripe-cms/issues/848
Having a look at the examples you gave and the current Silverstripe source, there is a few ways you could go about this. My solution involves using Silverstripe's extension system to manipulate how the hierarchy is generated.
How the SiteTree is loaded
The way the CMS loads the site tree is a little lengthy so I will quickly simplify:
The template CMSPagesController_Content.ss (used for the pages section) has markup to lazy-load the linked tree view
The linked tree view (a function specified in CMSMain) calls a few internal methods to basically load the CMSMain_TreeView template
This template calls the SiteTreeAsUL function back in CMSMain
Note: SiteTreeAsUL allows us to hook in before returning using the extension system in Silverstripe though we don't want to manipulate
the HTML directly.
getSiteTreeFor, a function part of LeftAndMain, is called inside the SiteTreeAsUL.
getSiteTreeFor calls getChildrenAsUL, a function part of Hierarchy, which actually does the HTML building but most importantly, calls the correct "children" method.
I say correct children method as there is a few:
AllChildren
AllChildrenIncludingDeleted
AllHistoricalChildren
Children
Because getSiteTreeFor is called without specifying the children method, it uses a hardcoded default of AllChildrenIncludingDeleted.
Now, time to sort the children...
Calling the function AllChildrenIncludingDeleted does a few calls but what we want to know is that it internally calls the extension method augmentAllChildrenIncludingDeleted.
So, to do what you are wanting to do, you likely will want to write an extension for SiteTree with the extended function augmentAllChildrenIncludingDeleted. First argument is the list of all children which are stored as an ArrayList.
Technical Note: It actually can be an ArrayList OR DataList
because if there are no live children, it returns the raw result of
stageChildren which is a DataList.
While both have sort functions, they may act differently.
ArrayList provides a sort function which would allow you to do what you were intending.
Something like this should work:
class CMSSiteTreeSortingExtension extends Extension
{
public function augmentAllChildrenIncludingDeleted($Children, $Context = null)
{
if ($this->owner->ClassName == 'GalleryPage')
{
//Do your class specific sorting here....
}
$Children = $Children->sort('Title DESC');
}
}
And just set the extension against SiteTree (or Page if you want, should still work).
Disclaimer: I haven't personally tried this however it follows the standard pattern for how Silverstripe works with extensions so you shouldn't have a problem.
I've been searching for a way to achieve this in SS4 when I couldn't get the above code to work. This is what I've come up with.
use SilverStripe\ORM\DB;
class MemberPage extends Page
{
public function onAfterWrite(){
parent::onAfterWrite();
$pages = MemberPage::get()->sort('Title');
$sortIndex = 0;
foreach ($pages as $page){
//sort indexes start at 1
$sortIndex++;
if ($page->Sort != $sortIndex){
//we can't use $page->write() here, otherwise it'll cause infinite loops,
//we'll just have to run the query on the database directly
DB::query("UPDATE SiteTree SET Sort = {$sortIndex} WHERE ID = {$page->ID}");
}
}
}
}
It's not exactly 'the silverstripe way' but it works.
I'm loading data using jQuery (AJAX), which is then being loaded into a table (so this takes place after page load).
In each table row there is a 'select' link allowing users to select a row from the table. I then need to grab the information in this row and put it into a form further down the page.
$('#selection_table').on('click', '.select_link', function() {
$('#booking_address').text = $(this).closest('.address').text();
$('#booking_rate').text = $(this).closest('.rate').val();
});
As I understand it, the 'closest' function traverses up the DOM tree so since my link is in the last cell of each row, it should get the elements 'address' and 'rate from the previous row (the classes are assigned to the correct cells).
I've tried debugging myself using quick and dirty 'alert($(this).closest(etc...' in many variations, but nothing seems to work.
Do I need to do something differently to target data that was loaded after the original page load? where am I going wrong?
You are making wrong assumptions about .closest() and how .text() works. Please make a habit of studying the documentation when in doubt, it gives clear descriptions and examples on how to use jQuery's features.
.closest() will traverse the parents of the given element, trying to match the selector you have provided it. If your .select_link is not "inside" .address, your code will not work.
Also, .text() is a method, not a property (in the semantical way, because methods are in fact properties in Javascript). x.text = 1; simply overrides the method on this element, which is not a good idea, you want to invoke the method: x.text(1);.
Something along these lines might work:
var t = $(this).closest('tr').find('.address').text();
$('#booking_address').text(t);
If #booking_address is a form element, use .val() on it instead.
If it does not work, please provide the HTML structure you are using (edit your question, use jsFiddle or a similar service) and I will help you. When asking questions like this, it is a good habit anyways to provide the relevant HTML structure.
You can try using parent() and find() functions and locate the data directly, the amount of parent() and find() methods depends on your HTML.
Ex. to get previous row data that would be
$('#selection_table').on('click', '.select_link', function(){
$('#booking_address').text = $(this).parent().parent().prev().find('.address').text();
});
Where parent stands for parent element (tr), then prev() as previous row and find finds the element.
Is there a demo of the code somewhere? Check when are you calling the code. It should be after the 'success' of AJAX call.
I have a function searchWorkByName that takes "key" as an argument and use SQOL to retrieve the data.
In visualforce side, I have a link that calls searchWorkByName but would like to be able to pass argument such as character 'a'
example, (this throws an error)
<apex:commandLink value="search!" action="{!searchWorkByName('aaa')}" />
Is it possible to do so if not what is the alternatives?
apex class
public class SearchWorkTest {
public PageReference searchWorkByName(String key) {
//find record of work names starting from provided key character
workNames = [select name from work__c where work__c.name like 'key%'];
return Page.searchResult;
}
}
visualforce
<apex:page standardController="work__c" extenstions="SearchWorkTest">
<!-- Is it possible to pass argument like 'foo' ? -->
<apex:commandLink value="search!" action="{!searchWorkByName}" />
</apex:page>
You can pass in parameters from a page into a function like this:
<apex:commandLink value="search!" action="{!searchWorkByName}">
<apex:param name="key" value="val"/>
</apex:commandLink>
Obviously, the value of the parameter in this case is fixed. If you want something dynamic (i.e. user types something and that is passed to the function), I'm not 100% sure how you'd do that, but I think it might be possible. However, the solution already posted skins the cat for you, but I thought I'd follow up with an alternative in case it's any use.
No, you cannot pass arguments to actions like that.
1 option is to make this variable a normal form field that user can type text/select from dropdown/whatever - if you'll use same name for a variable in Apex (and make it publicly visible by setters/getters), this will work without problems. Check out my answer at How do I integrate Salesforce with Google Maps? to get started.
Second option - if this search must be somehow done programatically without user having to click anything, if the data for example comes from page itself (i.e. is read in <apex:repeat> tag)... you could make a small helper page & controller and call them as components. There is no problem with passing data to components. Check documentation for <apex:component> and <apex:componentBody>. But I think first answer os most useful for you.
Good luck!
I have some data being loaded from a server, but there's no guarantee that I'll have it all when the UI starts to display it to the user. Every frame there's a tick function. When new data is received a flag is set so I know that it's time to load it into my data structure. Which of the following ways is a more sane way to decide when to actually run the function?
AddNewStuffToList()
{
// Clear the list and reload it with new data
}
Foo_Tick()
{
if (updated)
AddNewStuffToList();
// Rest of tick function
}
Versus:
AddNewStuffToList()
{
if (updated)
{
// Clear the list and reload it with new data
}
}
Foo_Tick()
{
AddNewStuffToList();
// Rest of tick function
}
I've omitted a lot of the irrelevant details for the sake of the example.
IMHO first one. This version separates:
when to update data (Foo_Tick)
FROM
how to loading data (AddNewStuffToList()).
2nd option just mixing all things together.
You should probably not run the function until it is updated. That way, the function can be used for more purposes.
Let's say you have 2 calls that both are going to come and put in data to the list. With the first set up, checking the variable inside of the function, you could only check if one call has came in. Instead, if you check it in the function that calls the data, you can have as many input sources as you want, without having to change the beginning function.
Functions should be really precise on what they are doing, and should avoid needing information created by another function unless it is passed in.
In the first version the simple variable check "updated" will be checked each time and only if true would AddNewStuffToList be called.
With the second version you will call AddNewStuffToList followed by a check to "updated" every time.
In this particular instance, given that function calls are generally expensive compared to a variable check I personally prefer the first version.
However, there are situations when a check inside the function would be better.
e.g.
doSomething(Pointer *p){
p->doSomethingElse();
}
FooTick(){
Pointer *p = new Pointer();
// do stuff ...
// lets do something
if (p){
doSomething(p);
}
}
This is clumbsy because every time you call doSomething you should really check you're
not passing in a bad pointer. What if this is forgotten? we could get an access violation.
In this case, the following is better as you're only writing the check in one place and
there is no extra overhead added because we always want to ensure we're not passing in a bad pointer.
doSomething(Pointer *p){
if (p){
p->doSomethingElse();
}
}
So in general, it depends on the situation. There are no right and wrong answers, just pros and cons here.