diff --git a/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md b/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md
index a63eb44cc..c840b26f4 100644
--- a/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md
+++ b/packages/eslint-plugin-template/docs/rules/no-duplicate-attributes.md
@@ -35,6 +35,12 @@ interface Options {
* Default: `true`
*/
allowTwoWayDataBinding?: boolean;
+ /**
+ * Input or output properties for which duplicate presence is allowed as an exception to the rule.
+ *
+ * Default: `[]`
+ */
+ ignore?: string[];
}
```
@@ -346,6 +352,38 @@ interface Options {
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
+
+
+---
+
+
+
+#### Custom Config
+
+```json
+{
+ "rules": {
+ "@angular-eslint/template/no-duplicate-attributes": [
+ "error",
+ {
+ "ignore": [
+ "class"
+ ]
+ }
+ ]
+ }
+}
+```
+
+
+
+#### ❌ Invalid Code
+
+```html
+
+ ~~~~~~~~~~~~ ~~~~~~~~~~
+```
+
diff --git a/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts b/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts
index 1dad252c9..d9bcdc9e7 100644
--- a/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts
+++ b/packages/eslint-plugin-template/src/rules/no-duplicate-attributes.ts
@@ -10,10 +10,18 @@ import {
} from '../utils/create-eslint-rule';
import { getOriginalAttributeName } from '../utils/get-original-attribute-name';
-type Options = [{ readonly allowTwoWayDataBinding?: boolean }];
+type Options = [
+ {
+ readonly allowTwoWayDataBinding?: boolean;
+ readonly ignore?: readonly string[];
+ },
+];
export type MessageIds = 'noDuplicateAttributes' | 'suggestRemoveAttribute';
export const RULE_NAME = 'no-duplicate-attributes';
-const DEFAULT_OPTIONS: Options[number] = { allowTwoWayDataBinding: true };
+const DEFAULT_OPTIONS: Options[number] = {
+ allowTwoWayDataBinding: true,
+ ignore: [],
+};
export default createESLintRule({
name: RULE_NAME,
@@ -34,6 +42,13 @@ export default createESLintRule({
default: DEFAULT_OPTIONS.allowTwoWayDataBinding,
description: `Whether or not two-way data binding is allowed as an exception to the rule.`,
},
+ ignore: {
+ type: 'array',
+ items: { type: 'string' },
+ uniqueItems: true,
+ default: DEFAULT_OPTIONS.ignore,
+ description: `Input or output properties for which duplicate presence is allowed as an exception to the rule.`,
+ },
},
additionalProperties: false,
},
@@ -44,7 +59,7 @@ export default createESLintRule({
},
},
defaultOptions: [DEFAULT_OPTIONS],
- create(context, [{ allowTwoWayDataBinding }]) {
+ create(context, [{ allowTwoWayDataBinding, ignore }]) {
const parserServices = getTemplateParserServices(context);
return {
@@ -68,7 +83,15 @@ export default createESLintRule({
...duplicateOutputs,
] as const;
- allDuplicates.forEach((duplicate) => {
+ const filteredDuplicates =
+ ignore && ignore.length > 0
+ ? allDuplicates.filter(
+ (duplicate) =>
+ !ignore.includes(getOriginalAttributeName(duplicate)),
+ )
+ : allDuplicates;
+
+ filteredDuplicates.forEach((duplicate) => {
const loc = parserServices.convertNodeSourceSpanToLoc(
duplicate.sourceSpan,
);
diff --git a/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts b/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts
index 9656f489a..70f2108a5 100644
--- a/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts
+++ b/packages/eslint-plugin-template/tests/rules/no-duplicate-attributes/cases.ts
@@ -496,4 +496,44 @@ export const invalid = [
},
],
}),
+ convertAnnotatedSourceToFailureCase({
+ description: 'should not report ignored properties',
+ annotatedSource: `
+
+ ~~~~~~~~~~~~ ^^^^^^^^^^
+ `,
+ options: [{ ignore: ['class'] }],
+ messages: [
+ {
+ char: '~',
+ messageId,
+ data: { attributeName: 'name' },
+ suggestions: [
+ {
+ messageId: suggestRemoveAttribute,
+ output: `
+
+
+ `,
+ data: { attributeName: 'name' },
+ },
+ ],
+ },
+ {
+ char: '^',
+ messageId,
+ data: { attributeName: 'name' },
+ suggestions: [
+ {
+ messageId: suggestRemoveAttribute,
+ output: `
+
+
+ `,
+ data: { attributeName: 'name' },
+ },
+ ],
+ },
+ ],
+ }),
];