我创建了自定义控件并将其连接到ngModel
。现在要在多个组件中重用此代码,我想将其分隔在不同的文件中。采用以下示例 -
import {Component, Provider, forwardRef} from "angular2/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR, CORE_DIRECTIVES} from "angular2/common";
const noop = () => {};
const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {
useExisting: forwardRef(() => CustomInput),
multi: true
});
@Component({
selector: 'custom-input',
template: `
<div class="form-group">
<label><ng-content></ng-content>
<input class="form-control"
[(ngModel)]="value"
(blur)="onTouched()">
</label>
</div>
`,
directives: [CORE_DIRECTIVES],
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CustomInput implements ControlValueAccessor{
//The internal data model
private _value: any = '';
//Placeholders for the callbacks
private _onTouchedCallback: (_:any) => void = noop;
private _onChangeCallback: (_:any) => void = noop;
//get accessor
get value(): any { return this._value; };
//set accessor including call the onchange callback
set value(v: any) {
if (v !== this._value) {
this._value = v;
this._onChangeCallback(v);
}
}
//Set touched on blur
onTouched(){
this._onTouchedCallback();
}
//From ControlValueAccessor interface
writeValue(value: any) {
this._value = value;
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this._onChangeCallback = fn;
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
this._onTouchedCallback = fn;
}
}
取自TableView.TableViewFocusModel
class。
然后我将这段代码分开 -
const noop = () => {};
const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {
useExisting: forwardRef(() => CustomInput),
multi: true
});
到external-file.ts
:
import { forwardRef } from 'angular2/core';
import { NG_VALUE_ACCESSOR } from 'angular2/common';
const fr = forwardRef;
export function createExtendedProvider(customInput) {
return new Provider(
NG_VALUE_ACCESSOR, {
useExisting: fr(() => { console.log(`Inside method ${customInput}`); return customInput; }),
multi: true
});
}
我在这样的组件中调用它 -
const MD_INPUT_CONTROL_VALUE_ACCESSOR = providerExtender.createExtendedProvider(CustomInput);
当我尝试运行应用程序时,我收到以下错误 -
browser_adapter.js:77 Error: Uncaught (in promise): Token must be defined!
at resolvePromise (zone.js:538)
at eval (zone.js:515)
at ZoneDelegate.invoke (zone.js:323)
at Object.NgZoneImpl.inner.inner.fork.onInvoke (ng_zone_impl.js:45)
at ZoneDelegate.invoke (zone.js:322)
at Zone.run (zone.js:216)
at eval (zone.js:571)
at ZoneDelegate.invokeTask (zone.js:356)
at Object.NgZoneImpl.inner.inner.fork.onInvokeTask (ng_zone_impl.js:36)
at ZoneDelegate.invokeTask (zone.js:355)
at Zone.runTask (zone.js:256)
at drainMicroTaskQueue (zone.js:474)
at HTMLDocument.ZoneTask.invoke (zone.js:426)BrowserDomAdapter.logError @ browser_adapter.js:77ExceptionHandler.call @ exception_handler.js:60(anonymous function) @ application_ref.js:194schedulerFn @ async.js:123SafeSubscriber.__tryOrUnsub @ Subscriber.js:166SafeSubscriber.next @ Subscriber.js:115Subscriber._next @ Subscriber.js:74Subscriber.next @ Subscriber.js:51Subject._finalNext @ Subject.js:124Subject._next @ Subject.js:116Subject.next @ Subject.js:73EventEmitter.emit @ async.js:112NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError @ ng_zone.js:120NgZoneImpl.inner.inner.fork.onHandleError @ ng_zone_impl.js:66ZoneDelegate.handleError @ zone.js:327Zone.runGuarded @ zone.js:233_loop_1 @ zone.js:487drainMicroTaskQueue @ zone.js:494ZoneTask.invoke @ zone.js:426
zone.js:461 Unhandled Promise rejection: Token must be defined! ; Zone: angular ; Task: Promise.then ; Value: BaseException {message: "Token must be defined!", stack: "Error: Token must be defined!↵ at new BaseExcep…oke (webpack:///./~/zone.js/dist/zone.js?:426:22)"}message: "Token must be defined!"stack: "Error: Token must be defined!↵ at new BaseException (webpack:///./~/angular2/src/facade/exceptions.js?:17:23)↵ at new Key (webpack:///./~/angular2/src/core/di/key.js?:26:19)↵ at KeyRegistry.get (webpack:///./~/angular2/src/core/di/key.js?:65:22)↵ at Function.Key.get (webpack:///./~/angular2/src/core/di/key.js?:40:60)↵ at resolveFactory (webpack:///./~/angular2/src/core/di/provider.js?:365:54)↵ at resolveProvider (webpack:///./~/angular2/src/core/di/provider.js?:385:66)↵ at Array.map (native)↵ at Object.resolveProviders (webpack:///./~/angular2/src/core/di/provider.js?:393:31)↵ at Function.Injector.resolve (webpack:///./~/angular2/src/core/di/injector.js?:426:27)↵ at Function.DirectiveProvider.createFromType (webpack:///./~/angular2/src/core/linker/element.js?:100:82)↵ at ResolvedMetadataCache.getResolvedDirectiveMetadata (webpack:///./~/angular2/src/core/linker/resolved_metadata_cache.js?:27:50)↵ at Function.AppProtoElement.create (webpack:///./~/angular2/src/core/linker/element.js?:159:45)↵ at eval (viewFactory_Login:833:41)↵ at Object.evalExpression (webpack:///./~/angular2/src/facade/lang.js?:457:94)↵ at TemplateCompiler._createViewFactoryRuntime (webpack:///./~/angular2/src/compiler/template_compiler.js?:184:27)↵ at eval (webpack:///./~/angular2/src/compiler/template_compiler.js?:144:49)↵ at ZoneDelegate.invoke (webpack:///./~/zone.js/dist/zone.js?:323:29)↵ at Object.NgZoneImpl.inner.inner.fork.onInvoke (webpack:///./~/angular2/src/core/zone/ng_zone_impl.js?:45:41)↵ at ZoneDelegate.invoke (webpack:///./~/zone.js/dist/zone.js?:322:35)↵ at Zone.run (webpack:///./~/zone.js/dist/zone.js?:216:44)↵ at eval (webpack:///./~/zone.js/dist/zone.js?:571:58)↵ at ZoneDelegate.invokeTask (webpack:///./~/zone.js/dist/zone.js?:356:38)↵ at Object.NgZoneImpl.inner.inner.fork.onInvokeTask (webpack:///./~/angular2/src/core/zone/ng_zone_impl.js?:36:41)↵ at ZoneDelegate.invokeTask (webpack:///./~/zone.js/dist/zone.js?:355:43)↵ at Zone.runTask (webpack:///./~/zone.js/dist/zone.js?:256:48)↵ at drainMicroTaskQueue (webpack:///./~/zone.js/dist/zone.js?:474:36)↵ at HTMLDocument.ZoneTask.invoke (webpack:///./~/zone.js/dist/zone.js?:426:22)"__proto__: ErrorconsoleError @ zone.js:461_loop_1 @ zone.js:490drainMicroTaskQueue @ zone.js:494ZoneTask.invoke @ zone.js:426
我注意到当我调用createExtendedProvider
函数并在那里传递CustomInput
时,它仍未定义。那么,有人可以给我一个提示,如果可以将它分成不同的文件,如果是 - 如何?
答案 0 :(得分:1)
<强>更新强>
要在外部文件中创建提供程序,您可以像
一样使用它@Component({
selector: 'custom-input',
template: `
...
`,
directives: [FORM_DIRECTIVES],
providers: [providerExtender.createExtendedProvider(forwardRef(() => CustomInput))]
})
export class CustomInput implements ControlValueAccessor{
并在另一个文件中
export function createExtendedProvider(customInput) {
return new Provider(
NG_VALUE_ACCESSOR, {useExisting: customInput, multi: true});
}
<强>原始强>
这似乎无效
export function createExtendedProvider(customInput) {
return new Provider(
NG_VALUE_ACCESSOR, {
useExisting: fr(() => { console.log(`Inside method ${customInput}`); return customInput; }),
multi: true
});
}
useExisting
意味着需要出现一种应由Angulars DI解决的类型。
我想这就是你想要的:
export function createExtendedProvider(customInput) {
return new Provider(
NG_VALUE_ACCESSOR, {
useClass: customInput,
multi: true
});
}
答案 1 :(得分:0)
也许这是一个愚蠢的答案,但我想说你只需要将CustomInput
组件指定到你想要使用它的组件(directives
属性)中:
import {CustomInput} from './custom.input';
@Component({
(...)
template: `
<custom-input [(ngModel)]="something" ngControl="test"></custom-input>
`,
directives: [CustomInput]
})
export class SomeOtherComponent {
(...)
}
实际上,值访问器是在组件本身的providers
内注册的,因此我认为没有必要将它们分成两个不同的模块。