Symfony2 - Saving child and parent from the same form - symfony

I'm new to Symfony2 and i've been struggling with an issue handling a form submission. I can't help feeling i'm missing something stupid, but after googling and not findind any solutions I thought i'd post my problem here.
So, I have a form to create a child entity and edit a couple fields on a parent entity. To build the form i've used the FormBuilder, where i added two form types (one for the child and one for the parent). The parent type includes the ID field for the parent.
On the action that handles the form submission i'm calling handleRequest which fails because:
Neither the property "id" nor one of the methods "setId()", "_set()" or "_call()" exist and have public access in class "Ahms\MyBundle\Entity\Parent".
This happens when the parent id field is part of the form, when it's not part of the form this runs smoothly.
What am I missing?
Thanks!

If you create the Form from child Controller, you have to declare id in child class and have to writ public function setId($id) and public function getId() method.

Related

Render a different view from the same action in Symfony2

I am developing a small system using Symfony2, and I have reached a situation that I need to duplicate the same editAction, but only change the view it renders.
I use this action to edit basic information of all the registered budgets that is listed in a page. I have a special page listing all the inactive budgets, and I want a different editing page to change some statuses and add dates.
How can I use the same editAction to render different views depending on the URLs? The page that lists all the budgets is /budgets and the inactive ones is /budgets/inactive.
Use something like:
/**
* #Route("/budgets/{active}", defaults={"active" = "active"})
*/
public function editAction($active)
{
// ...
}
Then, when you go to /budgets $active will be "active" and when you go to /budgets/inactive $active will be "inactive". You can then use this variable to decide which template to show
More info here:
http://symfony.com/doc/current/book/routing.html#required-and-optional-placeholders
Nice answer by Carlos, thanks, but an edit action requires also validation, and after that the same page with errors or some kind of action on a valid form. So i would say that even a simple form is a bit complex. When you also make a "two views" action it gets 2 * 2 = 4 times more complex. besides of that the $active variable could have any value and not only "active" or as you want "inactive". It could be "joke", "stupid" and a lot more possibilities. My suggestion would be to add a second action and to use private functions for functionality that fits for both forms OR to use a custum class (or service) that handles the forms.

Validation while rendering multiple partial views

I have multiple views/models that are being looped into the main view. I load them via #Html.Partial()...These views/models are basically form elements with certain properties...Unfortunately I soon found out that only the first of each type of view/model is validated. I tried moving the fields around and only the first of each kind would validate.
My partial views look something like this:
#Html.DropDownListFor(model=>model.dropdownVal,Model.SelectItems,new { id=Model.FieldID, Name = Model.FieldID })
I looked at the HTML rendered, and it seems that the validation tags like "data-val" are not applied...
Any ideas would be greatly appreciated!
Add the following at the top of your partials to trick ASP.NET MVC into thinking that the helpers are used inside a form and generate the proper data-val attributes:
#{
this.ViewContext.FormContext = new FormContext();
}
Basically the Html.* helpers are generating data-val clients side validation attributes only if they are placed inside an Html.BeginForm. Except that in your case I guess that this Html.BeginForm is in your parent view and not inside the partial, so the #Html.DropDownListFor doesn't emit any validation attributes. By setting the current FormContext to a new instance in the partial as shown previously, the helper will generate the proper client side validation attributes on the corresponding input field.

Entity reference field and dependent dropdown

I have a content type with an entity reference field referencing to a custom entity. I need to use a select box because an autocomplete widget is not suitable in my case. However, I cannot load all the entities at once as selectable values because they are too many (72000+ the form won't even load). So I default the entity reference select box to a limited number of values using a views filter and then hide it by default. Then I use an ajax dependent dropdown to show and populate the entity reference select box with filtered down values (I'm using a module that implements hook_form_alter).
My problem is that the form won't validate because now I can select entity reference values which are not the default ones in the select box. So I guess I should control in some way the validation rules of the entity reference field. Is there an easy way to do this? Which hook should I use?
Set the entity reference field to autocomplete and take it out of the process entirely in your form alter with $form['field_entity_ref']['#access'] = FALSE. This should fix the validation problem. (of course, "field_entity_ref" is what I'm calling your actual reference field.
Add your own validation to the form, if that is still necessary.
Finally, implement hook_node_presave() to manually put the value of your custom ajax drop down box.
So if your custom ajax select box was named my_custom_ref, then it would look something like this:
function mymodule_node_presave($node) {
if (isset($node->my_custom_ref)) {
$node->field_entity_ref[$node->language][0]['target_id'] = $node->my_custom_ref;
}
}

JSF 2.0 dynamically controlling which values get posted back based on CSS

I have this form with ~170 individual text boxes with values in a session scoped bean. There is a requirement to only submit values when the component has a certain CSS class.
The way I originally approached this was to create a PhaseListener at the UPDATE_MODEL_VALUES and test the CSS class there. If the class was the affected class I set the value of the component to null. Then on the front end, I switched the class on focus using a generic JavaScript method. This meant in terms of changes to each componenet I only needed to add:
... styleClass="examfieldgrey" onfocus="whiteField(this);"
which is kind of nice given how many components I need to change.
This was working fine until I re-factored my e form to use multiple h form tags. Now the CSSclass is switching on the front end, but this change is not being saved. The phase listener is getting the old class.
I'm thinking this is obviously related to me switching the class in jQuery/javascript. What I am wondering is:
Is there a better way to do this arachatectually? One that preferably means I don't have to modify 170+ componenets?
If I do have to continue with using Javascript to switch the class, is there a way I can post that change back from javascript?
Sorry if this is an obvious question, I'm still a little green with the JSF lifecycle.
I'm using JSF 2.0 MyFaces
For reference here is an example of a component on my form that needs to be filtered:
<h:inputTextarea
id="inputVal"
styleClass="midTextArea examfieldgrey"
onfocus="whiteField(this);"
value="#{bean.form.val}"/>
where "examfieldgrey" is the class I test for when determining if I'm going to block a component.
And the whiteField method:
function whiteField(field){
if(! jQuery(field).hasClass("examfieldgrey")){
return;
}
jQuery(field).removeClass("examfieldgrey");
jQuery(field).addClass("examfieldwhite");
}
And my phase listener before phase method where I filter:
// TODO: make whatever mode allows ghosting to be configurable outside of
// the system (perhaps in the config file)
/**
* Before the model is updated, test each component's CSS on the form. If the
* CSS style is 'examfieldgrey' set the value to null so it doesn't get submitted
*/
#Override
public void beforePhase(PhaseEvent arg0) {
//We need the session to get the backing bean
if (arg0.getFacesContext().getExternalContext().getSessionMap() == null) {
return;
}
//get the measurements bean so we can determine the form mode
if (arg0.getFacesContext().getExternalContext().getSessionMap()
.get("measurements") == null) {
return;
}
//ensure the bean is the expected data type, it should always be this type. I'm just paranoid ;)
if (!(arg0.getFacesContext().getExternalContext().getSessionMap()
.get("measurements") instanceof MeasurementsController)) {
return;
}
//get, convert and check the backing bean's mode. We only filter if the mode is COPY
if (((MeasurementsController) arg0.getFacesContext()
.getExternalContext().getSessionMap().get("measurements"))
.getMode() != FormMode.COPY) {
return;
}
//recursivly traverse the componenets and filter the ones who have the CSS class
traverseChildren(arg0.getFacesContext().getViewRoot().getChildren());
}
/**
* Traverse a List of UIComponenets and check the CSS. If it's the 'examfieldgrey' class
* and the component is a UIInput component, set the value to null.
* #param children a List of the componenets to filter on the form.
*/
private void traverseChildren(List<UIComponent> children) {
debugLevelCount++;
if (children == null || children.size() == 0) {
debugLevelCount--;
return;
}
for (UIComponent component : children) {
if (component instanceof UIInput) {
if (component.getAttributes() != null
&& component.getAttributes().get("styleClass") != null
&& component.getAttributes().get("styleClass")
.toString().contains("examfieldgrey")) {
((UIInput) component).setValue(null);
} else {
debugPrintAllow(component);
}
continue;
}
traverseChildren(component.getChildren());
}
debugLevelCount--;
}
Ignore the print functions, they don't do anything ;)
Thanks guys!
Edit
This is a copy operation so the backing bean has values in it after construction of the bean. The option of using the primefaces selector is great if I hit submit and the backing bean is not already populated. But I'm not sure if it will be able to actually clear out those values.
One other thing to note is that I am referencing values inside an instance of my form object. I don't know if that helps but it wasn't present in my original post.
There is a requirement to only submit values when the component has a certain CSS class.
If you happen to use PrimeFaces already or are open to use it, since the latest 3.3 version you can use the new #() selector syntax which accepts jQuery based CSS selectors in process and update attributes of PrimeFaces ajax components (which are equivalent to execute and render attributes of JSF standard <f:ajax> component).
For example
<p:commandButton ... process="#(.foo)" />
or
<p:ajax ... process="#(.foo)" />
will instruct JSF to process only the HTML input elements having classname of foo.
Now the CSSclass is switching on the front end, but this change is not being saved. The phase listener is getting the old class.
That's because you didn't keep JSF component tree in the server side in sync with the HTML DOM tree in the client side. You're making changes in the client side only without notifying JSF about this. CSS classes are not been sent as a request parameter to the server side, only the HTML form input values are. You basically need to change the CSS classes by JSF instead of by JS/jQuery so that the change is also reflected in the JSF component tree.
Implementing this is however not exactly trivial and potentially wasteful. Easiest is thus to use PrimeFaces with its #() selector support. This selector is evaluated in the client side and converted to a string of JSF-understandable component client IDs matching the selector. This thus takes client side changes fully into account.
I was able to get this one solved by creating a map of boolean values for each field on the form with string keys that are the ids of the fields. Each value represented weather or not to copy the field. I update this value using ajax on blur. And I set the CSS class to be based on the boolean value in the map for that field.
Rendering didn't work out so well. Originally I was doing this all on focus but it quickly became apparent that attempting to rendering a textbox on focus would actually lose focus to the textbox. So, on focus I just call a quick js function to switch the class as I had been doing originally.
Since the css class is chosen based on the map on the front end, it gets updated before the phase listener is called and the components get filtered properly.
Thanks for the help BalusC!

How to create "Available field" properties in my own property?

I have created a class inherit from StateManagedCollection.
It has got a few class as Columns like GridView.
But I can not select which filed I want to select from.
It should look like the picture below in design.
But mine is the one below:
I have written the property as below:
[Description("A collection of ToolBarItem's ")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)), PersistenceMode(PersistenceMode.InnerProperty)]
public virtual Items Items
{
}
Can anyone help me out?
GridView columns collection uses a custom UI Type editor to show this interface. The in-built ASP.NET CollectionEditor will not show the required UI. Further in your case, CollectionEditor may not work if the collection's item type is a abstract class.
Solution is to build your own custom UI Type editor - basic steps are
Inherit from System.Drawing.Design.UITypeEditor.
Override GetEditStyle method to inform the property browser that you will launch modal form.
Override the EditValue method to launch your custom UI form.
Build the custom UI Form.
See a couple examples here (see sample for TagTypeEditor) and here.

Resources