I have a control that overrides Render() and creates its HTML dynamically,
here is a simiplified version of it:
public class CReportBillReadings : Control
{
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.Write(Html);
}
private string Html
{
get
{
// render result table
var sb = new StringBuilder();
sb.Append("<ul id=\"export\"><li><img src=\"/resources/images/pdfIcon16x16.gif\" /></li></ul><div class=\"clear\"></div>");
And so on.
What it actually does is draw a table according to the given data.
I want to add an asp button to the html ,
so that I can register to its event on the server side.
The only ways I found to do this is:
Add this button to a place holder - not applicable here because the entire HTML is generated dynmically.
Add a jquery button - but I need to handle the event on the server side.
I read about a promising lead here:
http://www.evagoras.com/2011/02/10/how-postback-works-in-asp-net/
But I still can't figure out how to use it- because in the examples I've seen the button is still declared in .aspx and not dynmically in the code.
You can inherit your control from Button control instead of Control. In this case you will have events and all other properties of button. And you can also render table in overrided Render method.
Related
The goal is to download a file from a website using HtmlUnit. The download link is generated in JavaScript and dynamically injected into a dropdown menu, based on a previous selection in another dropdown menu. Both dropdown menus are dynamically injected via JavaScript and have the same element structure.
So in general, when loading the page, it shows only the first dropdown list. This sample shows the initial element structure when the page is loaded. When selecting an item from that list, the second dropdown list appears and the element's source structure has been updated to this. When clicking the second dropdown list, the page shows a list with several options containing a direct link to a file. This example shows the selection of the first dropdown list is made, showing the options of the second dropdown list (with a green download icon). At this moment, the source has been updated to with this code (i.e. similar to the previous div). Clicking on any of the list items will start an immediate download of the required file.
What happens in my HtmlUnit application is, when I trigger the same steps until the second dropdown list should appear, the div with id 'formatSelect' stays empty. It looks like the JavaScript code is not being executed for that part only, while it does so for the first part.
A brief sample of my code:
private static final String URL = "https://some.url.org/";
private static final String xpathDeltaSelect = "div[#id='div1']";
private static final String xpathFormatSelect = "div[#id='div2']";
private static final String xpathDropDownToggle = "div[#class='dropdown-toggle']";
private static final String xpathSearchButton = "div/input[#type='search']";
private static final String xpathListBox = "div/ul[#role='listbox']";
webClient_ = new WebClient(BrowserVersion.CHROME);
webClient_.getOptions().setRedirectEnabled(false);
webClient_.getOptions().setDownloadImages(false);
webClient_.getOptions().setThrowExceptionOnScriptError(false);
webClient_.getOptions().setThrowExceptionOnFailingStatusCode(false);
HtmlPage page = webClient_.getPage(URL);
webClient_.waitForBackgroundJavaScript(JAVASCRIPT_TIMEOUT);
final HtmlDivision divDeltaSelect = selectWrapper(page).getFirstByXPath(xpathDeltaSelect );
final HtmlDivision divDeltaDropdown = divDeltaSelect.getFirstByXPath(xpathDropDownToggle);
final HtmlInput inputDelta = divDeltaDropdown.getFirstByXPath(xpathSearchButton);
inputDelta.focus();
final HtmlUnorderedList ulDeltaSelect = divDeltaSelect.getFirstByXPath(xpathListBox);
Iterator<DomElement> iterator = ulDeltaSelect.getChildElements().iterator();
// Iterate to the desired item and 'click'
...
page = li.click();
break;
}
// So far, everything goes well.
final HtmlDivision divFormatSelect = selectWrapper(page).getFirstByXPath(xpathFormatSelect);
System.out.println(divFormatSelect.asXml());
// At this point, divFormatSelect is still empty...
The output of the sysout at the end is the following:
<div data-v-0d6d77a2="" id="formatSelect">
<!---->
</div>
The selectWrapper method is briefly doing the following (because there are 3 similar "selectWrapper" divs that return different data files, but I only need the first one):
final List<DomElement> elements = page.getElementsById("selectWrapper");
...
return elements.get(0);
I did notice that the initial page load at line
HtmlPage page = webClient_.getPage(URL);
throws the following JavaScript error:
com.gargoylesoftware.htmlunit.ScriptException: missing ) after formal parameters (https://some.url.org/path/to/js/javascript.js#1)
... + stacktrace
Could this be related to this bug? However, I do not understand why the first injected code can be retrieved, but not the second part, while the browser interface does the processing correctly. Am I missing something?
I have a viewmodel along the lines of
public class Person
{
public Person()
{
Locations = new List<Location>();
}
public int Id {get;set;}
public string Name {get;set;}
public List<Location> Locations {get;set}
}
public class Location
{
public int Id {get;set;}
public string LocationName {get;set;}
}
The class Person is used to populate a simple view that displays the Person details and below the list of Locations.
I have created a button that adds a new Location by creating a new div, populating html and then appending it to a div containing all of the Locations.
$('#Locations').append(newItem.html()); <---- newItem - contains new div
<div id="Locations">
<div class="location">
</div>
...
...
<div class="location">
</div>
</div>
All of this works, but on posting back to the controller, the new locations are not passed back in the viewmodel.
How is this done?
Can I see the data in any other way?
I have used ajax before, but cannot use it on this specific site.
Rico Suter has a great blog about this at https://blog.rsuter.com/asp-net-mvc-how-to-implement-an-edit-form-for-an-entity-with-a-sortable-child-collection/.
The extension methods he created are really easy to use and customize.
As far as I can see you're not actually submitting any of the new locations back - you need to encapsulate them into hidden input fields (as well as the divs for displaying the information to the user) so that when you resubmit your form data, the new locations are included in the request and can be captured by your controller action.
It sounds like you are trying to bind an array when you post back. The general form to format your HTML input tags so that they will reconstitute as a list when you bind in post is described in several places such as Phil Hack - Model Binding To A List. So if you structure your DOM changes to adhere to the format described, your extra rows should bind on post.
Changed code to use Ajax call, then on return add the relevant html to add hidden fields required.
I'm a novice in the java world, so thanks in advance for the help.
What I am trying to do is this:
I have two EditText fields and a Button. When I hit the button, I want to take the contents of the two fields, combine them and display them below button. The fields are cleared. Then if they are filled again and the button is pushed, combine the fields and display them below previously generated textview.
Example:
Before Button Click:
eT1___ eT2____ |Button|
After Button click
eT1___ eT2____ |Button|
eT1-eT2
After 2nd Button click
eT1___ eT2____ |Button|
eT1-eT2
eT1-eT2
And so on however many times the button is clicked. What I need help with is the new views that are being added. Currently I am using a Relative Layout. What I am trying to figure out is how to reference the new textView's that I am creating within the code so that when new views are added, they can reference the previously added view.
This is what I've been attempting at the moment:
private TextView createNewTextView(String desc, Double cost){
RelativeLayout.LayoutParams lparams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);;
TextView newTextView = new TextView(this);
if (i==1) {
lparams.addRule(RelativeLayout.BELOW, mButton.getId());
}
else {
lparams.addRule(RelativeLayout.BELOW, mLayout.getId());
}
lparams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
newTextView.setLayoutParams(lparams);
newTextView.setText(desc + " - $" + cost);
newTextView.setTag(i);
i++;
return newTextView;
}
I was trying to use "i" as an incrementing variable that I could use to reference the previous view by except on the first pass when it the new view references based on the button.
Any help would be greatly appreciated. Thanks in advance and for everything I've lurked already!
Create a member variable of ArrayList of TextView :
ArrayList<TextView> textviews = new ArrayList<>();
and then in your createNewTextView() method, store the reference of TextView you are adding:
textviews.add(newTextView);
So the TextView you've added at the first will be at index 0 and so on. Then you can easily reference your TextViews.
Suppose you want to get the second TextView,
TextView secondTv = textviews.get(1);
Just add your textviews to a global list and find them based on the tag from there.
I am currently customizing a SugarCRM instance. In one module I have created a few custom fields and a drop down menu. Based on the selection of the drop down menu I want to show or hide some of my fields. This works fine. My question is about the initial load of the page: In that case, all possible fields are displayed - not only the ones that should be displayed based on the default selection in the drop down menu.
My first instinct was to register the onload event and just hide whatever I don't need to see when the page is loaded. But I couldn't find anywhere to place it as I don't want to change /modules/... directly. I'd like to restrict my changes to /custom/modules...
Any ideas?
Here few ways to control dropdown visibility:
There is admin when you edit field there is formula and visibility functionality you can also try this.
Create custom edit view and add javascript code in display function of view.
custom/modules//views/view..php
.php');
class View extends View {
function View() {
parent::View();
}
function display() {
echo '';
parent::display();
}
}
?>
Custom Javascript can be added to editviewdefs.php
'templateMeta' =>
array (
includes' => array (
1 =>array ('file' => 'custom/modules/<ModuleName>/js/custom.js',),
),
),
How can I capture the onclick event for the up & down buttons on a spinner component from JQuery?
I am able to capture the onchange for the associated textfield with the following code, but the up & down spinner buttons are not triggering the event:
Spinner<Integer> psbBudget = new Spinner<>("psbBudget", new PropertyModel(psb,"psbBudget"), Integer.class);
psbBudget.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget art) {
Integer inputS = (Integer) getFormComponent().getConvertedInput();
BigDecimal spinnerTime = BigDecimal.valueOf(inputS);
unallocatedTimeMap.put(psb, unallocatedTimeMap.get(psb).subtract(spinnerTime));
art.add(budgetListViewContainer);
}
});
listItem.add(psbBudget);
li.add(new Label("remainingBudgetLabel", new Model(unallocatedTimeMap.get(psb))));
Just to make it more difficult on myself, this spinner is contained in a ListView as the number of budget items is variable. I am getting the remainingBudgetLabel to reflect the changes that are made within the text field, but not the changes made using the spinner buttons!
I have started exploring injecting my own javascript with the following lines:
// behavior to capture changes made using spinner buttons (doesn't work!!!)
add(new Behavior() {
#Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
response.render(OnDomReadyHeaderItem.forScript("$('.ui-spinner-button ui-spinner-up ui-corner-tr ui-button ui-widget ui-state-default ui-button-text-only').click(function() {$(this).siblings('input').change();})"));
}
});
Here is the relevant section of the declared html:
<table>
<tr>
<td/>
<th>Budget</th>
<th>Remaining</th>
</tr>
<tr wicket:id="budgetListView">
<th wicket:id="budgetLabel">Budget
</th><td><input wicket:id="psbBudget" type="text" style="width:3em" maxLength="2"/>
</td><td wicket:id="remainingBudgetLabel"/>
</tr>
</table>
So I guess this is somewhat of a two part question:
1 - Is there any hope of this javascript injection working (I am just getting my feet wet with javascript) or is there a way to do this more correctly within Wicket?
2 - If javascript is the only way, how can I correctly find the elements when I can't control the id attribute of the generated jquery (or can I control that somehow?)?
I am using the wicket-jquery-ui 6.2.1 library with wicket 6.6.0. Thank you.
EDIT
The generated html I posted previously (now removed) was from right clicking and selecting viewSource, but does not show the actual generated html from jquery. Please see below for a screenshot from the F12 Developer Tools in IE9 that shows the generated html for the spinner includes the original <input> plus two generated <a> tags for the spinner buttons.
I just saw this post...
Just to say that you had 2 other options:
1/ You have you own extended spinner with:
#Override
protected void onConfigure(JQueryBehavior behavior)
{
super.onConfigure(behavior);
behavior.setOption("onspinstop", this.onStopBehavior.getCallbackFunction());
//onStopBehavior is an JQueryAjaxPostBehavior, see how AjaxDatePicker works for instance
}
The second option is to ask for an AjaxSpinner, because it makes sense, either through the github project or the google group:
https://github.com/sebfz1/wicket-jquery-ui/
http://groups.google.com/group/wicket-jquery-ui/
Best regards,
Sebastien.
As much as I hate to answer my own question, here it is:
I was able to capture the events with the following code. The "onchange" captures the event when someone manually enters a number in the field, the "onspinstop" captures the event after the component has been updated by clicking the up/down arrow:
Spinner<Integer> psbBudget = new Spinner<>("psbBudget", new Model(allocatedTimeMap.get(psb)), Integer.class);
psbBudget.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget art) {
Integer input = (Integer) getFormComponent().getConvertedInput();
// Do whatever with updated input //
art.add(remainingBudgetLabel,remainingBudgetLabel.getMarkupId());
}
}
psbBudget.add(new AjaxFormComponentUpdatingBehavior("onspinstop") {
#Override
protected void onUpdate(AjaxRequestTarget art) {
Integer input = (Integer) getFormComponent().getConvertedInput();
// Do whatever with updated input //
art.add(remainingBudgetLabel,remainingBudgetLabel.getMarkupId());
}
}
I was able to figure this out after looking at the API documentation for the JQuery UI Spinner component then trying every possible combination of the event strings. To do this correctly you have to put the string all in lowercase and add "on" string to the beginning of the event name.
One additional thing that I learned during this exercise was that I can update (I think I am actually replacing them?) individual components within a listview by calling art.add(component, component.getMarkupId());. This is SO much better than what I was doing before with listView.setList(updatedList) and art.add(listViewContainer);