I am invoking a method from rendered attribute, where I noticed that the method is triggered multiple times in RENDER_RESPONSE phase.
It was also noticed that the method was trigered many times in other phases (APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS etc.) also.
I saw a related query (Why is the getter called so many times by the rendered attribute?) where the reason behind these calls were told.
Is there a way where we can control this, so that the method is invoked only once.
My usage
<rich:panelMenuItem label="Menu1" actionListener="#{testMenuMB.panelMenuClickedAjax}" rendered="#{testMenuMB.checkForRendering('RoleA,RoleB')}"></rich:panelMenuItem>
public boolean checkForRendering(String rolesString){
System.out.println("Roles-->"+rolesString+FacesContext.getCurrentInstance().getCurrentPhaseId());
boolean authorized = false;
String [] rolesArray = rolesString.split(",");
for (String string : rolesArray) {
if(string!=null && accesibleRolesMap.containsKey(string)){
authorized = true;
break;
}
}
return authorized;
}
You cannot control the number of times the method is called, its framewroks lifecycle. You should find appropriate place to set the boolean value e.g. on command click action method, use this boolean value in render method so that logic is executed once in action method and only boolean is returned.
Other thing is you can protect the logic with if condition by probing for current lifecycle phase for render response.
if(FacesContext.getCurrentInstance().getRenderResponse()){
//logic
}
But I'll prefer first option.
Related
Suppose I want to pass one object (reference) through several pages. I can navigate and pass parameters via Frame.Navigate(typeof(FirstPage), object). But how to pass the reference back on back press properly?
protected override void OnNavigatedTo(NavigationEventArgs e) {
if (e.Parameter is SomeClass) {
this.someObject = (SomeClass)e.Parameter;
}
else {
this.someObject = new SomeClass();
}
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
SystemNavigationManager.GetForCurrentView().BackRequested += OnHardwareButtonsBackPressed;
base.OnNavigatedTo(e);
}
private void OnHardwareButtonsBackPressed(object sender, BackRequestedEventArgs e) {
// This is the missing line!
Frame.Navigate(typeof(FirstPage), this.someObject);
}
But when I press back button it goes back to the FirstPage OnNavigatedTo with no parameter, and then back to the SecondPage OnHardwareButtonsBackPressed and then back to FirstPage OnNavigatedTo with filled parameter.
Could you please advice me some better approach?
In your back handler, don't navigate forwards again, just call GoBack -- and it's typically easier if you handle that at a global level rather than at a page level.
You can store your application state (the things you want to persist across page navigations) in global / static objects, or you could directly modify the object that was passed from the initial navigation (if the calling page still has a reference, it will be able to see the changes).
I would consider doing a search for "MVVM Windows Apps" and looking at some of the results to learn about a common way of building XAML apps.
I have a webMethods CAF task that has a big form with a save button and a submit button. Many elements on the form have validation. The user needs to be able to hit Save and have the form submitted to the back end model so it can be saved as task data, without firing validation. Hitting Submit should fire validation.
How can I configure the page to do this. It's such a normal requirement, and I'm stuck!
It's not much fun.
Give your Save button a nice ID. Say, saveButton
Create a getter in your Java code that returns a boolean. Inside it, return true if the button's ID is one of the submitted fields, otherwise false:
private boolean validationRequired() {
return mapValueEndsWith((Map<String, String>)getRequestParam(),
new String[] {
"saveButton", // Your save button
"anotherButton", // Perhaps another button also shouldn't validate
"myForm:aThirdButton" // perhaps you want to be specific to a form
});
}
In every field that should be required, except on Save, bind the Validation->required attribute to your validationRequired getter.
That's it! Very tedious with a lot of fields on the screen, but it works.
P.s. what's mapValueEndswith? Just a utility; removed whitespace for compactness' sake:
private boolean mapValueEndsWith(Map<String, String> haystack, String[] needles) {
for(String needle : needles) if(mapValueEndsWith(haystack, needle)) return true;
return false;
}
private boolean mapValueEndsWith(Map<String, String> haystack, String needle) {
for(String value : haystack.values()) if(value.endsWith(needle)) return true;
return false;
}
As I see the approach provided, works only if the form contains only string type fields. If there are any other data types like integer, float, data-time are mapped to UI fields and conversion is used then this might fail if wrong data is entered in those fields.
In my controller, I have a before() function that calls parent::before() and then does some additional processing once the parent returns. based on a specific condition, I want to "save" the original request and pass execution to a specific action. Here is my before() function.
public function before() {
parent::before();
$this->uri = Request::Instance()->uri;
$match = ORM::factory('survey_tester')
->where('eid','=',$this->template->user->samaccountname)
->find();
if (!$match->loaded()) {
self::action_tester("add",$this->template->user);
}
}
And the action that is being called..
public function action_tester($op=null,$user=null) {
$testers = ORM::factory('survey_tester')->find_all();
$tester = array();
$this->template->title = 'Some new title';
$this->template->styles = array('assets/css/survey/survey.css' => 'screen');
$this->template->scripts = array('assets/js/survey/tester.js');
$tester['title'] = $this->template->title;
$tester['user'] = $this->template->user;
switch ($op) {
case "add":
$tester = ORM::factory('survey_tester');
$tester->name = $user->displayname;
$tester->email = $user->mail;
$tester->division = $user->division;
$tester->eid = $user->samaccountname;
if ($tester->save()) {
$this->template->content = new View('pages/survey/tester_add', $admin);
} else {
$this->template->content = new View('pages/survey/tester_error', $admin);
}
break;
default:
break;
}
}
This all seems to work fine. This is designed to prompt the user for a specific piece of information that is not provided by $user (populated by LDAP) if this is the first time they are hitting the controller for any reason.
The problem is the views are not rendering. Instead control passes back to whatever action was originally requested. This controller is called survey. If i browse to http://my.site.com/survey and login with new user info, the record gets written and i get the action_index views instead of my action_tester views.
I cannot figure out what I am doing wrong here. Any ideas will be appreciated. Thank you.
EDIT: I managed to get this working (sort-of) by using $this->request->action = 'tester'; but I'm not sure how to add/set new params for the request yet.
The issue is that you are calling your method (action_tester), but then Kohana is still going to call the original action after the before method is called, which is going to change the response content overwriting the changed made in action_tester().
You can change the action being called (after before is called) inside your before() method:
$this->request->action('action_tester');
After the before method is called, it should then call the new Action (action_tester) rather than the old one, but then you need to do something about the way you are passing your parameters then.
Or you could just redirect the request upon some condition:
if($something) {
$this->request->redirect('controller/tester');
}
This doesn't seem like a nice way to do it anyway.
Im using subsonic 2.2
I tried asking this question another way but didnt get the answer i was looking for.
Basically i ususally include validation at page level or in my code behind for my user controls or aspx pages. However i haev seen some small bits of info advising this can be done within partial classes generated from subsonic.
So my question is, where do i put these, are there particular events i add my validation / business logic into such as inserting, or updating. - If so, and validation isnt met, how do i stop the insert or update. And if anyone has a code example of how this looks it would be great to start me off.
Any info greatly appreciated.
First you should create a partial class for you DAL object you want to use.
In my project I have a folder Generated where the generated classes live in and I have another folder Extended.
Let's say you have a Subsonic generated class Product. Create a new file Product.cs in your Extended (or whatever) folder an create a partial class Product and ensure that the namespace matches the subsonic generated classes namespace.
namespace Your.Namespace.DAL
{
public partial class Product
{
}
}
Now you have the ability to extend the product class. The interesting part ist that subsonic offers some methods to override.
namespace Your.Namespace.DAL
{
public partial class Product
{
public override bool Validate()
{
ValidateColumnSettings();
if (string.IsNullOrEmpty(this.ProductName))
this.Errors.Add("ProductName cannot be empty");
return Errors.Count == 0;
}
// another way
protected override void BeforeValidate()
{
if (string.IsNullOrEmpty(this.ProductName))
throw new Exception("ProductName cannot be empty");
}
protected override void BeforeInsert()
{
this.ProductUUID = Guid.NewGuid().ToString();
}
protected override void BeforeUpdate()
{
this.Total = this.Net + this.Tax;
}
protected override void AfterCommit()
{
DB.Update<ProductSales>()
.Set(ProductSales.ProductName).EqualTo(this.ProductName)
.Where(ProductSales.ProductId).IsEqualTo(this.ProductId)
.Execute();
}
}
}
In response to Dan's question:
First, have a look here: http://github.com/subsonic/SubSonic-2.0/blob/master/SubSonic/ActiveRecord/ActiveRecord.cs
In this file lives the whole logic I showed in my other post.
Validate: Is called during Save(), if Validate() returns false an exception is thrown.
Get's only called if the Property ValidateWhenSaving (which is a constant so you have to recompile SubSonic to change it) is true (default)
BeforeValidate: Is called during Save() when ValidateWhenSaving is true. Does nothing by default
BeforeInsert: Is called during Save() if the record is new. Does nothing by default.
BeforeUpdate: Is called during Save() if the record is new. Does nothing by default.
AfterCommit: Is called after sucessfully inserting/updating a record. Does nothing by default.
In my Validate() example, I first let the default ValidatColumnSettings() method run, which will add errors like "Maximum String lenght exceeded for column ProductName" if product name is longer than the value defined in the database. Then I add another errorstring if ProductName is empty and return false if the overall error count is bigger than zero.
This will throw an exception during Save() so you can't store the record in the DB.
I would suggest you call Validate() yourself and if it returns false you display the elements of this.Errors at the bottom of the page (the easy way) or (more elegant) you create a Dictionary<string, string> where the key is the columnname and the value is the reason.
private Dictionary<string, string> CustomErrors = new Dictionary<string, string>
protected override bool Validate()
{
this.CustomErrors.Clear();
ValidateColumnSettings();
if (string.IsNullOrEmpty(this.ProductName))
this.CustomErrors.Add(this.Columns.ProductName, "cannot be empty");
if (this.UnitPrice < 0)
this.CustomErrors.Add(this.Columns.UnitPrice, "has to be 0 or bigger");
return this.CustomErrors.Count == 0 && Errors.Count == 0;
}
Then if Validate() returns false you can add the reason directly besides/below the right field in your webpage.
If Validate() returns true you can safely call Save() but keep in mind that Save() could throw other errors during persistance like "Dublicate Key ...";
Thanks for the response, but can you confirm this for me as im alittle confused, if your validating the column (ProductName) value within validate() or the beforevalidate() is string empty or NULL, doesnt this mean that the insert / update has already been actioned, as otherwise it wouldnt know that youve tried to insert or update a null value from the UI / aspx fields within the page to the column??
Also, within asp.net insert or updating events we use e.cancel = true to stop the insert update, if beforevalidate failes does it automatically stop the action to insert or update?
If this is the case, isnt it eaiser to add page level validation to stop the insert or update being fired in the first place.
I guess im alittle confused at the lifecyle for these methods and when they come into play
I've implemented IAlertUpdateHandler interface in a class and used it for handling creation and updating of alerts. The code is fired but it goes into endless loop by calling itself again and again.
Actually I want to suppress email notification so I'm calling a.Update(false); but this again calls PreUpdate or PostUpdate method and there is
StackOverFlowException :(
I've tried returning true/false from both the methods but nothing is helping.
According to the documentation, you're supposed to just return true or false. Try commenting out the Update call and just return either true or false. If you call Update, you'll just go into the recursive loop and your return statement will never get evaluated.
1
I know that it might be a bit late for this but I thought put it anyway to help the next one.
I found a solution/hack for this problem.
I beleive that when the alert is created from the UI the system fires an SPAlert.update(), so what I came up with is to do similar but ignore the update called from the UI to do that I added a custom property to the SPAlert property bag.
public bool PreUpdate(SPAlert a, SPWeb web, bool newAlert, string properties)
{
if (CHECK_IF_SUPPRESSING_EMAIL && !a.Properties.ContainsKey("CustomUpdate"))
{
//add a property to identify this update
a.Properties.Add("CustomUpdate", ""); //can be called anything :)
a.Update(false);
//return false to ignore the update sent by the UI
return false;
}
else
{
//no changes here proceed with custom behaviour
return true;
}
}
I have tested and it seems to do the trick hope this helps someone :)