Подтвердить что ты не робот

Ng-repeat с контроллером для каждой строки таблицы: как мне получить доступ к x-редактируемым элементам формы?

Я создаю сценарий, очень похожий на пример Editable Row на x-editable site. В этом случае существует простая таблица с тремя столбцами для данных и четвертая для кнопок редактирования и удаления. Третья кнопка за пределами таблицы добавляет строку в таблицу. Когда форма редактируется, столбцы данных становятся редактируемыми (основная особенность x-редактируемой библиотеки). Для этой демонстрации первый столбец становится простым текстовым редактированием, а два вторых столбца становятся списками.

Таблица создается с помощью ng-repeat в шаблоне строки. Мне нужно сделать несколько разных вещей, которые включают в себя доступ к области, созданной ng-repeat. Мне нужно

  • обнаруживает, когда строка редактируется, а когда нет
  • отфильтровать параметры для второго списка при первом изменении списка.

Чтобы попытаться работать с этой демонстрацией, я добавил контроллер для отдельной строки. Это дало мне некоторый доступ к форме (name = rowform), но я все еще не могу установить часы на свойство "make". Я даже не могу найти, какое свойство формы изменяется, когда пользователь делает выбор.

Как настроить часы на свойство "make"?

Контроллер страницы

    angular.module('app').controller("quoteBuckingRaterController",
    function ($scope, $q, $filter, listService, transactionDataService) {

        $scope.equipment = []; 
        $scope.makes = []; 
        $scope.models = [];

        $scope.showModel = function(equip) {
            if(equip.model) {
                var selected = $filter('filter')($scope.models, {id: equip.model});
                return selected.length ? selected[0].name : 'Not set';
            } else {
                return 'Not set';
            }
        };

        $scope.showMake = function(equip) {
            if (equip.model) {
                var selected = $filter('filter')($scope.models, { id: equip.model });
                if (selected.length && selected.length > 0) {
                    if (equip.make != selected[0].make)
                        equip.make = selected[0].make;
                    return selected[0].make;
                }
                else {
                    return 'Not set';
                }
            } else {
                return 'Not set';
            }
        };

        $scope.checkName = function (data, id) {
            if (!data) {
                return "Description is required";
            }
        };

        $scope.checkModel = function (data, id) {
            if (!data) {
                return "Model is required";
            }
        };

        $scope.saveEquipment = function (data, id) {
            $scope.inserted = null;
        };

        $scope.cancelRowEdit = function (data, id) {
            $scope.inserted = null;
        };

        $scope.removeEquipment = function(index) {
            $scope.equipment.splice(index, 1);
        };

        $scope.addEquipment = function() {
            $scope.inserted = {
                id: $scope.equipment.length+1,
                name: '',
                make: null,
                model: null 
            };
            $scope.equipment.push($scope.inserted);
        };

        $scope.filterModels = function (make) {
            $scope.models = _.where($scope.allModels, function(item) {
                return item.make == make;
            });
        };

        //called by another process when page loads
        $scope.initialize = function (loaded) {
            return $q(function (resolve, reject) {
                if (!loaded) {
                    listService.getEquipmentModels().then(function (data) {
                        $scope.allModels = data;
                        $scope.models = data;

                        //uses underscore.js
                        $scope.makes = _.chain(data)
                                        .map(function (item) {
                                            var m = {
                                                id: item.make,
                                                name: item.make
                                            };
                                            return m;
                                        })
                                        .uniq()
                                        .value();                            
                        resolve();
                    });
                }
            });
        }
    });

Регулятор строк

angular.module('app').controller("editRowController",
function ($scope) {
    $scope.testClick = function () {
        alert('button clicked');
    };

    $scope.make = null;

    $scope.$watch('make', function () {
        alert('how do I tell when the make has been changed?');
        this.$parent.$parent.filterModels(make.id);
    });
});

HTML

<div>
    <div class="col-md-12" style="margin-bottom: 3px">
        <div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
        <div class="col-md-offset-10">
            <button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
        </div>
    </div>
    <div class="col-md-10 col-md-offset-1">    
        <table class="table table-bordered table-hover table-condensed">
            <tr style="font-weight: bold; background-color: lightblue">
                <td style="width:35%">Name</td>
                <td style="width:20%">Make</td>
                <td style="width:20%">Model</td>
                <td style="width:25%">Edit</td>
            </tr>
            <tr ng-repeat="equip in equipment" ng-controller="editRowController">
                <td>
                    <!-- editable equip name (text with validation) -->
                    <span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
                        {{ equip.name || 'empty' }}
                    </span>
                </td>
                <td>
                    <!-- editable make (select-local) -->
                    <span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
                        {{ showMake(equip) }}
                    </span>
                </td>
                <td>
                    <!-- editable model (select-remote) -->
                    <span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
                        {{ showModel(equip) }}
                    </span>
                    <button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
                        test
                    </button>
                </td>
                <td style="white-space: nowrap">
                    <!-- form -->
                    <form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip">
                        <button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
                            save
                        </button>
                        <button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
                            cancel
                        </button>
                    </form>
                    <div class="buttons" ng-show="!rowform.$visible">
                        <button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
                        <button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
                    </div>
                </td>
            </tr>
        </table>
    </div>
</div>
4b9b3361

Ответ 1

ng-repeat создает дочернюю область для каждой строки (для каждого equipment). Таким образом, область EditRowController является дочернимScope родительского quoteBuckingRaterController.

Этот childScope содержит:

  • все свойства родительской области (например, equipment, makes, models)
  • свойство equip с одним значением массива equipment, предоставленным ng-repeat
  • любое дополнительное свойство области, которое определено внутри блока ng-repeat, например. rowform

Поэтому вы можете получить доступ к этим свойствам в childController EditRowController с помощью переменной $scope, например:

$scope.equip.make
$scope.equipment

и внутри элемента ng-repeat в html файле с помощью выражения angular, например:

{{equip.make}}
{{equipment}}

Теперь $scope.$watch: если вы предоставили строку в качестве первого аргумента, это выражение angular, как в html файле, без окружающих скобок {{}}. Пример для equip.make:

$scope.$watch('equip.make', function (value) {
     console.log('equip.make value (on save): ' + value);
});

Однако angular -xeditable обновляет значение equip.make только тогда, когда пользователь сохраняет строку. Если вы хотите смотреть пользовательский вход вживую, вы должны использовать свойство $data в объекте rowform, предоставляемое angular -xeditable:

$scope.$watch('rowform.$data.make', function (value) {
    console.log('equip.make value (live): ' + value);
}, true);

Вы также можете использовать ng-change:

<span editable-select="equip.make" e-name="make" e-ng-change="onMakeValueChange($data)" e-form="rowform" e-ng-options="s.value as s.name for s in makes">

JS:

$scope.onMakeValueChange = function(newValue) {
    console.log('equip.make value onChange: ' + newValue);
}

Это должно решить ваш первый вопрос: как посмотреть свойство make.

Второй вопрос, как определить, когда строка редактируется, а когда нет, может быть решена с помощью атрибутов onshow/onhide в форме или путем просмотра свойства $visible объекта rowform в области видимости как задокументированный в angular -используемой ссылке

<form editable-form name="rowform" onshow="setEditable(true)" onhide="setEditable(false)">

$scope.setEditable = function(value) {
      console.log('is editable? ' + value);
};

// or
$scope.$watch('rowform.$visible', function(value) {
  console.log('is editable? ' + value);
});

Вы можете спросить, почему объект rowform находится в текущем childScope. Он создается тегом <form>. См. ссылку angular о встроенной директиве формы:

Директива, которая создает экземпляр FormController.

Если указан атрибут name, публикуется контроллер формы на текущую область под этим именем.

Рабочий фрагмент с кодом примера:

angular.module('app', ["xeditable"]);

angular.module('app').controller("editRowController", function ($scope) {
    $scope.testClick = function () {
        alert('button clicked');
    };

    $scope.$watch('equip.make', function (value) {
        console.log('equip.make value (after save): ' + value);
    });
  
    $scope.$watch('rowform.$data.make', function (value) {
        console.log('equip.make value (live): ' + value);
    }, true);
  
    // detect if row is editable by using onshow / onhide on form element
    $scope.setEditable = function(value) {
      console.log('is equip id ' + $scope.equip.id + ' editable? [using onshow / onhide] ' + value);
    };
  
    // detect if row is editable by using a watcher on the form property $visible
    $scope.$watch('rowform.$visible', function(value) {
      console.log('is equip id ' + $scope.equip.id + ' editable [by watching form property]? ' + value);
    });
});


angular.module('app').controller("quoteBuckingRaterController", function ($scope, $filter) {
    $scope.equipment = []; 
    $scope.makes = [{value: 1, name: 'Horst'}, {value: 2, name: 'Fritz'}]; 
    $scope.models = [{id: 1, name: 'PC', make: 1}];

    $scope.showModel = function(equip) {
        if(equip.model) {
            var selected = $filter('filter')($scope.models, {id: equip.model});
            return selected.length ? selected[0].name : 'Not set';
        } else {
            return 'Not set';
        }
    };

    $scope.showMake = function(equip) {
        if (equip.model) {
            var selected = $filter('filter')($scope.models, { id: equip.model });
            if (selected.length && selected.length > 0) {
                if (equip.make != selected[0].make)
                    equip.make = selected[0].make;
                return selected[0].make;
            }
            else {
                return 'Not set';
            }
        } else {
            return 'Not set';
        }
    };

    $scope.checkName = function (data, id) {
        if (!data) {
            return "Description is required";
        }
    };

    $scope.checkModel = function (data, id) {
        if (!data) {
            return "Model is required";
        }
    };

    $scope.saveEquipment = function (data, id) {
        $scope.inserted = null;
    };

    $scope.cancelRowEdit = function (data, id) {
        $scope.inserted = null;
    };

    $scope.removeEquipment = function(index) {
        $scope.equipment.splice(index, 1);
    };

    $scope.addEquipment = function() {
        $scope.inserted = {
            id: $scope.equipment.length+1,
            name: '',
            make: null,
            model: null 
        };
        $scope.equipment.push($scope.inserted);
    };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/js/xeditable.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/css/xeditable.css" rel="stylesheet"/>
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<div ng-app="app" ng-controller="quoteBuckingRaterController">
    <div class="col-md-12" style="margin-bottom: 3px">
        <div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
        <div class="col-md-offset-10">
            <button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
        </div>
    </div>
    <div class="col-md-10 col-md-offset-1">    
        <table class="table table-bordered table-hover table-condensed">
            <tr style="font-weight: bold; background-color: lightblue">
                <td style="width:35%">Name</td>
                <td style="width:20%">Make</td>
                <td style="width:20%">Model</td>
                <td style="width:25%">Edit</td>
            </tr>
            <tr ng-repeat="equip in equipment" ng-controller="editRowController">
                <td>
                    <!-- editable equip name (text with validation) -->
                    <span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
                        {{ equip.name || 'empty' }}
                    </span>
                </td>
                <td>
                    <!-- editable make (select-local) -->
                    <span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
                        {{ showMake(equip) }}
                    </span>
                </td>
                <td>
                    <!-- editable model (select-remote) -->
                    <span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
                        {{ showModel(equip) }}
                    </span>
                    <button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
                        test
                    </button>
                </td>
                <td style="white-space: nowrap">
                    <!-- form -->
                    <form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip" onshow="setEditable(true)" onhide="setEditable(false)">
                        <button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
                            save
                        </button>
                        <button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
                            cancel
                        </button>
                    </form>
                    <div class="buttons" ng-show="!rowform.$visible">
                        <button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
                        <button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
                    </div>
                </td>
            </tr>
        </table>
    </div>
</div>

Ответ 2

Если вы просто хотите $watch свойство make equipment, попробуйте изменить на:

$scope.$watch('equipment.make', function(){(...)})

Ответ 3

Вы можете написать свою собственную директиву для этого.

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

см. директивную документацию, чтобы узнать, если это для вас.