• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 27688554805

17 Jun 2026 12:21PM UTC coverage: 90.153% (+0.009%) from 90.144%
27688554805

Pull #17333

github

web-flow
Merge 63b0aa9fb into dcdcabcd3
Pull Request #17333: feat(grid): update grid border-related styles

14879 of 17335 branches covered (85.83%)

Branch coverage included in aggregate %.

29956 of 32397 relevant lines covered (92.47%)

34456.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.47
/projects/igniteui-angular/chat/src/chat.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    Component,
4
    CUSTOM_ELEMENTS_SCHEMA,
5
    Directive,
6
    effect,
7
    inject,
8
    input,
9
    OnInit,
10
    output,
11
    signal,
12
    TemplateRef,
13
    ViewContainerRef,
14
    OnDestroy,
15
    ViewRef,
16
    computed,
17
} from '@angular/core';
18
import {
19
    IgcChatComponent,
20
    type IgcChatMessageAttachment,
21
    type IgcChatMessage,
22
    type IgcChatOptions,
23
    type ChatRenderContext,
24
    type ChatRenderers,
25
    type ChatAttachmentRenderContext,
26
    type ChatInputRenderContext,
27
    type ChatMessageRenderContext,
28
    type IgcChatMessageReaction,
29
} from 'igniteui-webcomponents';
30

31
type ChatContextUnion =
32
    | ChatAttachmentRenderContext
33
    | ChatMessageRenderContext
34
    | ChatInputRenderContext
35
    | ChatRenderContext;
36

37
type ChatContextType<T extends ChatContextUnion> =
38
    T extends ChatAttachmentRenderContext
39
    ? IgcChatMessageAttachment
40
    : T extends ChatMessageRenderContext
41
    ? IgcChatMessage
42
    : T extends ChatInputRenderContext
43
    ? string
44
    : T extends ChatRenderContext
45
    ? { instance: IgcChatComponent }
46
    : never;
47

48
type ExtractChatContext<T> = T extends (ctx: infer R) => any ? R : never;
49

50
type ChatTemplatesContextMap = {
51
    [K in keyof ChatRenderers]: {
52
        $implicit: ChatContextType<
53
            ExtractChatContext<NonNullable<ChatRenderers[K]>> & ChatContextUnion
54
        >;
55
    };
56
};
57

58
/**
59
 * Template references for customizing chat component rendering.
60
 * Each property corresponds to a specific part of the chat UI that can be customized.
61
 *
62
 * @example
63
 * ```typescript
64
 * templates = {
65
 *   messageContent: this.customMessageTemplate,
66
 *   attachment: this.customAttachmentTemplate
67
 * }
68
 * ```
69
 */
70
export type IgxChatTemplates = {
71
    [K in keyof Omit<ChatRenderers, 'typingIndicator'>]?: TemplateRef<ChatTemplatesContextMap[K]>;
72
};
73

74
/**
75
 * Configuration options for the chat component.
76
 */
77
export type IgxChatOptions = Omit<IgcChatOptions, 'renderers'>;
78

79

80
/**
81
 * Angular wrapper component for the Ignite UI Web Components Chat component.
82
 *
83
 * This component provides an Angular-friendly interface to the igc-chat web component,
84
 * including support for Angular templates, signals, and change detection.
85
 *
86
 * Uses OnPush change detection strategy for optimal performance. All inputs are signals,
87
 * so changes are automatically tracked and propagated to the underlying web component.
88
 *
89
 * @example
90
 * ```typescript
91
 * <igx-chat
92
 *   [messages]="messages"
93
 *   [draftMessage]="draft"
94
 *   [options]="chatOptions"
95
 *   [templates]="chatTemplates"
96
 *   (messageCreated)="onMessageCreated($event)"
97
 * />
98
 * ```
99
 */
100
@Component({
101
    selector: 'igx-chat',
102
    standalone: true,
103
    changeDetection: ChangeDetectionStrategy.OnPush,
104
    schemas: [CUSTOM_ELEMENTS_SCHEMA],
105
    templateUrl: './chat.component.html'
106
})
107
export class IgxChatComponent implements OnInit, OnDestroy {
3✔
108
    //#region Internal state
109

110
    private readonly _view = inject(ViewContainerRef);
21✔
111
    private readonly _templateViewRefs = new Map<TemplateRef<any>, Set<ViewRef>>();
21✔
112
    private _oldTemplates: IgxChatTemplates = {};
21✔
113

114
    protected readonly _transformedTemplates = signal<ChatRenderers>({});
21✔
115

116
    protected readonly _mergedOptions = computed<IgcChatOptions>(() => {
21✔
117
        const options = this.options();
22✔
118
        const transformedTemplates = this._transformedTemplates();
22✔
119
        return {
22✔
120
            ...options,
121
            renderers: transformedTemplates
122
        };
123
    });
124

125
    //#endregion
126

127
    //#region Inputs
128

129
    /** Array of chat messages to display */
130
    public readonly messages = input<IgcChatMessage[]>([]);
21✔
131

132
    /** Draft message with text and optional attachments */
133
    public readonly draftMessage = input<
21✔
134
        { text: string; attachments?: IgcChatMessageAttachment[] } | undefined
135
    >({ text: '' });
136

137
    /** Configuration options for the chat component */
138
    public readonly options = input<IgxChatOptions>({});
21✔
139

140
    /** Custom templates for rendering chat elements */
141
    public readonly templates = input<IgxChatTemplates>({});
21✔
142

143
    //#endregion
144

145
    //#region Outputs
146

147
    /** Emitted when a new message is created */
148
    public readonly messageCreated = output<IgcChatMessage>();
21✔
149

150
    /** Emitted when a user reacts to a message */
151
    public readonly messageReact = output<IgcChatMessageReaction>();
21✔
152

153
    /** Emitted when an attachment is clicked */
154
    public readonly attachmentClick = output<IgcChatMessageAttachment>();
21✔
155

156
    /** Emitted when attachment drag starts */
157
    public readonly attachmentDrag = output<void>();
21✔
158

159
    /** Emitted when attachment is dropped */
160
    public readonly attachmentDrop = output<void>();
21✔
161

162
    /** Emitted when typing indicator state changes */
163
    public readonly typingChange = output<boolean>();
21✔
164

165
    /** Emitted when the input receives focus */
166
    public readonly inputFocus = output<void>();
21✔
167

168
    /** Emitted when the input loses focus */
169
    public readonly inputBlur = output<void>();
21✔
170

171
    /** Emitted when the input value changes */
172
    public readonly inputChange = output<string>();
21✔
173

174
    //#endregion
175

176
    /** @internal */
177
    public ngOnInit(): void {
178
        IgcChatComponent.register();
21✔
179
    }
180

181
    /** @internal */
182
    public ngOnDestroy(): void {
183
        for (const viewSet of this._templateViewRefs.values()) {
23✔
184
            viewSet.forEach(viewRef => viewRef.destroy());
14✔
185
        }
186
        this._templateViewRefs.clear();
23✔
187
    }
188

189
    constructor() {
190
        // Templates changed - update transformed templates and viewRefs
191
        effect(() => {
21✔
192
            const templates = this.templates();
22✔
193
            this._setTemplates(templates ?? {});
22✔
194
        });
195
    }
196

197
    private _setTemplates(newTemplates: IgxChatTemplates): void {
198
        const templateCopies: ChatRenderers = {};
34✔
199
        const newTemplateKeys = Object.keys(newTemplates) as Array<keyof IgxChatTemplates>;
34✔
200

201
        const oldTemplates = this._oldTemplates;
34✔
202
        const oldTemplateKeys = Object.keys(oldTemplates) as Array<keyof IgxChatTemplates>;
34✔
203

204
        for (const key of oldTemplateKeys) {
34✔
205
            const oldRef = oldTemplates[key];
4✔
206
            const newRef = newTemplates[key];
4✔
207

208
            if (oldRef && oldRef !== newRef) {
4✔
209
                const obsolete = this._templateViewRefs.get(oldRef);
3✔
210
                if (obsolete) {
3✔
211
                    obsolete.forEach(viewRef => viewRef.destroy());
3✔
212
                    this._templateViewRefs.delete(oldRef);
3✔
213
                }
214
            }
215
        }
216

217
        this._oldTemplates = {};
34✔
218

219
        for (const key of newTemplateKeys) {
34✔
220
            const ref = newTemplates[key];
12✔
221
            if (ref) {
12✔
222
                (this._oldTemplates as Record<string, TemplateRef<unknown>>)[key] = ref;
11✔
223
                templateCopies[key] = this._createTemplateRenderer(ref);
11✔
224
            }
225
        }
226

227
        this._transformedTemplates.set(templateCopies);
34✔
228
    }
229

230
    private _createTemplateRenderer<K extends keyof IgxChatTemplates>(ref: NonNullable<IgxChatTemplates[K]>) {
231
        type ChatContext = ExtractChatContext<NonNullable<ChatRenderers[K]>>;
232

233
        if (!this._templateViewRefs.has(ref)) {
19✔
234
            this._templateViewRefs.set(ref, new Set<ViewRef>());
17✔
235
        }
236

237
        const viewSet = this._templateViewRefs.get(ref)!;
19✔
238

239
        return (ctx: ChatContext) => {
19✔
240
            const context = ctx as ChatContextUnion;
13✔
241
            let angularContext: any;
242

243
            if ('message' in context && 'attachment' in context) {
13✔
244
                angularContext = { $implicit: context.attachment };
1✔
245
            } else if ('message' in context) {
12✔
246
                angularContext = { $implicit: context.message };
10✔
247
            } else if ('value' in context) {
2✔
248
                angularContext = {
1✔
249
                    $implicit: context.value,
250
                    attachments: context.attachments
251
                };
252
            } else {
253
                angularContext = { $implicit: { instance: context.instance } };
1✔
254
            }
255

256
            const viewRef = this._view.createEmbeddedView(ref, angularContext);
13✔
257
            viewSet.add(viewRef);
13✔
258

259
            return viewRef.rootNodes;
13✔
260
        }
261
    }
262
}
263

264
/**
265
 * Context provided to the chat input template.
266
 */
267
export interface ChatInputContext {
268
    /** The current input value */
269
    $implicit: string;
270
    /** Array of attachments associated with the input */
271
    attachments: IgcChatMessageAttachment[];
272
}
273

274
/**
275
 * Directive providing type information for chat message template contexts.
276
 * Use this directive on ng-template elements that render chat messages.
277
 *
278
 * @example
279
 * ```html
280
 * <ng-template igxChatMessageContext let-message>
281
 *   <div>{{ message.text }}</div>
282
 * </ng-template>
283
 * ```
284
 */
285
@Directive({ selector: '[igxChatMessageContext]', standalone: true })
286
export class IgxChatMessageContextDirective {
3✔
287

288
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
289
    public static ngTemplateContextGuard(_: IgxChatMessageContextDirective, ctx: unknown): ctx is { $implicit: IgcChatMessage } {
290
        return true;
×
291
    }
292
}
293

294
/**
295
 * Directive providing type information for chat attachment template contexts.
296
 * Use this directive on ng-template elements that render message attachments.
297
 *
298
 * @example
299
 * ```html
300
 * <ng-template igxChatAttachmentContext let-attachment>
301
 *   <img [src]="attachment.url" />
302
 * </ng-template>
303
 * ```
304
 */
305
@Directive({ selector: '[igxChatAttachmentContext]', standalone: true })
306
export class IgxChatAttachmentContextDirective {
3✔
307

308
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
309
    public static ngTemplateContextGuard(_: IgxChatAttachmentContextDirective, ctx: unknown): ctx is { $implicit: IgcChatMessageAttachment } {
310
        return true;
×
311
    }
312
}
313

314
/**
315
 * Directive providing type information for chat input template contexts.
316
 * Use this directive on ng-template elements that render the chat input.
317
 *
318
 * @example
319
 * ```html
320
 * <ng-template igxChatInputContext let-value let-attachments="attachments">
321
 *   <input [value]="value" />
322
 * </ng-template>
323
 * ```
324
 */
325
@Directive({ selector: '[igxChatInputContext]', standalone: true })
326
export class IgxChatInputContextDirective {
3✔
327

328
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
329
    public static ngTemplateContextGuard(_: IgxChatInputContextDirective, ctx: unknown): ctx is ChatInputContext {
330
        return true;
×
331
    }
332
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc