Angular-Formly:在多个指令中与多个表单交互

时间:2016-02-29 14:56:30

标签: javascript angularjs angular-formly

我正在试图弄清楚如何在多个指令中保存多个表单并做出反应。

简要介绍一下: Screenshot of the current view

我有三个包含表单的选项卡,第四个包含JsTree(Groups)。三个选项卡中的每一个都包含一个指令,该指令又包含一个Formly表单。 选项卡由一个主指令包装,该指令包含一个页脚指令,右下角有保存和取消按钮。

主要指示:

/**
 * Displays the ui for editing a specific user
 */
export function UserDetailsDirective() {
	class UserDetailsDirective {

		/*@ngInject*/
		constructor(
			$stateParams,
			userService,
			formlyChangeService
		) {
			this.currentUser = this.currentUser || {};
			this.originalUser = this.originalUser || {};

			this.userForms = {
				mainData: {},
				personalData: {},
				basicSettings: {}
			};

			this.savingAllowed = true;
              
            /* Second try: Registering a callback at the change service, which will be executed on any field change in the passed form (mainData) */
            formlyChangeService.onFormChange('mainData', () => {
				console.log('test123');
				console.log('this123', this);

				console.log('this.userForms.mainData.api.isValid()', this.userForms.mainData.api.isValid());
			});


			if ($stateParams.id > 0) {
				userService.getUser($stateParams.id).then((userData) => {
					userData.Birthday = new Date(userData.Birthday);
					this.currentUser = userData;

					this.breadcrumbData = [...];
				})
			}
		}

		onSave(controller) {
			alert('on save');
			console.log('controller', controller);
		}
	}

	return {
		restrict: 'E',
		templateUrl: 'components/usermanagement/edit/user-details/user-details.directive.html',
		controller: UserDetailsDirective,
		controllerAs: 'controller',
		bindToController: true
	}
}
<breadcrumb [...]></breadcrumb>

<ul class="nav nav-tabs">
	<li class="active"><a data-toggle="tab" data-target="#mainData">Account data</a></li>
	<li><a data-toggle="tab" data-target="#personalData">Personal data</a></li>
	<li><a data-toggle="tab" data-target="#basicSettings">Settings</a></li>
	<li><a data-toggle="tab" data-target="#userGroupAssignment">Groups</a></li>
</ul>

<div class="row">
	<div class="col-lg-6">
		<div class="tab-content">
			<div id="mainData" class="tab-pane fade in active">
				<main-data user="controller.currentUser"></main-data>
			</div>
			<div id="personalData" class="tab-pane fade">
				<personal-data user="controller.currentUser"></personal-data>
			</div>
			<div id="basicSettings" class="tab-pane fade">
				<basic-settings user="controller.currentUser"></basic-settings>
			</div>
			<div id="userGroupAssignment" class="tab-pane fade">
				<group-assignment user="controller.currentUser"></group-assignment>
			</div>
		</div>
	</div>
	<div class="col-lg-6">
		[...] <!-- Right column -->
	</div>
</div>

<!-- Footer -->
<user-details-footer
	on-save="controller.onSave(controller)"
	saving-allowed="controller.savingAllowed"
></user-details-footer>

页脚指令

/**
 * Displays the user details footer
 */
export function UserDetailsFooterDirective() {
	class UserDetailsFooterDirective {

		/*@ngInject*/
		constructor(
			$state,
			Notification,
			$translate
		) {
			this.state = $state;
			this.notification = Notification;
			this.translate = $translate;

			this.savingAllowed = this.savingAllowed || false;
		}

		/**
		 * Event that is triggered on save button click
		 *
		 * Propagates to the parent controller via attribute binding
		 */
		saveEvent() {
			if (typeof this.onSave === 'function') {
				this.onSave();
			}
		}

		/**
		 * Navigates to the user list
		 */
		goToUserList() {
			this.state.go('userList');
		}
	}

	return {
		restrict: 'E',
		templateUrl: 'components/usermanagement/edit/user-details-footer/user-details-footer.directive.html',
		controller: UserDetailsFooterDirective,
		controllerAs: 'controller',
		bindToController: true,
		scope: {
			onSave: '&?',
			savingAllowed: '=?'
		}
	}
}
<nav class="navbar navbar-fixed-bottom">
	<div class="container-fluid pull-right">
		<button class="btn btn-default" ng-click="controller.goToUserList()"><i class="fontIcon fontIconX"></i> Cancel</button>
		<button class="btn btn-primary" ng-disabled="controller.savingAllowed !== true" ng-click="controller.saveEvent()"><i class="fontIcon fontIconSave"></i> Save</button>
	</div>
</nav>

第一个标签的指令

/**
 * Displays the contents of the tab "Account data"
 */
export function MainDataDirective() {
	class MainDataDirective {

		/*@ngInject*/
		constructor(
			formlyFormService,
			mainDataFieldProviders,
			$state,
			userSubmitService,
			$timeout,
		    formlyChangeService,
		    $scope
		) {
			this.state = $state;
			this.$timeout = $timeout;

			this.userSubmitService = userSubmitService;

			this.model = {};
			this.originalUser = this.originalUser || {};
			this.fields = [];

			this.form = null;
			var that = this;

            /* Third try: Watching the form instance => partial success */
			this.watch('formMainData', function(x, y, form) {
				console.log('formMainData', form);
				that.form = form;
				form.watch('$invalid', function(foo, bar, value) {
                    /* This will react on field changes but it seems really dirty to me */
					console.log('$invalid', arguments);
				});

			});


			formlyFormService.getFormConfiguration(mainDataFieldProviders).then((result) => {
				/* Here the formly fields are set */
                this.fields = result;
                /* Second try: A service which provides a callback that will be executed on field invalidation => no success */
				formlyChangeService.registerFields(this.fields, 'mainData');
			}, (error) => {
				console.error('getMainDataFields error:', error);
			});

			this.api = {
				isValid: angular.bind(this, this.isValid),
				submit: angular.bind(this, this.onSubmit)
			}
		}

        /* First try to get the validity of the fields => no success */
		isValid() {
			//return this.$timeout(() => {
				let isValid = true;

				this.fields.some((field) => {
					if (
						field.validation.errorExistsAndShouldBeVisible === true
						|| field.validation.serverMessages.length > 0
					) {
						isValid = false;
						return true;
					}
				});

				//return isValid;
			//}, 10);

            return isValid;
		}

		/**
		 * Method triggered by the formSubmit event
         */
		onSubmit() {
			this.userSubmitService.submitUser(this.fields, this.model);
		}
    }

	return {
		restrict: 'E',
		templateUrl: 'components/usermanagement/edit/main-data/main-data.directive.html',
		controller: MainDataDirective,
		controllerAs: 'controller',
		bindToController: true,
		scope: {
			originalUser: '=user',
			api: '=?'
		},
		link: (scope) => {
			scope.$watch('controller.originalUser', (newValue) => {
				if (newValue.hasOwnProperty('ID')) {
					scope.controller.model = angular.copy(newValue);
				}
			});
		}
	}
}
<form name="controller.form" ng-submit="controller.onSubmit()" class="form-horizontal" novalidate>
	<formly-form form="controller.formMainData" model="controller.model" fields="controller.fields" ></formly-form>
</form>

第二次尝试:FormlyChangeService =&gt;在验证之前触发了更改事件=&gt;没有成功

export /*@ngInject*/ function FormlyChangeService() {
	let callbacks = [];

	return {
		triggerFormChangeEvent: triggerFormChangeEvent,
		registerFields: registerFields,
		onFormChange: onFormChange
	};

	function triggerFormChangeEvent(value, options) {
		callbacks.forEach((callback) => {
			if (
				typeof callback === 'function'
				&& callback.formDirective === options.templateOptions.formDirective
			) {
				callback();
			}
		});
	}

	function onFormChange(formDirective, callback) {
		callback.formDirective = formDirective;
		callbacks.push(callback);
	}

	function registerField(fieldConfig) {
		fieldConfig.templateOptions.changeEvents.push(
			triggerFormChangeEvent
		);
	}

	function registerFields(fieldConfigs, formDirective) {
		fieldConfigs.forEach((fieldConfig) => {
			fieldConfig.templateOptions.formDirective = formDirective;
			registerField(fieldConfig);

			fieldConfig.watcher = {
				listener: function() {
					console.log('listener', arguments);
				}
			};

			console.log('fieldConfig', fieldConfig);


			fieldConfig.watch('$valid', function() {
console.log('valid field', arguments);
			});




		});
	}

}

Formly表格由用户模型提供,该模型由主要指令提供。

我必须同时保存所有四个标签,因为必须存在几个必填字段才能保存输入的记录。 现在这里有一个棘手的部分:

如果模型未更改或任何形式的任何字段发生错误,我希望禁用保存按钮。我也想知道错误来自哪种形式。

我想到的是Formly字段配置中的事件或观察者或类似的东西。

我在字段配置上尝试了onChange事件,但是在字段验证运行之前就被触发了,所以我不会得到该字段的当前错误状态。

错误状态必须传递给main指令,从那里它应该传递给保存按钮。

任何人都可以帮我获取表格(甚至更好的相应字段)来告诉主要指令是否存在无效字段?

很难说明这么复杂的任务,所以如果有任何晦涩难懂,请告诉我。

非常感谢你。

儒略

1 个答案:

答案 0 :(得分:0)

我认为您应该拥有一个服务或工厂,您的所有指令都依赖于该服务或工厂,以保存所有表单的数据。

通过这种方式,您可以在指令中设置一个监视器,该监视器将调用共享服务上的任何方法来验证/使其他选项卡上的表单无效。

我希望这会有所帮助