如何使用ngModel将自定义控件的提供程序扩展器分离到Angular 2中的单独文件中
How to separate provider extender for custom control with ngModel to a separate file in Angular 2
我创建自定义控件并将其连接到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;
}
}
示例取自此处。
然后我把这段代码分开——
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
时,它仍然未定义。那么,有人可以给我一个提示,是否可以将其分离到不同的文件,如果是 - 如何?
更新
要在外部文件中创建提供程序,您可以像
@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
});
}
也许这是一个
愚蠢的答案,但我想说你只需要在你想要使用它的组件(directives
属性(中指定CustomInput
组件:
import {CustomInput} from './custom.input';
@Component({
(...)
template: `
<custom-input [(ngModel)]="something" ngControl="test"></custom-input>
`,
directives: [CustomInput]
})
export class SomeOtherComponent {
(...)
}
事实上,值访问器是在组件本身的providers
中注册的,因此我认为没有必要将它们分成两个不同的模块。
相关文章:
- Angular JS IE9 Hashbang url rewriting
- 如何使用skip参数使用angular ui引导进行服务器端分页
- 在自定义mean.io包中使用angular-chart.js作为依赖项
- 无法在数据endVal中设置值=“”;{{ucount}}”;使用Angular JS的CountUp
- 使用angular重定向到html页面
- angular.js没有'无法在PhoneGap中处理视图标记
- Javascript(Angular)从一个对象数组到第二个数组查找值
- angular 1.5应用程序中的导航栏
- angular的下拉菜单
- 如何使用ngModel将自定义控件的提供程序扩展器分离到Angular 2中的单独文件中
- 将 DOM 操作与 Angular 控制器分离 - 需要最佳实践
- Angular JS概念'关注点分离'-->CRUD屏幕
- 将angular与html完全分离
- 指令中的Angular条件模板;基于从承诺中分离范围变量的决策
- Angular指令作用域.如何分离单个变量
- Angular -为每个显示和隐藏元素分离变量
- 在Angular 2中分离项目的逻辑
- 如何在Angular 2组件中分离移动版和桌面版的模板和样式
- Angular:控制器与指令的交互,支持关注点分离
- 在Angular中,从分离视图控制器中改变事件的作用域变量