Can I exclude a specific id from update=":myComponent"?
I have a larger page (with several tables, input fields and so on) which is wrapped in a <p:panel id="outerPanel">. Most of the time, I just execute update=":outerPanel", which works quite fine. But now I'm facing a problem that I have to update the page except for ONE table.
How can I exclude that table (or any component in general) from an update process?
If the ID not to refresh can be identified by some logic (i.e.:
if(myVar==1) { idToExclue = 'id1';}
else // ... etc
Then you can try building a JSF call using the jsf.util.chain: (Note this uses Mojarra JSF impl.)
function myJsfSubmit(callerElement) {
var myIdsToSubmit = 'main-form:id-to-refresh-1 main-form:id-to-refresh-2';
// put your logic to exclude the desired ID here
// ...
// then
jsf.util.chain(callerElement,null,'mojarra.ab(this,event,\'action\',
\'main-form:id_to-submit\',\'+ myIdsToSubmit +\')');
return false;
}
And use that above function on the onClick() or onSubmit() of your form or component:
<h:commandButton onclick="myJsfSubmit(this)" />
No, I think that is not possible from the facelet. On the server side you could manipulate the ajax request, but I don't think that is what you want.
Maybe your view allows to wrap some h:panelGroup around the parts to update.
Related
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 would like to have a central place where I monitor ajax requests and in certain situations abort them.
The only thing I don't know to do is to actually abort the ajax request from one central function.
I Imagine that the solution would look something like this:
jsf.ajax.addOnEvent(function(data) {
if (data.status === 'begin') {
// abort the ajax request
}
});
But I might be mistaken.
Thanks!
This is not possible by the standard JSF JS API. Your best bet is to add an onclick or onchange handler on the calling JSF component which returns false if the desired condition is met to abort the ajax request.
<h:commandButton onclick="return mayFireAjax(this)">
<f:ajax />
</h:commandButton>
You can if necessary abstract this away with jQuery.on() (or jQuery.delegate() or jQuery.live() depending on the jQuery version used) on a common selector so that you don't need to repeat it on every desired component.
$(someSelector).on("click", function() {
return mayFireAjax(this);
});
If you have control the source, you can always attach a onClick to the UICommand component. But in some situation, you do not have access to source. For example, the ajax is provided by some third-party component. You do not want to mess up with their components.
First, I have a small js library.
var FxJSFBegin = "JSFBegin";
if (jsf) {
var originalRequest = jsf.ajax.request;
jsf.ajax.request = function(source, oevent, options) {
var event = $.Event(FxJSFBegin);
event.options = options;
event.originalEvent = oevent;
$(source).trigger(event);
if (event.isDefaultPrevented()) {
return;
} else {
originalRequest.apply(null, arguments);
}
};
}
This piece code proxies original JSF ajax call. It uses jQuery to fire a "JSFBegin" event. Integration code can listen this event using jQuery mechanism. Listener can cancel the jsf call using event.preventDefault().
Requirement:
jQuery
This piece code should be placed after jsf.js is loaded.
Using global="false" will help you prevent calling the ajax status for an ajax call. Please refer to the following link.
Different Ajax statuses for different components in PrimeFaces
I have an issue concerning the use of a remoteFunction component in java script function ; I am using Grails 1.3.7.
I have few div in a page which contain a div I want to update. Each div I want to update has its own id (fullUrlSaProfilDivX) where X is and unique ID in the page.
I want to update two div (one after one).
I created a java script function :
<g:javascript>
function removeSelectedProfilAssoc(urlSaId, profilAssocId) {
${ remoteFunction (action:"delete", update:'fullUrlSaProfilDiv'+urlSaId, controller:"profilAssoc", params:'\'id=\'+profilAssocId', options:[asynchronous:false]) };
${ remoteFunction (action:"listUrlSaProfil", controller:"profilAssoc", update:'lightUrlSaProfilDiv'+urlSaId, params:'\'urlSa.id=\'+urlSaId') };
};
</g:javascript>
Called by a link :
I want to update the div linked with the button (linkage with unique id).
I can't figure out why in the generated page I got null instead of the id and the div is not refresh :
function removeSelectedProfilAssoc(urlSaId, profilAssocId) {
new Ajax.Updater('fullUrlSaProfilDivnull','/_Pong2WAR/profilAssoc/delete',{asynchronous:false,evalScripts:true,parameters:'id='+profilAssocId});;
new Ajax.Updater('lightUrlSaProfilDivnull','/_Pong2WAR/profilAssoc/listUrlSaProfil',{asynchronous:true,evalScripts:true,parameters:'urlSa.id='+urlSaId});;
};
Am I doing something wrong ? How can I pass the id of the div I want to refresh and add it refreshed ?
Thank you for having a look !
Benjamin
ahah, you are mixing up javascript and gsp. I did it many times also, it can be tricky to find out!
In your case urlSaId is a javascript var, but you are using it in a GSP function call so it will be null....
Unfortunatly the workaround is not easy since the remoteFunction won't let you concatenate properly the javascript variable in the update since what you want is:
new Ajax.Updater('fullUrlSaProfilDiv'+urlSaId,'/_Pong2WAR/profilAssoc/delete',{asynchronous:false,evalScripts:true,parameters:'id='+profilAssocId});
What i suggest is to build directly this Ajax.Updater(..) without the use of remoteFunction (or something similar):
<g:javascript>
function removeSelectedProfilAssoc(urlSaId, profilAssocId) {
new Ajax.Updater('fullUrlSaProfilDiv'+urlSaId,'${createLink(action:"delete", controller:"profilAssoc")}',{asynchronous:false,evalScripts:true,parameters:'id='+profilAssocId});
new Ajax.Updater('lightUrlSaProfilDiv'+urlSaId,'${createLink(action:"listUrlSaProfil", controller:"profilAssoc")}',{asynchronous:true,evalScripts:true,parameters:'urlSa.id='+urlSaId});;
};
</g:javascript>
On a side note now, i am always using jquery instead, it simplifies all ajax in your GSP.
replace remoteFunction by
jQuery.ajax({
type:'POST',
data:'territorio='+territorio_id+'&anho='+anho+'&id='+firstIndicador+'&div='+divId+'&divmap='+divmap,
url:'/observatoriograils/eje/indGeneralporAnhoyTerritorio',
success:function(data,textStatus){jQuery('#'+divId).html(data);},
error:function(XMLHttpRequest,textStatus,errorThrown){}});
where data are the parameters, url is /url/controller/function and divId is the name of div for update
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.