I have a page of products and for each of them, I want to have a form that uses AJAX to update the session. I've done this bit - just providing background.
I use the product's ID to create a different form class and name for each form, so each form is called something like this_form_name_567 and the next would be this_form_name_568 and so on.
There is a submit button per form. I'm having trouble figuring out
Which event is best to use so that the correct form will be identified when a submit button is clicked?
Once clicked, how to then make sure the correct value is taken from a hidden field (unique ID) within the submitted form so that I can populate a line of code such as:
$.post("compare_response.php", {compare_this: $("#compare_this").val()}, function(data){
}
You can use the .closest tree traversal method to get the form in which the button of interest is nested:
$("input[type=submit]").click(function() {
alert($(this).closest("form").attr("id"));
});
or even simpler, just get the element's form property :)
$("input[type=submit]").click(function() {
alert(this.form.id);
});
You can try it out here.
You can get the form you are submitting like this:
$('form').submit(function() {
var yourForm = $(this);
var hiddenValue = $(this).find('input[type=hidden]').val();
});
Of course you can get the hidden value differently, or if you have more than one hidden you'll have to give a little more information about it.
Related
i am using material UI dropdown component and trying to run a callback function only when user fills all the form and submits the form. On the call back function i intend to collect all the form field and generate url to call to api.
My problem is that i can not use onChange as stated solution in #560 as i want to collect all the detail only when user clicks the submit button. It is also weird that at the moment, i am able to get value of all the other form element like slider, textfield which uses material-ui but only dropdown does not seem to be working.
My call back function:
handleFilter: function(event){
event.preventDefault();
var location = this.refs.location.getValue();
var posted_date = this.refs.posted_date.getValue();
var radius = this.refs.distance.getValue();
var salary = this.refs.salary.getValue();
var jobtype = this.refs.jobtype.getValue();
console.log(jobtype);
}
In the above function "location, posted_date, radius, salary" returns value but "jobtype" which happens to be dropdown does not seem to return any value. It returns this error in console:
"Uncaught TypeError: this.refs.jobtype.getValue is not a function"
Here is my dropdown component :
<DropDownMenu menuItems={menuItems} ref="jobtype" />
May not be the right way but i figured out that
console.log(this.refs.jobtype) (jobtype is the refs value of dropdown in my case)
was giving the following result.
R…s.c…s.Constructor {props: Object, context: Object, state: Object, refs: Object, _reactInternalInstance: ReactCompositeComponentWrapper}
Inpecting it i found value of payload index (index number of selected item) inside state object within "selectedIndex" property. I used following code to access the selected index:
var jobtype = this.refs.jobtype.state.selectedIndex;
If there is a better way to do this please suggest.
I have an OpenUI5 form consisting of a number of Inputcontrols. These Inputcontrols are bound to a model using the OpenUI5 DataBinding as described in the documentation.
For example:
new sap.m.Input({
value: {
path: "/Position/Bezeichnung",
type: new sap.ui.model.type.String(null, {
minLength: 1,
maxLength: 128
})
}
})
As in the example above I'm using constraints on the stringlength.
When a User changes the Value of the Input, the Validation is triggered and according to the Validationresult one of the functions descripted here is called.
In these functions I'm setting the ValueState of the control like this:
setupValidation: function() {
var oCore = sap.ui.getCore();
oCore.attachValidationError(function (oEvent) {
oEvent.getParameter("element").setValueState(sap.ui.core.ValueState.Error);
});
oCore.attachValidationSuccess(function (oEvent) {
oEvent.getParameter("element").setValueState(sap.ui.core.ValueState.None);
});
oCore.attachFormatError(function (oEvent) {
oEvent.getParameter("element").setValueState(sap.ui.core.ValueState.Error);
});
oCore.attachParseError(function (oEvent) {
oEvent.getParameter("element").setValueState(sap.ui.core.ValueState.Error);
});
},
Let's assume the bound model variable is initial.
I'm loading the view, the property value is parsed and displayed as empty.
The Validationerror/Parseerror method is not called although the constraints are not met.
This seems to be standard behaviour of OpenUI5. Only changes in the Control will be a validated.
Now let's assume I've a submit button and the Value of the Inputcontrol is still empty. When the user hits the submit button I'd like to trigger the DataBinding Validation for all childcontrols of my view. This would validate the above mentioned input and would result in an errorstate.
My question is: How can I trigger the databinding validation for all childcontrols of my view?
There is another question on SO where the poster asks for a way to define required fields. The proposed solution is to call getValue() on the control and validate the value manually. I think this is kind of cumbersome as formating and constraint information and logic is already present.
I suggest looking into field groups.
An example here in the UI5 docs
Field Groups allow you to assign group IDs to the input fields. Then you can call all of the input fields at once. You can set the name property and required property on each <Input> separately in your view, allowing you to handle some logic when you perform validation.
You can call this.getView().getControlsByFieldGroupId("fieldGroupId"), which will return an array of the input controls. Then you can loop through the controls, pass them through your logic, and use setValueState() to show the results.
Or, you can assign the validateFieldGroup event on the parent container, which is usually a form, but can be anything like a <VBox> that contains the controls. When the users focus moves out of the field group, the event is fired. You can then use the event handler in your controller to perform the validation.
In your case, I would assign a press event to your submit button, and in the handler, call the field group by ID and loop through the controls. At the end of your function, check to see if all fields are validated before continuing.
View
<Input name="email" required="true" value="{/user/email}" fieldGroupIds="fgUser"/>
<Input name="firstName" required="false" value="{/user/firstName"} fieldGroupIds="fgUser"/>
<Button text="Submit" press="onSubmit"/>
Controller
onSubmit: function() {
var aControls = this.getView().getControlsByFieldGroupId("fgUser");
aControls.forEach(function(oControl) {
if (oControl.getRequired()) {
//do validation
oControl.setValueState("Error");
oControl.setValueStateText("Required Field");
}
if (oControl.getName() === "firstName") {
//do validation
oControl.setValueState("Success");
}
});
var bValidated = aControls.every(function(oControl) {
return oControl.getValueState() === "Success";
});
if (bValidated) {
//do submit
}
}
The concept goes like this.
Use custom types while binding, to define validations. Validation
rules go inside these custom types (in the method 'validateValue').
When Submit is pressed, loop through the control hierarchy and
validate each control in your view. (By calling 'validateValue'
method of the Custom Type).
Validator (https://github.com/qualiture/ui5-validator ) uses this concept and it is a small library to make your life easy. Its main advantage is that it recursively traverses through the control library.
Using Message Manager (using sap.ui.get.core().getMessageManager() ) is the way to show the validation messages on the UI control.
Triggering data binding validations is not possible. Rather for empty fields that are having required property true you can do a work around using jQuery.
Please refer my answer to this same problem at Checking required fields
I want to append more fields based on a users selection of Type of Event.
What is the best way to add additional fields based on the users selection without refreshing the page?
I was thinking ajax call when user clicks Add the details and return html?
Is there a way to do this using the template system with a series of if/else conditionals?
Two solutions come to mind here...
1)
Attach a jQuery change handler to the 'Type of Event' select element, and execute an ajax request to return the dynamic fields that will need to be displayed.
$('#TYPE_OF_EVENT_ID').change(function() {
$.get('/api/to/return/dynamic/fields/', {'type_of_event': $(this).val()}, function(data, textStatus, jqXHR) {
# Update DOM with dynamic content return by data (should probably be JSON)
});
});
2)
Hard code the logic straight into your javascript to handle the case statements to display the dynamic fields depending upon the value which is selected in the 'Type of Event' select element.
$('#TYPE_OF_EVENT_ID').change(function() {
switch($(this).val()) {
case 'Special Event':
# Show Special Event Fields
case 'Non Special Event':
# Show Non Special Event Fields
}
});
I'd recommend option 1 as it scales better keeping this logic on the server to be database driven.
Let´s say I have some sort of datagrid and I want to add a couple chained filters like in this site:
http://www.yelp.com/search?find_desc=bar&ns=1&find_loc=Minneapolis%2C+MN
(sort by,distance,price etc).
Each time a user clciked in a filter link it will update the content of datagrid accordingly. But I would also need to update the links in other filters to take account of the changes. Ex: if i change the order field I need to add/update ?order_field=x in all the other filters links.
What you think is the best way to implement such scenario?
Should i create a function that, when a filter link is clicked, it update the query string params of all the other filters? Or use hidden fields to record the selected option in each filter?
I would like a reusable solution if possible.
Since the data is loading via AJAX, there shouldn't be any links to update - at least not if you mean anchor tags <a>. You don't even need to store the filters in a hidden field.
I would store all the filters as a JSON object. Depending on how your API is set up, you may have to convert the JSON object to something usable by your API or you may even be able to pass on the JSON object directly in the $.ajax request.
This sample code assumes you have a textbox with id="price" in the markup. I intentionally left convert_filters_to_parameters blank because you didnt provide any details as to your API. jQuery will in turn serialize those parameters into a GET or POST request before it sends them out.
var filters = {
distance:null,
price:null,
sortBy:'distance'
}
//this assumes you have a textbox with id="price"
$('#price').changed(function()
{
filters.price = $(this).val();
refresh_data();
});
function refresh_data()
{
var parameters = convert_filters_to_parameters(filters);
$.ajax('/my_api',
{
//i left out a lot of properties here for brevity
data: parameters,
success: function(response) { alert(response); }
});
}
I have a Partial View that renders WebGrid. My controller looks like
public ActionResult Index()
{
return View();
}
public ActionResult GetUserList(int? page, string sort, string sortdir)
{
var model = UserModel.getList(page,sort,sortdir);
return PartialView("_UserList",model);
}
Index.cshtml :
....
#Html.Action("GetUserList")
The problem is that every time I click on grid navigation or sort links it calls Index method. How can I make Webgrid to execute a different action (GetUserList in this case)? I'm sure I can prepend GetUserList to all links in grid using jquery, but I believe it should be a better way.
It's also possible that what I'm doing is completely wrong, so thanks for your suggestions.
After lot of monkeying around and digging (and even fiddling with Reflector with WebGrid's source code), I came to the conclusion that with WebGrid, you cannot control/change the Header link action.
To create the header link URL, the path is taken from HttpContext.Request.Path, so there is no way to customize it to point to a different route.
One very ugly hack would be to tap into to jQuery Ajax's events (since the header link uses jQuery.load to sort) and overwrite the URL:
Album Id
Better solution would be to use:
Telerik Grid which lets you specify custom routes and also offers much more flexibility in rendering your layout
or MvcContrib Grid (not sure if this lets you modify header links but definitely offers more flexibility than WebGrid)
#MrChief had the idea above about the ugly hack...I put that together. Here is the main code that I used to do this. It does, indeed, hijack the ajax call before it is put on the wire. The key is to modify the URL that is getting sent because the grid will grab that URL from HttpContext.Request.Path. and plug it into the onclick for the anchor element.
I put this into my main common.js and will simply attach a function to capture the ajaxSend event which happens just before the data is sent.
// Used to hijack the sending of all AJAX calls. Before it sends the call to the server, it checks to see if the
// active element (the element that prompted the call) is marked with a given class. If so, then it will perform
// the given operation.
$(document).ajaxSend(function (event, jqXHR, ajaxOptions) {
var activeElement = document.activeElement;
if ($(activeElement).attr('redosorturl') != null) {
// If this is a sort anchor link from a grid that needs to have the sort link redone, do it here.
// the code is in the eipGrip.js file.
if ($(activeElement).attr('redosorturl').toString() == 'redoSortURL') {
var newURL = RedoGridSortURL(activeElement, ajaxOptions.url.toString());
ajaxOptions.url = newURL.toString();
}
}
return false;
});
When rendering the page, I have marked the tag in column header that contains the incorrect URL with a class named "redosorturl', so I know when I hijack the ajax call, the operation has to be done on this element. I then call a custom function that gives me the correct URL, then the ajaxOptions.url is then rewritten with that new URL.
I have to pass the activeElement to that rewrite function so I can traverse up the DOM to get the grid information, where I have put data like the controller and action method that is used along with and IDs and other info that I use for the URL. Likewise, I pass in the current url string because the grid will inject a token at the end of the url that I parse off and put on the new url.
Your conclusion isn't right. You just need to wrap your webgrid in a Get form:
using (Html.BeginForm("GetUserList", "ThingaMaBob", System.Web.Mvc.FormMethod.Get))
{
var grid = new WebGrid(
...
));
Html.Hidden(grid.SortFieldName, grid.SortColumn);
Html.Hidden(grid.SortDirectionFieldName, grid.SortDirection == SortDirection.Ascending ? "ASC" : "DESC");
}
The hiddens are so that the sort dir and sort field end up in parseable form in the querystring. You end up with urls like localhost/ThingaMaBob/GetUserList?someotherfields=whatever=&sort=city&sortdir=ASC
If you remove [HttpPost] attribute and let the route come to the same function. you'll find the Request["page"] value in your method. this will allow you to put a check on Request["Page"] value.