Problem/Motivation

Variation field rendering doesn't differentiate between fields without values and fields which should not render. This can lead to situations in which a product variant may have a field w/o a value and be with another variant that has a value for the same field. The intent is for these fields to render and update on ajax, but the ajax will remove the field and never replace it if a variant has no value.

In addition to this basic problem, the ajax response commands appear to be nesting divs during the ajax. I've provided an image.

Proposed resolution

The variant field rendering needs to be aware of what fields are to be rendered in a given view mode, and which are not. For those which should be rendered, a stub div with the appropriate ajax class should ALWAYS be rendered, even in the absence of data from the variant entity. This will allow ajax responses to always replace content on the page.

I've provided a very ugly hack of \Drupal\commerce_product\ProductVariationFieldRenderer::replaceRenderedFields() to demonstrate what I mean.

  public function replaceRenderedFields(AjaxResponse $response, ProductVariationInterface $variation, $view_mode = 'default') {
    $rendered_fields = $this->renderFields($variation, $view_mode);
    foreach ($rendered_fields as $field_name => $rendered_field) {
      $element = $rendered_field;
      unset($element['#cache'], $element['#weight'], $element['#attributes'], $element['#ajax_replace_class']);
      if (!$element) {
        $rendered_field['#markup'] = '<div class="' . $rendered_field['#ajax_replace_class'] . '"></div>';
      }
      $response->addCommand(new ReplaceCommand('.' . $rendered_field['#ajax_replace_class'], $rendered_field));
    }
  }

Remaining tasks

Figure out the right way to fix this.
Fix this.

User interface changes

The UI will work more predictably.

API changes

None

Data model changes

Likely none. EntityViewDisplay should know all this stuff already.

Comments

EclipseGc created an issue. See original summary.

drugan’s picture

I also had a similar issue with Commerce Quick Purchase module. If only an attribute on the Add to cart is ajax refreshed then autocomplete field of the module is also somehow changed and don't work anymore. For now, as a quick and dirty solution I've made this:

http://cgit.drupalcode.org/commerce_quick_purchase/tree/src/QuickPurchas...

Not sure what the actual cause is but as it noticed already the DC attempts to render an empty field on ajax refresh. Which field? That is the question! :)

eclipsegc’s picture

Status: Active » Needs review
StatusFileSize
new1.32 KB

Ok, a little smarter solution. Something more like this.

Eclipse

fall_0ut’s picture

StatusFileSize
new1.91 KB

it's corrects the issue, when the first variation doesn't have the field filled

Status: Needs review » Needs work

The last submitted patch, 4: 2938760-4.patch, failed testing. View results

fall_0ut’s picture

Status: Needs work » Needs review
StatusFileSize
new1.76 KB

Fix PHP typo

Mirroar’s picture

Status: Needs review » Needs work

Thanks for the patch, it indeed fixes the vanishing fields issue. However, the duplicating divs still persist. That probably needs to be fixed wherever the variation fields are actually rendered?
Also, I'm not sure if we need to implement out own isEmpty-check, when there is helper functions like Element::isEmpty, though that one only works in special cases. Not sure if there is a better one we could use.

Mirroar’s picture

Status: Needs work » Needs review
StatusFileSize
new1.55 KB
new1.2 KB

Alright, I've looked at this a little more closely. The patch was causing PHP notices because in replaceRenderedFields it checks $content, but the variable is called $rendered_field here. Furthermore, this whole second part isn't needed (which is why the patch worked despite the mistake), because all content in replaceRenderedFields has already run though renderField, where the fix was added initially.
So I removed the offending portion and reworded some of the documentation.

I also looked into the wrapper div issue, and it seems we can't really do much about that in contrib. The offending code lies in core/misc/ajax.js where it reads:

var $newContentWrapped = $('<div></div>').html(response.data);
var $newContent = $newContentWrapped.contents();

if ($newContent.length !== 1 || $newContent.get(0).nodeType !== 1) {
  $newContent = $newContentWrapped;
}

Basically, it's checking whether the inserted content is only a single html element, else it creates a plain wrapper div. However, the output of the field template (at least when using the classy theme) contains whitespace before the actual element (and potentially twig debug comments if enabled), which means $newContent contains a whitespace text node before the actual element. So it just wraps it in a div.

goz’s picture

Thanks for the patch, this help to have something which work, even if i think we still have work to make a commit.

I make some improvements, but i still have some issues and see other cases this couldn't be enough.

1/ We still have the <div> nested issue
2/ I add a test in isEmpty() in case field has a #markup but no item. I think we should think about other cases like templates (no markup, no items), which also should display something even if the template return nothing.
3/ I add a hide class. I ok the markup is empty, but in case some css apply to this class, we should not display this (i guess)

Status: Needs review » Needs work

The last submitted patch, 9: 2938760-add_variation_field_wrapper-9.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 9: 2938760-add_variation_field_wrapper-9.patch, failed testing. View results

ndf’s picture

goz’s picture

Status: Needs work » Needs review
StatusFileSize
new1.91 KB
new821 bytes

Some tests are missing, we should be sure #prefix and #suffix exists.

bojanz’s picture

lukasss’s picture

#13 working for me

zincdesign’s picture

Will this patch work with Commerce 8.x-2.10 ?

lukasss’s picture

@zincdesign yes

zincdesign’s picture

That's great thanks. #13 Working for me.

bojanz’s picture

Status: Needs review » Needs work

The div nesting issue was fixed by Drupal 8.6.0, which we require.

Looking into this issue I found problems with performance and handling fields that are hidden on the entity display, fixed in #3010225: Optimize ProductVariationFieldRenderer performance. This patch now needs a reroll.

Implementation notes:
- We should be able to use Element::children() to check if the render array is empty.
- Setting #theme/#type to container should be an easy way to ensure a div.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new5.23 KB

Updated patch.

1) Still missing the main test coverage (ensuring that switching the variation renders the field). No good place to add it to right now, it seems.

2) There's some code duplication between renderFields() and renderField()

3) An empty div has a height, so it changes the way the page looks. We could explode either using a span, or adding an additional class that can be targetted with CSS.

czigor’s picture

Status: Needs review » Needs work

The last submitted patch, 21: commerce-2938760-empty_variation_fields-21.patch, failed testing. View results

czigor’s picture

I think this redirect test tends to fail randomly.

mglaman’s picture

Status: Needs work » Needs review
bojanz’s picture

StatusFileSize
new12.35 KB

This should be the final patch.

Renamed and cleaned up the functional test, since it's now doing more than testing the view mode.
Eliminated the code duplication in ProductVariationFieldRenderer.
Decided to keep the container as-is (which means keeping its height) for now. It does prevent a sliding effect when a new value appears. People can target the div with :empty in CSS.

  • bojanz committed 79229cd on 8.x-2.x
    Issue #2938760 by GoZ, bojanz, fall_0ut, czigor, EclipseGc, Mirroar:...
bojanz’s picture

Status: Needs review » Fixed

Thanks, everyone!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.