Open sudomabider opened 9 years ago
@sudomabider I have just spent a couple of hours debugging the exact same issue and reached the same conclusions as you. I really wish I had checked the list of open issues first!
As you have already stated, the logic to store the Producer ID field against the Product (and nothing else, hence issue 1) is in PickerFieldAddExistingSearchHandler::add(), the last stage of which is to write/save the Product which, as you say, if the Product has not been saved before will create a new record in the DB.
There is then some logic executed somewhere which triggers an AJAX based refresh of the CMS's main content frame. I cannot work out where this is triggered, perhaps deep down in the core code of GridField?
Regardless what it seems to do is request a fresh version of the content returned by the current URL (i.e./admin/products/Product/EditForm/field/Product/item/new - hence issue 2).
So to expand the 2 issues a little further:
Issue 1: As the PickerFieldAddExistingSearchHandler::add() method only updates the Product->ProducerID property, and nothing else, before saving and refershing, any other data you have entered to the Product edit form will be lost. I would expect this to also be an issue when updating the Producer on previously saved Products, but somehow that seems not to be the case? I can't work out why.
Potential Solution 1: Pass the values in all other fields of the edit form to the PickerFieldAddExistingSearchHandler::add() method (in the POSTed form data) and also update all the corresponding properties of Product before calling write() in the add() method
Issue 2: The automatically triggered AJAX refresh of the CMS main content area seems to be using the current URL, which makes sense in other scenarios, but in the scenario where the Product object has yet to be written before it should refresh that content based on the newly created Product objects edit form (i.e. /admin/products/Product/EditForm/field/Product/item/27/edit and not ./admin/products/Product/EditForm/field/Product/item/new)
Potential Solution 2: Not sure how to implement it, but ensure that the URL called by the AJAX refresh is that of the edit item form not the new item form
@briceburg any thoughts?
@HARVS1789UK Correct me if I'm wrong but I think only the current GridField is refreshed via AJAX not the whole content area unless you do a create/save, therefore unaffecting your values in the other fields. The code that triggers the gridfield AJAX refresh is in GridFieldExntensions.js from, well, GridFieldExtensions...
Your suggested solutions sound like a good idea. I tried playing with them for a while but it soon became quite messy plus I also had to touch the core code. Will have to spend some more time later digging deeper. For now I'm just using javascript to always submit the empty form straight away when creating anything that contains a HasOnePickerField. So far it's working well. Dirty hack I know...but gets the job done.
@sudomabider I think you right about the current GridField being the only thing that is refreshed via AJAX in the scenario where you have saved the Product already (where it is working) but I think for me at least I think I am getting a whole new edit form when trying to select a Producer before having saved the Product.
I will try and do a screencast of it shortly to show the issue I am having. I am not sure how to implement my suggestions without editing core code either (or at least this modules 'core' code) which is something I also try to avoid.
Regarding your current hack/solution, do you mean you automatically trigger an initial save of the Product at the very start of the creation process? This was something I had also considered, do you have any examples of how you have done it?
@HARVS1789UK Yes we're seeing the same thing. Sorry about the confusion. I was just trying to explain the bit at the end of your issue 1:
I would expect this to also be an issue when updating the Producer on previously saved Products, but somehow that seems not to be the case?
The hack I've done is pretty simple at this stage. I put this in the GridFieldExtensions.js.
//if reached by ajax
$(document).ajaxComplete(function(event, xhr, settings){
if (/\/new$/.test(settings.url) && $(xhr.responseText).find('.hasonepicker').length){
$('button[type="submit"][name="action_doSave"]').click();
}
})
//if reached by page load
$(document).ready(function(){
if (/\/new$/.test(window.location.href) && $('.hasonepicker').length) {
$('button[type="submit"][name="action_doSave"]').click();
}
})
Not the best js code... but I'm fine with it for now.
@sudomabider @HARVS1789UK apologies to you on the radio delay! I've just merged a PR that may aid in resolving this.
The ability to create and edit managed items is expected. I'll ensure compatibility w/ latest SS version ASAP.
Hi @briceburg,
Im afraid your recently accepted pull request has not resolved this issue. I have done a bit more investigating based on @sudomabider 's comments about this behaviour being triggered by GridFieldExtensions.js and I have worked out that the content area reload is being triggered by line 58 of GridfieldExtensions.js dialog.data("grid").reload();
$(".add-existing-search-dialog .add-existing-search-items a").entwine({
onclick: function() {
var link = this.closest(".add-existing-search-items").data("add-link");
var id = this.data("id");
var dialog = this.closest(".add-existing-search-dialog")
.addClass("loading")
.children(".ui-dialog-content")
.empty()
$.post(link, { id: id }, function() {
dialog.data("grid").reload();
dialog.dialog("close");
});
return false;
}
});
dialog is a jQuery object holding the dialog div e.g.
<div class="ui-dialog-content ui-widget-content" scrolltop="0" scrollleft="0" style="width: auto; min-height: 0px; height: 582px;">
dialog.data("grid") seems to be a jQuery object holding the fieldset which contains the entire HasOnePickerField e.g.
<fieldset class="hasonepicker ss-gridfield field" id="Form_ItemEditForm_CustomerID" data-url="admin/orders/Order/EditForm/field/Order/item/new/ItemEditForm/field/CustomerID" data-name="CustomerID"><div class="ss-gridfield-buttonrow ss-gridfield-buttonrow-before">
I am unfamiliar with the JS method .reload()
except for when used on the window.location object, so perhaps this is an entwine based method? Regardless it seems to be getting the current page URL and reloading it.
In our scenario we don't want that to happen, we want the URL referencing the edit form of the newly created DataObject to be loaded into the current content area. I have tried adding my own entwine based onclick listener for these 'add existing search item' links but I cannot get it to be called at all.
Any ideas? Everything else about this module is working well for me, but this really is a show stopper.
@sudomabider - I have tried your JS solution and it works (after I modified the submit button selectors slightly as I am using GridFieldBetterButtons) however, it is not a solution for me as in my context there will be many scenarios where a new edit form will be opened, but aborted (I am dealing with orders in a takeaway) so auto-saving a new object on every load of this page is not really a workable solution for me.
We've had two PRs merged -- willing to accept a fix if we can do one cleanly.
@briceburg I do not have enough JS and Entwine experience to fix this elegantly and as part of the issue seems to be caused in the GridFieldExtensions modules GridFieldExtensions.js file, I am not sure if that is even possible using the code of your module alone?
I have spent another 4 hours or so attempting to find a fix, sadly all I have come up with is the below (horrible) hack.
1) Edit PickerFieldAddExistingSearchHandler::add() to contain the following:
public function add($request) {
// use native GridFieldAddExistingSearchHandler add() method when not has_one
if(!$this->grid->isHaveOne()) { return parent::add($request); }
if(!$id = $request->postVar('id')) {
$this->httpError(400);
}
// appropriate handling of has_one relationships
$childProperty = $this->grid->getName();
$this->grid->childObject->$childProperty = $id;
$this->grid->childObject->write();
$url = $request->getHeader('Referer');
if(!empty($url) && preg_match("/\/new(\/)?$/i", $url)) {
return json_encode(array(
/*
* Replace /item/new portion of EditForm URL with <ObjectID>/edit
* and pass the updated URL back to the client-side $.post() success
* method (modified to update the history state and refresh the page
* on GridFieldExtensions.js lines 57 - 67 to fix bug in HasOnePicker
* field when linking an existing child object to a new, not yet saved,
* parent object)
*/
"Link" => preg_replace("/\/new(\/)?$/i", "/" . $this->grid->childObject->ID . "/edit", $url)
));
}
}
In order to pass the URL of the EditForm for the newly create parent object back to the front end.
2) Modify lines 57 - 67 of theGridFieldExtensions modules GridFieldExtensions.js file to process this returned URL and use it to update the History object and issue a hard reload of the page if a URL is provided (and continue as normal if not):
$.post(link, { id: id }, function(data, status, xhr) {
if(data && data.Link) {
history.pushState({}, "", data.Link);
window.location.reload();
} else {
dialog.data("grid").reload();
dialog.dialog("close");
}
}, "json");
I am by no means pleased with this solution, but (at least temporarily for me) it is a solution.
I can not afford to spend any more time on this issue and due to the poor quality of my fix, I am looking for a different module/approach which can satisfy the needs of my project without this horrible hack. If you do manage to find a better solution in the future please let me know as with the exception of this issue I have been very happy with your module.
Cheers,
HARVS1789UK
Hi guys,
I am new to this module. When I tried to Add new -> Save and close, it doesn't work. However, Save and go back does work. Is that the same issue you're having?
Thanks
@namnn96 I'm not sure here -- could you provide any console output?
Hi @briceburg ,
Nothing on the console, the network seems to be normal with a POST newitem request, followed by a redirect to the parent page, but the newly created item is not linked, though it was successfully created.
@namnn - Yes that is the same issue I was having, the hack in my previous thread response was how I fixed it for now. I couldn't spend any more of my project time trying to find a more elegant solution
@HARVS1789UK ,
Actually I have no problem with add existing, I just have issue when adding new item.
It goes to a newItemForm, Save button works, but not Save and close, which supposes to work as usual.
Update: I found a way to get over it. A bit extension done to the PickerFieldEditHandler for the doSaveAndQuit function
Hi @namnn96,
would you be able to share the bit of the code you have to work around this issue? Thanks in advance!
Hi @michalkleiner,
It's been a long time and i am not sure if this is the one. This extends GridFieldBetterButtonsItemRequest
public function doSaveAndQuit($data, $form) { $this->save($data, $form); Controller::curr()->getResponse()->addHeader("X-Pjax","Content"); return $this->saveAndRedirect($data, $form, $this->getBackLink()); }
Issue description:
For simplicity let's say the relationship is Product has_one Producer. Both are subclasses of DataObject. Within the Product CMSfields is a HasOnePickerField for Producer which goes:
When editing an existing Product everything works fine. When adding a new Product however, after clicking on a Producer from the popup list, the animation fires and then the page is unchanged and unaware of the selection. At the same time, a new Product entry is created in the database with the just selected Producer and every other attribute blank.
Suspected Reason
When the Producer is clicked on from the popup list, an add action is called on the controller (PickerFieldAddExistingSearchHandler?). This action performs a write to db in order to establish the link between the current Product and the selected Producer so the selection can be reflected on the page. But which Product does it write to? It creates a new entry and writes to it.
Now I'm not sure if this is something in your code or in mine or silverstripe's own. It's a relatively clean install so I'd like to think it's not my code. But I have a similar issue with RelationEditor (autocomplete doesn't work) when creating new entries and it works fine when editing.
Any advise?
Thanks, Veo