0

I am trying to use sitecore forms with Jss angular app, I find this on github and trying to use it in my app https://github.com/bic742/sitecore-jss-angular-forms

the form is rendered successfull. but when I click submit button, i got this error. enter image description here

I checked the network tab and I find that all parameters passed successfully. enter image description here

this is the code of from.ts

import { FormBuilder, FormGroup } from "@angular/forms";
import {
  serializeForm,
  submitForm,
  SitecoreForm,
  FormFetcher,
  TrackerFetcher,
  ValueFormField,
  FormTracker,
} from '@sitecore-jss/sitecore-jss-forms';
import { FieldTypes } from '../forms/FieldTypes';
import { ComponentRendering } from '@sitecore-jss/sitecore-jss-angular';
import { DynamicFieldDirective } from '../forms/directives/dynamic-field/dynamic-field.directive';
import { environment } from '../../../environments/environment';
import { FieldState, FormStateService, FieldStateCollection } from '../forms/services/form-state.service';

export interface ErrorComponentProps {
  form: SitecoreForm;
  formErrors: string[];
  fieldErrors: Array<{ fieldName: string, state: FieldState }>;
}

export interface FormProps {
  form: SitecoreForm;
  sitecoreApiHost: string;
  sitecoreApiKey: string;
  onRedirect?: (url: string) => void;
  
  /** Fetch function used when submitting the form (defaults to using `fetch`) */
  formFetcher?: FormFetcher;

  /** Fetch function used when posting form field tracking data (defaults to using `fetch`) */
  trackerFetcher?: TrackerFetcher;
}

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.scss']
})

export class FormComponent implements FormProps {
  @ViewChild(DynamicFieldDirective, { static: true}) field: DynamicFieldDirective;
  @Input() public rendering: ComponentRendering;

  private _tracker: FormTracker;
  public trackerFetcher: TrackerFetcher;
  public formFetcher: FormFetcher;

  public formGroup: FormGroup;

  get value() {
    return this.formGroup.value;
  }

  public form: SitecoreForm;
  public formErrors: string[];
  
  public sitecoreApiHost = environment.sitecoreApiHost;
  public sitecoreApiKey = environment.sitecoreApiKey;
  public action: string;

  public constructor(private formBuilder: FormBuilder, private formState: FormStateService) { 
    const state = {
      errors: [],
      // in a multistep form the server can reset the form schema
      // to display further steps; this state property overrides
      // the form passed in from props if present
      nextForm: null,
      submitButton: null
    };

    this.formState.setState(state);

    this._tracker = new FormTracker({
      endpoint: `${this.sitecoreApiHost}/api/jss/fieldtracking/register?sc_apikey=${this.sitecoreApiKey}`,
      fetcher: this.trackerFetcher,
    });
  }

  public ngOnInit(): void { 
    this.form = this.formState.state.nextForm || this.rendering.fields as unknown as SitecoreForm;
    this.formGroup = this.createControl();

    this._tracker.setFormData(this.form.formItemId.value, this.form.formSessionId.value, this.form.metadata.isTrackingEnabled);

    this.action = `${this.sitecoreApiHost}/api/jss/formbuilder?fxb.FormItemId=${this.form.metadata.itemId}&fxb.HtmlPrefix=${this.form.htmlPrefix}&sc_apikey=${this.sitecoreApiKey}`;
  }

  createControl() {
    const group = this.formBuilder.group({});
    this.form.fields.forEach(field => {
      if (field.model.fieldTypeItemId === FieldTypes.Button) return;

      const castField = field as ValueFormField;

      if (castField.valueField) {
        const control = this.formBuilder.control(castField.valueField.name);
        group.addControl(castField.valueField.name, control);
      }
    });

    return group;
  }

  collectCurrentFieldValues() {
    return Object.keys(this.formState.state)
      .filter((fieldName) => this.formState.state[fieldName] && typeof this.formState.state[fieldName].isValid !== 'undefined')
      .map((fieldName) => ({ fieldName: fieldName, state: this.formState.state[fieldName] as FieldState }));
  }

  /**
   * Handler triggered when the form is submitted. May transition its state between
   * steps in a multistep form or handle a final submit.
   */
  onSubmit(e) {
    //console.log("onSubmit",e);
    e.preventDefault();

    const form = this.formState.state.nextForm || this.form;

    const fieldValues: { [key: string]: string | string[] | boolean } = {};

    const currentFieldValues = this.collectCurrentFieldValues();

    currentFieldValues.forEach(field => {
      if (typeof field.state.value !== 'undefined') {
        fieldValues[field.fieldName] = field.state.value;
      }
    });

    // NOTE: we're not pre-validating the submit on the client because
    // Sitecore won't be able to track validation errors in xConnect

    // serialize the form data that we got from the server
    // (hidden fields with constant values, unchanged default field values, etc)
    const formData = serializeForm(form, { submitButtonName: this.formState.state.submitButton });
    console.log("formData",formData);
    // merge in user-updated field values
    formData.mergeOverwritingExisting(fieldValues);

    const submitUrl = (e.target as HTMLFormElement).action;
    console.log("submitUrl",submitUrl);
    console.log("formDataAftermerge",formData);
    if (!submitUrl) {
      throw new Error('Submit URL was not defined. Ensure the form has an action attribute.');
    }

    submitForm(formData, submitUrl)
      .then((result) => {
        this.form = result.nextForm;

        if (result.success && result.redirectUrl) {
          // Process redirect-on-success action.
          // if (this.props.onRedirect) {
          //   this.props.onRedirect(result.redirectUrl);
          // } else {
            window.location.href = result.redirectUrl;
          //}
        }

        if (result.validationErrors) {
          this.formErrors = [];
          Object.keys(result.validationErrors).forEach((fieldKey) => {
            this.formErrors = this.formErrors.concat(result.validationErrors[fieldKey]);
          });

          const stateUpdate: FieldStateCollection = {};

          Object.keys(result.validationErrors).forEach((fieldKey) => {
            stateUpdate[fieldKey] = {
              value: (this.formState.state[fieldKey] || {}).value,
              isValid: false,
              errors: result.validationErrors[fieldKey],
            };
          });

          this.formState.setState(stateUpdate);
        }

        if (result.nextForm) {
          this.formState.setState({ nextForm: result.nextForm });
        }

        if (result.success) {
          this.formState.resetFieldsState();
        }

        if (result.errors && result.errors.length > 0) {
          throw result.errors;
        }

        this.formState.setState({ errors: [] });
      })
      .catch((error: Error | string[] | string) => {
        console.log('Form submit error', error);

        if (Array.isArray(error)) {
          this.formState.setState({ errors: error });
        } else if (typeof error === 'string') {
          console.log('Form submit error', error);
          this.formState.setState({ errors: [error] });
        } else {
          console.log('Form submit error', error);
          this.formState.setState({ errors: [error.message] });
        }
      });
  }
}

any one have an idea about this?

1
  • Any updates for the above issue. I am also facing the same issue while using the angular jss form.
    – vijayan M
    Nov 10, 2021 at 6:51

1 Answer 1

0

It's been a while (time, Sitecore versions, ...) since this has been posted. Did you manage to solve this?

I faced a similar (same?) issue with a NextJS / React implementation (Sitecore 10.2 - Sitecore Headless 20.0.3). My Sitecore logs showed following details:

1000 09:39:31 ERROR Application error.
Exception: System.NullReferenceException
Message: Object reference not set to an instance of an object.
Source: Sitecore.JavaScriptServices.Forms
   at Sitecore.JavaScriptServices.Forms.Controllers.JsonFormBuilderController.GetNextForm(String id, String contextItemId, NavigationType navigationType, String[] submitErrors)
   at Sitecore.JavaScriptServices.Forms.Controllers.JsonFormBuilderController.GetSubmitActionResult(FormSubmitContext submitContext, String contextItemId)

The issue here is that Sitecore is missing the ItemId.

The react sample implementation (https://github.com/Sitecore/jss/blob/39f4267e8cd3c942910029d2e186058c1ab105bc/packages/sitecore-jss-react-forms/src/components/form.tsx#L99) passes this a query string parameter

const action = `${this.props.sitecoreApiHost}/api/jss/formbuilder?...sc_itemid=${form.contextItemId}`;

but this doesn't work for my setup (assuming SC bug, to be investigated): Sitecore item resolver kicks in, preventing the call to reach the api/jss/formbuilder route.

Long story short: I managed to solve this by including sc_itemid in the FormData payload (adding it to formdata right before fetching, but you could render it as a hidden field as well I guess).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.