1

I would like to enable strictTemplates in angular but when I do I am getting some of the following type errors.

By default most of our rendering variables are using ComponentRendering Type from JSS, but see the errors I am getting. How would I resolve these?

For example in one of my html template... Given that rendering variable is of type:

rendering: ComponentRendering;

Then in my page(html template) I have this:

<img class="imgSize" 
  *scImage="rendering.fields.BackImage" 
  [alt]="this.rendering.fields.BackImage.value.alt" 
/>

<span *scGenericLink="rendering.fields.BackLink" class="back-link"></span>

I am getting the following errors: For *scImage: Type 'Field | Item | Item[]' is not assignable to type '"" | ImageField'

For [alt]: Property 'value' does not exist on type 'Field | Item | Item[]'. Property 'value' does not exist on type 'Item'.

For *scGenericLink: Type 'Field | Item | Item[]' is not assignable to type 'LinkField'. Type 'Field' is not assignable to type 'LinkField'. Types of property 'value' are incompatible. Type 'GenericFieldValue' is not assignable to type 'LinkFieldValue'. Type 'string' is not assignable to type 'LinkFieldValue'

::::PLEASE NOTE::::::: I have tried messing around with the typings file in jss to no avail. Example: in node_modules/@sitecore-jss/layout.models.d.ts:

export interface ComponentFields {
    [name: string]: Field | Item | Item[] | any; //adding any here makes all issues go away...
}

But I know this is not correct practice to alter the node_modules contents. So my question is the implementation incorrect by using rendering: ComponentRendering for everything? Or how can I add custom types to get around these issues? Like maybe extend the type in my own d.ts file etc. Thanks in advance!

2
  • Can you try something like *scImage="rendering.fields.BackImage as ImageField" *scGenericLink="rendering.fields.BackLink as LinkField" Nov 9, 2023 at 12:46
  • Unfortunately you cannot do "as SomeType" in the HTML files :( Nov 9, 2023 at 14:51

1 Answer 1

1
+100

Create Specific Components

In this case, it seems reasonable to create specific components that only deal with image / link / multilist fields. This is a common pattern in the builds that I have seen (Vue, Next, and Angular).

As was mentioned in the comments of the original question, type assertions aren't possible in Angular templates, but you can handle these issues in your component TS code instead of the template:

import { Component, Input } from '@angular/core';
import { ComponentRendering, ImageField, LinkField } from '@sitecore-jss/sitecore-jss-angular';

export class MyComponent {
  @Input() rendering: ComponentRendering;

  get backImage(): ImageField | undefined {
    const field = this.rendering.fields.BackImage;
    if (this.isImageField(field)) {
      return field;
    }
    return undefined;
  }

  get backLink(): LinkField | undefined {
    const field = this.rendering.fields.BackLink;
    if (this.isLinkField(field)) {
      return field;
    }
    return undefined;
  }

  private isImageField(field: any): field is ImageField {
    return field && typeof field.src === 'string';
  }

  private isLinkField(field: any): field is LinkField {
    return field && typeof field.href === 'string';
  }
}

Then use the getters instead of directly accessing the rendering.fields. The ngIf directives ensure that elements are only rendered if the corresponding fields are 1. available and 2. the correct type:

<img *ngIf="backImage" class="imgSize" 
  *scImage="backImage" 
  [alt]="backImage?.value?.alt" />
<span *ngIf="backLink" *scGenericLink="backLink" class="back-link"></span>

Extend Existing Types in .d.ts

You should also be able to extend the types in your own .d.ts file. For example:

import { ComponentRendering, Field, ImageField } from '@sitecore-jss/sitecore-jss-angular';

export interface MyComponentFields extends ComponentFields {
  BackImage: ImageField;
  BackLink: LinkField;
}

export interface MyComponentRendering extends ComponentRendering {
  fields: MyComponentFields;
}

Then use MyComponentRendering instead of ComponentRendering.

Sample Application

Take a look at the sample app if you haven't already:

npx create-sitecore-jss@ver<version-number> angular [--appName <your-app-name> --fetchWith [REST| GraphQL]]

You may be able to glean some insights from there. For example, regarding setting the alt attribute of images, you can try this:

export class MyImageComponent {
  @Input() rendering: ComponentRendering;
  // this pattern can be used with the 'attrs' arg to the link to inject dynamic attributes from code
  linkDynamicAttributes = {
    alt: "xxxxxxxxx", // This is where you would do the type checking and value population
  };
}

Usage:

<img *scImage="rendering.fields.myField; attrs: alt"/>

If you see an area for improvement, submit a pull request!

Additional Insights

You can also check out the issues section of the JSS repo. There are numerous items which may provide additional insights / ideas. For example:

Last Resort

If all else fails and you need to hack a solution by modifying node_modules, I recommend using the patch-package node module (props to Corey Smith for making the Sitecore community aware of this).

2
  • Amazing explanation. I really appreciate you doing this. I noticed yesterday when I was cleaning up our code base I was able to resolve some of this, but the problem is that in the HTML templates VS code is not picking up all the timings until I build, so I was curious how I can get my script file to pick up all those typings from JSS so that they are identified During development? Dec 15, 2023 at 15:11
  • Real time type checking and potential auto-fixes on save events can be achieved with a variety of tools and configurations such as tsconfig.json, VS Code settings.json, ESLint (along with the ESLint TypeScript plugin @typescript-eslint and also the ESLint VS Code extension), and Prettier. Specific Angular tools include @angular/language-service along with the Angular Language Service VS Code extension. Dec 15, 2023 at 18:11

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.