2

tl; dr;Динамические формы и имена имен Ng-сообщений

Похоже, что директива ng-messages не может быть переведена. Когда вы переводите его, он не связывается правильно. У меня есть 2 рабочих стола, но у обоих есть свои недостатки. У кого-то есть лучшее решение, или я делаю что-то неправильно?

Я использую машинопись, и я не знаю, как сделать плункер или jsfiddle (извините за это ..).


Версии

AngularJS: 1.6.1

угловой одушевленные: 1.6.1

Радиально-ARIA: 1.6.1

Радиально-сообщения: 1.6 .1

Угловой материал: 1.1.0

Машинопись: 3.10.5


Что я хочу

У меня есть компонент, который форматирует дату (делает несколько больше, но это не имеет отношения) и показывает его. Некоторые из этих компонентов включены на одной странице. Окружение этих компонентов - это форма. Я хочу проверить эту форму с помощью настраиваемых ng-сообщений, скажем, сообщение об ошибке отображается, когда дата будет в будущем.


чем проблема

Когда я transclude сообщения пользовательских ошибок в компоненте связывания терпит неудачу. Существует ошибка (Угловой материал показывает красную строку под полем ввода), но сообщение, которое принадлежит этой ошибке, не отображается.


Когда это работает?

Сообщения об ошибках отображаются, когда я не перехватываю ng-сообщения, но помещаю их в строку с html компонента. Единственная проблема здесь в том, что имя формы и имя ввода динамические (определенные с помощью привязок компонентов), поэтому мне нужно использовать this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error'], что я реально не хочу использовать (исходит от GitHub AngularJS issues).


Как выглядит:?

Angular usage of component

В WorkHoursController проверить даты на валидность. Если один из них неверен, я устанавливаю $setValidity входного поля подформы. Вот почему я использую имена динамической формы и формы (также есть более 1 datepicker.html, переведенный в рабочие часы .html, поэтому я не могу использовать статические имена).


То, что я хочу (и не работает)

Transcluding весь нг-сообщение DIV в компонент, без this.$eval()

workhours.html

<div layout="column" layout-fill ng-cloak> 
    <md-content> 
     <form name="bigForm" novalidate> 
      <div class="formCard md-whiteframe-2dp"> 
       <h4>Workhours</h4> 
       <date-picker title="Select workhours" 
             model="$ctrl.time.workhour" 
             input-name="workhour"> 
        <div ng-messages="workhourForm.workhour.$error"> 
         <div ng-message="inFuture">You cannot plan workhours in the future</div> 
        </div> 
       </date-picker> 
      </div> 
     </form> 
    </md-content> 
</div> 

datePicker.html

<ng-form name="{{$ctrl.inputName}}Form"> 
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()"> 
     <input ng-model="$ctrl.formattedModel" 
       aria-label="Open dateTimePicker" 
       name="{{$ctrl.inputName}}" 
       readonly 
       ng-transclude> 
    </md-input-container> 
</ng-form> 

Когда делают это таким образом, сообщение об ошибке не будет показано. Только красная линия. Таким образом, что-то происходит неправильно.

Missing error message


Я могу думать 2 workarrounds. Оба имеют свои недостатки ..

Вариант 1 - Рабочий код

Определение нг-сообщений в самом компоненте

Datepicker.html

<ng-form name="{{$ctrl.inputName}}Form"> 
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()"> 
     <input ng-model="$ctrl.formattedModel" 
       aria-label="Open dateTimePicker" 
       name="{{$ctrl.inputName}}" 
       readonly> 
     <div ng-messages="this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error']"> 
      <div ng-message="inFuture">You cannot plan workhours in the future</div> 
     </div> 
    </md-input-container> 
</ng-form> 

Этот путь все сообщения определены в Datepicker.html. Из-за этого я не могу добавить какие-либо пользовательские сообщения, и я могу использовать только предопределенные сообщения. Это не то, что я хочу, так как этот компонент можно использовать в нескольких ситуациях, причем каждая ситуация имеет свои собственные бизнес-правила.


Вариант 2 - Рабочий код (с 1 ошибкой)

Transcluding нг-сообщения в компоненте

datepicker.html

<ng-form name="{{$ctrl.inputName}}Form"> 
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()"> 
     <input ng-model="$ctrl.formattedModel" 
       aria-label="Open dateTimePicker" 
       name="{{$ctrl.inputName}}" 
       readonly> 
     <div ng-messages="this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error']" ng-transclude> 

     </div>  
    </md-input-container> 
</ng-form> 

workhours .html

<div layout="column" layout-fill ng-cloak> 
    <md-content> 
     <form name="bigForm" novalidate> 
      <div class="formCard md-whiteframe-2dp"> 
       <h4>workhours</h4> 
       <date-picker title="Select workhours" 
             model="$ctrl.time.workhours" 
             input-name="workhours"> 
        <div ng-message="inFuture">You cannot plan workhours in the future</div>    
       </date-picker> 
      </div> 
     </form> 
    </md-content> 
</div> 

С помощью этой опции в нг-сообщения являются динамическими и все, что вы хотите, может быть добавлен. Но класс md-input-message-animation не добавлен. Что приводит к большому тексту и без анимации сообщения об ошибке. Этот класс может быть добавлен вручную, но в переходе что-то происходит не так.

(левая Неправильно, право справа) Image that shows what class is missing


Мои вопросы

Я не могу понять две вещи. Может ли кто-нибудь помочь мне на любом из этих двух?

  1. Как я transclude нг-сообщения не делая мой компонент в директиву (помещая директиву в между HTML и компонентом вариант)?
  2. Как я могу удалить использование this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error'] и все еще сделать код работы с динамической формой и именами форм?

Заранее спасибо.

ответ

1

После прочтения некоторых других сообщений и попыток многого материала я нашел ответ на мои вопросы.

Решение на вопрос 1

Это проблема материального дизайна. Я мог бы исправить это с помощью функции $postLink в моем компоненте выбора даты. Однако эта проблема не является ответственностью этого компонента. Таким образом, решение, которое также соответствовало бы объектно-ориентированным принципам, мне нужно было бы создать директиву, которая добавляет недостающий класс к каждому переводимому элементу.

директива (заказ нг-сообщение)

class CustomNgMessageLinkController { 
    constructor(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) { 
     element.addClass('md-input-message-animation'); 
    } 
} 

function CustomNgMessageDirective(): ng.IDirective { 
    return { 
     restrict: 'A', 
     scope: {}, 
     link: CustomNgMessageLinkController 
    }; 
} 

С помощью этой директивы можно сделать следующее:

<div custom-ng-message ng-message="inFuture">You cannot plan workhours in the future</div> 

Это transclude к ожидаемому результату:

enter image description here


Решение на вопрос 2

я на основе этого решения на 'вариант 2 - Рабочий код (с 1 ошибкой)' пример в моем вопросе. Чтобы выполнить эту работу, мне пришлось создать и добавочное свойство области (formName), создать имя формы в контроллере (а не в HTML, как я это делал ранее), и добавить дополнительную функцию в контроллер, который вернул поле (объект ng.INgModelController).

Я добавил в конструкторе компонента дата-подборщика:

constructor(private $scope: ng.IScope) { 
    this.formName = this.inputName + 'Form'; 
} 

И новый метод в компоненте дата-подборщика:

/** 
* Gets the form of the scope and returns the input field 
* @return {ng.INgModelController} 
*/ 
getFormInput(): ng.INgModelController { 
    let formName = this.inputName + 'Form'; 
    return this.$scope[formName][this.inputName]; 
} 

Этот метод делает почти такой же, как у this.$eval. Единственное различие заключается в том, что он не создает проблему безопасности.

Теперь я могу использовать formName в HTML так:

<ng-form name="{{$ctrl.formName}}"> 
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()"> 
     <input ng-model="$ctrl.formattedModel" 
       aria-label="Open dateTimePicker" 
       name="{{$ctrl.inputName}}" 
       readonly> 
     <div ng-messages="$ctrl.getFormInput().$error" ng-transclude> 
     </div> 
    </md-input-container> 
</ng-form> 

Это позволяет мне использовать динамические формы и названия входов. Это полезно, потому что у меня есть более одного из этих компонентов на одной странице.


Как установить ошибку

В моем WorkHours контроллер я могу проверить MomentJS даты и установить сообщение об ошибке, как так:

constructor($scope: IWorkHourScope) { 
    $scope.$watch(() => { 
     return this.time.workhour; 
    }, (newValue: Moment, oldValue: Moment) => { 
     if (newValue > moment()) {      
      $scope.timeSheetForm.workHourForm.workhour.$setValidity('inFuture', false); 
     } 
    }); 
} 

Кроме того, это делает создавая требуемые интерфейсы для моих элементов формы намного проще, так как все формы имеют разные имена, я могу назначить правильные свойства для каждой подформы.

interface IWorkDateForm { 
    workhour: ng.INgModelController; 
} 

interface IIllNessDateForm { 
    illness: ng.INgModelController; 
} 

interface ITimeForm extends ng.IFormController { 
    workhourForm: IWorkDateForm; 
    illNessForm: IIllNessDateForm;  
} 

interface ITimeSheetScope extends ng.IScope { 
    timeForm: ITimeForm; 
} 

Таким образом, я не должен использовать «any» для моих форм, и я могу автозаполнение из библиотеки AngularJS.