adobe / aem-spa-page-model-manager

Interface between Adobe Experience Manager and Single Page Application framework.
Apache License 2.0
35 stars 25 forks source link

[bug] After dialog update Boolean false being sent as empty string #79

Open ChazUK opened 2 years ago

ChazUK commented 2 years ago

Describe the bug I've had an issue when trying to process boolean values when updating components in AEM. When switching boolean values in the dialog, trying to set it to false does not send false but instead sends an empty string ''. The dialog sets the values to be boolean, the sling model sets it to boolean, and the .model.json also shows the update as a boolean, but the angular component receives the string.

Update in Network bannerhero.model.json?=1645206630867 {"title":"hello world","showPattern":false,":type":"eds-aem-app/components/banner-hero"}

But the console logs show pattern true {value: ''}

I would expect that the angular component is sent false instead of an empty string.

I've added some code below to try and replicate, I've got no idea how to package stuff up.

Package version @adobe/aem-angular-editable-components@1.4.0 @adobe/aem-spa-component-mapping@1.1.1 @adobe/aem-spa-page-model-manager@1.4.0 @angular/*@13.2.0

To Reproduce

package com.business.europe.core.models;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.Getter;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(
  adaptables = { SlingHttpServletRequest.class },
  adapters = { ComponentExporter.class },
  resourceType = BannerHeroModel.RESOURCE_TYPE,
  defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
  name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
  extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
@JsonInclude(Include.NON_EMPTY)
@Getter
public class BannerHeroModel implements ComponentExporter {

  protected static final String RESOURCE_TYPE =
    "eds-aem-app/components/banner-hero";

  public String getExportedType() {
    return RESOURCE_TYPE;
  }

  @ValueMapValue
  private Boolean title;

  @ValueMapValue
  private Boolean showPattern;
}

Dialog

<?xml version="1.0" encoding="UTF-8" ?>
<jcr:root
  xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
  xmlns:cq="http://www.day.com/jcr/cq/1.0"
  xmlns:jcr="http://www.jcp.org/jcr/1.0"
  xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
  xmlns:granite="http://www.adobe.com/jcr/granite/1.0"
  jcr:primaryType="nt:unstructured"
  jcr:title="Hero"
  sling:resourceType="cq/gui/components/authoring/dialog"
>
  <content
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/container"
  >
    <items jcr:primaryType="nt:unstructured">
      <tabs
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/tabs"
        maximized="{Boolean}true"
      >
        <items jcr:primaryType="nt:unstructured">
          <content
            jcr:primaryType="nt:unstructured"
            jcr:title="Content"
            sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
            margin="{Boolean}true"
          >
            <items jcr:primaryType="nt:unstructured">
              <column
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/container"
              >
                <items jcr:primaryType="nt:unstructured">
                  <title
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                    fieldLabel="Title"
                    name="./title"
                    required="{Boolean}true"
                  />
                  <showPattern
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
                    text="Display Pattern?"
                    name="./showPattern"
                    checked="{Boolean}true"
                    value="{Boolean}true"
                    uncheckedValue="{Boolean}false"
                  />
                </items>
              </column>
            </items>
          </content>
        </items>
      </tabs>
    </items>
  </content>
</jcr:root>
import { MapTo } from '@adobe/aem-angular-editable-components';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { Component, Input } from '@angular/core';

@Component({
  selector: 'eds-base-banner-hero',
  template: `Show Pattern: {{ showPattern }}`,
})
export class BannerHeroComponent {
  @Input() title?: string;
  @Input()
  set showPattern(value: BooleanInput) {
    this._showPattern = coerceBooleanProperty(value);
    console.log('showPattern', this._showPattern, {value});
  }
  get showPattern(): boolean {
    return this._showPattern;
  }

  private _showPattern!: boolean;
}

MapTo('eds-aem-app/components/banner-hero')(BannerHeroComponent, {
  emptyLabel: 'Hero',
  isEmpty: (cqModel: any) => !cqModel || !cqModel.title,
});
orangeven commented 2 years ago

I can confirm also the problem. To demonstrate the problem I created a branch with 2 failing unit tests: https://github.com/adobe/aem-spa-page-model-manager/compare/master...orangeven:aem-spa-page-model-manager:boolean-value-becomes-empty-space-after-setData?expand=1

the responsible line is https://github.com/adobe/aem-spa-page-model-manager/blob/master/src/ModelStore.ts#L97

orangeven commented 2 years ago

it is a side effect of changes for https://github.com/adobe/aem-angular-editable-components/issues/20