fix(ivy): unable to inject class and style attributes (#29192)
Fixes not being able to inject the `class` and `style` attributes via the `Attribute` decorator. This PR resolves FW-1139. PR Close #29192
This commit is contained in:
parent
ec01594e97
commit
940fbf796c
|
@ -17,7 +17,7 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
|
||||||
import {NG_ELEMENT_ID} from './fields';
|
import {NG_ELEMENT_ID} from './fields';
|
||||||
import {DirectiveDef} from './interfaces/definition';
|
import {DirectiveDef} from './interfaces/definition';
|
||||||
import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector';
|
import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector';
|
||||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node';
|
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node';
|
||||||
import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view';
|
import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
|
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
|
||||||
|
@ -271,14 +271,39 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str
|
||||||
ngDevMode && assertDefined(tNode, 'expecting tNode');
|
ngDevMode && assertDefined(tNode, 'expecting tNode');
|
||||||
const attrs = tNode.attrs;
|
const attrs = tNode.attrs;
|
||||||
if (attrs) {
|
if (attrs) {
|
||||||
for (let i = 0; i < attrs.length; i = i + 2) {
|
const attrsLength = attrs.length;
|
||||||
const attrName = attrs[i];
|
let i = 0;
|
||||||
|
while (i < attrsLength) {
|
||||||
|
const value = attrs[i];
|
||||||
|
|
||||||
// If we hit a `Bindings` or `Template` marker then we are done.
|
// If we hit a `Bindings` or `Template` marker then we are done.
|
||||||
if (isNameOnlyAttributeMarker(attrName)) break;
|
if (isNameOnlyAttributeMarker(value)) break;
|
||||||
|
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
// Skip to the first value of the marked attribute.
|
||||||
|
i++;
|
||||||
|
if (value === AttributeMarker.Classes && attrNameToInject === 'class') {
|
||||||
|
let accumulatedClasses = '';
|
||||||
|
while (i < attrsLength && typeof attrs[i] === 'string') {
|
||||||
|
accumulatedClasses += ' ' + attrs[i++];
|
||||||
|
}
|
||||||
|
return accumulatedClasses.trim();
|
||||||
|
} else if (value === AttributeMarker.Styles && attrNameToInject === 'style') {
|
||||||
|
let accumulatedStyles = '';
|
||||||
|
while (i < attrsLength && typeof attrs[i] === 'string') {
|
||||||
|
accumulatedStyles += `${attrs[i++]}: ${attrs[i++]}; `;
|
||||||
|
}
|
||||||
|
return accumulatedStyles.trim();
|
||||||
|
} else {
|
||||||
|
while (i < attrsLength && typeof attrs[i] === 'string') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (value === attrNameToInject) {
|
||||||
// TODO(FW-1137): Skip namespaced attributes
|
// TODO(FW-1137): Skip namespaced attributes
|
||||||
// TODO(FW-1139): supports classes/styles in @Attribute injection
|
|
||||||
if (attrName == attrNameToInject) {
|
|
||||||
return attrs[i + 1] as string;
|
return attrs[i + 1] as string;
|
||||||
|
} else {
|
||||||
|
i = i + 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {ChangeDetectorRef, Component, Directive, Inject, LOCALE_ID, Optional, Pipe, PipeTransform, SkipSelf, ViewChild} from '@angular/core';
|
import {Attribute, ChangeDetectorRef, Component, Directive, Inject, LOCALE_ID, Optional, Pipe, PipeTransform, SkipSelf, ViewChild} from '@angular/core';
|
||||||
import {ViewRef} from '@angular/core/src/render3/view_ref';
|
import {ViewRef} from '@angular/core/src/render3/view_ref';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
@ -130,4 +130,31 @@ describe('di', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.componentInstance.myDir.localeId).toBe('en-GB');
|
expect(fixture.componentInstance.myDir.localeId).toBe('en-GB');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to inject different kinds of attributes', () => {
|
||||||
|
@Directive({selector: '[dir]'})
|
||||||
|
class MyDir {
|
||||||
|
constructor(
|
||||||
|
@Attribute('class') public className: string,
|
||||||
|
@Attribute('style') public inlineStyles: string,
|
||||||
|
@Attribute('other-attr') public otherAttr: string) {}
|
||||||
|
}
|
||||||
|
@Component({
|
||||||
|
template:
|
||||||
|
'<div dir style="margin: 1px; color: red;" class="hello there" other-attr="value"></div>'
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [MyDir, MyComp, MyComp]});
|
||||||
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const directive = fixture.componentInstance.directiveInstance;
|
||||||
|
|
||||||
|
expect(directive.otherAttr).toBe('value');
|
||||||
|
expect(directive.className).toBe('hello there');
|
||||||
|
expect(directive.inlineStyles).toBe('margin: 1px; color: red;');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue