如何使Angular保持两个字段同步?

时间:2015-05-30 18:53:00

标签: angularjs

我正在学习Angular。作为练习,我正在转换使用jQuery的现有应用程序来使用Angular。

在我的页面上,我有两个输入。一个是组合框;另一个是文本输入。

当用户更改组合框中的选择时,文本输入将填充用户选择的文本,除非用户在组合框中选择名为“自定义”的条目。当用户选择“自定义”时,文本输入被清除,焦点自动移动到文本输入,供用户键入自定义值。

每当用户手动将焦点移动到文本输入并键入某些内容时,组合框的值会自动更改为“自定义”。

在Angular中进行此操作的适当方法是什么?

我想如果我有两个相同的文本输入,我可以将它们绑定到同一个模型,但这是不同的。我猜我必须捕获事件并手动更新模型,但我想听听更多有经验的人。

2 个答案:

答案 0 :(得分:5)

您可以在控制器的$watch内包含一些逻辑,也可以更喜欢ng-clickng-change等指令。

我认为Nikos Paraskevopoulos的答案很好,但它并没有真正解答你关于聚焦/清除输入的问题。

这是我实现这一目标的一种方式。首先是HTML模板。

<input type="text"
       ng-model="vm.selected.value"
       ng-change="vm.inputChanged()"
       focus-when-empty>

<select ng-model="vm.selected.option" 
        ng-change="vm.selectionChanged()"
        ng-options="option for option in vm.options">
</select>

这很清楚。文本inputselection有单独的模型,可供选择的一些选项和两者的ng-change处理程序。控制器可能类似于

app.controller('MainController', function() {
  var vm = this;

  // your options to select from
  vm.options = ['custom','one','two','three'];

  // current text input value and dropdown selection
  vm.selected = {
    value: null,
    option: null
  };

  // handle text input
  vm.inputChanged = function() {
    var index = vm.options.indexOf(vm.selected.value);
    vm.selected.option = index > 0 ? vm.options[index] : vm.options[0];
  };

  // handle dropdown
  vm.selectionChanged = function() {
    var index = vm.options.indexOf(vm.selected.option);
    vm.selected.value = index > 0 ? vm.selected.option : null;
  };
});

当选择“自定义”时,专注于文本输入,有点棘手,所以它通过简单的focus-when-empty指令处理。

app.directive('focusWhenEmpty', function() {
  return {
    restrict: 'A',
    scope: {
      ngModel: '='
    },
    link: function(scope, element) {
      // if there is no model, focus on element
      scope.$watch('ngModel', function(value) {
        if (!value) {
          element[0].focus();
        }
      });
    }
  };
});

另外一个额外的好处是,如果您在选项中键入任何值,则会相应地更新选择(因此它不再是“自定义”)。

这是相关的plunker,希望它有所帮助! http://plnkr.co/edit/uJeV5L

imgur

答案 1 :(得分:0)

很好的练习:)

传统的解决方案是使用手表并在输入上使用更改事件:

app.controller('Ctrl', function($scope) {
    var self = this, changeIsInternal = false;

    this.selection = null;
    this.options = [
        'custom',
        'alpha',
        'beta'
    ];
    this.custom = null;

    this.customChanged = function() {
        if( this.selection !== 'custom' ) {
            this.selection = 'custom';
            changeIsInternal = true;
        }
    };

    $scope.$watch(
        function() {
            return self.selection;
        },
        function(newval, oldval) {
            if( newval !== oldval ) { // needed to skip initialization
                if( changeIsInternal ) {
                    changeIsInternal = false;
                }
                else {
                    if( newval !== 'custom' ) {
                        self.custom = newval;
                    }
                    else {
                        self.custom = '';
                    }
                }
            }
        }
    );
});

及其相关的HTML:

<div ng-controller="Ctrl as ctrl">
    <select ng-model="ctrl.selection" ng-options="o for o in ctrl.options"></select>
    <br/>
    <input ng-model="ctrl.custom" ng-change="ctrl.customChanged()" focus-when="ctrl.selection === 'custom'" />
    <br/>
    <pre><code>{{ ctrl | json }}</code></pre>
</div>

另一个解决方案是使用自定义对象属性;这使用 NO WATCHES ,我相信 Angular 2友好

app.controller('Ctrl2', (function() {
    function Ctrl2() {
        this._selection = null;
        this.options = [
            'custom',
            'alpha',
            'beta'
        ];
        this._custom = null;
    }

    Object.defineProperty(Ctrl2.prototype, 'selection', {
        get: function() { return this._selection; },
        set: function(value) {
            if( value !== 'custom' ) {
                this._custom = value;
            }
            else {
                this._custom = '';
            }
            this._selection = value;
        }
    });

    Object.defineProperty(Ctrl2.prototype, 'custom', {
        get: function() { return this._custom; },
        set: function(value) {
            this._selection = 'custom';
            this._custom = value;
        }
    });

    return Ctrl2;
})());

HTML是相同的,省略了ng-change部分。

编辑:设定焦点...... ) 在这两种情况下,我建议使用以下指令来设置焦点:

app.directive('focusWhen', function($parse) {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, elem, attrs) {
            scope.$watch(
                $parse(attrs.focusWhen),
                function(newval, oldval) {
                    if( newval && newval !== oldval ) {
                        elem[0].focus();
                    }
                }
            );
        }
    };
});

如果if( newval !== oldval )的初始值为selection,则"custom"部分是为了防止焦点窃取。

这是一个更新的小提琴:http://jsfiddle.net/6dn55t5w/1/

编辑评论...... ) Mikko Viitala的答案有趣地显示了用于监听选择框更改的ng-change版本。这可能更适合这个问题(而不是使用手表),因为OP指出:&#34;当用户更改组合框中的选择时#34;。当模型也可以以编程方式更改时使用手表,因为在这种情况下不会触发更改事件。如果确保只有UI可以更改值,请使用ng-change

我仍然觉得Object.defineProperty版本很有趣,因为它可以在没有手表的情况下处理UI和程序化更改。