Использование начальной загрузки с компонентами AngularJS не работает, если нужно инкапсулировать внутренние элементы Bootstrap в компонентах, поскольку дополнительная разметка самого компонента, добавленного в DOM, нарушает правила CSS с прямым дочерним оператором >. Например, при реализации DropDown необходимо создать полный компонент с DropDown и создать каждую опцию раскрывающегося списка внутри этого полного компонента с ng-repeat, считывающими данные из массива конфигурации. Что-то вроде этого:

<my-bootstrap-drop-down 
    my-label="Some label" 
    my-options="[ { label: 'Option1 },  {label: 'Option2'} ]" >
</my-bootstrap-drop-down> 

Из Дэна Уолина " Создание настраиваемых директив AngularJS ", чтобы иметь возможность передавать функцию с переменным количеством аргументов компоненту AngularJS, вам понадобится специальный синтаксис, в котором вы передаете ссылку на функцию атрибуту тега элемента следующим образом:

<my-component 
    my-action="myMethod(p1, p2)"
    my-params="{p1:1, p2:25}">
</my-componenet>

А затем в компоненте вы вызываете функцию с помощью этого кода:

<a ng-click="$ctrl.myAction($ctrl.myParams)"></a>

Этот синтаксис работает правильно только при использовании в атрибутах элемента, отображаемых с помощью оператора & как привязки компонента / директивы. Даже когда my-action="myMethod(p1, p2) кажется вызовом функции, на самом деле это передача по ссылке. К сожалению, если вы хотите использовать ng-repeat для генерации некоторого кода внутри компонента, как описано выше, нет никакого способа заставить этот синтаксис работать, поскольку синтаксис myThethod(p1, p2) работает только в атрибуте.

Итак, как вы можете реализовать компонент, имеющий массив внутренних элементов, сгенерированных с помощью ng-repeat, и эти элементы, имеющие вызовы функций с переменным числом аргументов, поскольку более поздний синтаксис не работает?

<my-bootstrap-drop-down 
    my-label="Some label" 
    my-options="[ 
        { label: 'Option1', action: myMethod(p1, p2),  params: {p1:1, p2:25}},  
        ...
    ]" >
</my-bootstrap-drop-down> 

При попытке выполнить этот код myMethod(p1, p2) выполняется при создании компонента, поскольку это фактически вызов функции, а не передача по ссылке.

Примечание . В той же статье, упомянутой выше, предлагается другой синтаксис для вызова функций. Синтаксис предполагает, что компонент знает, сколько аргументов передать, что не так. Его можно было бы использовать в любом случае, передавая аргументы в виде массива и вызывая функцию с помощью apply, но применение не разрешено в выражениях angular.

Я добавил Plunker, чтобы было понятно:

https://plnkr.co/edit/dkofEYhebp0T6lSf22RP?p=preview

1
David Casillas 23 Сен 2018 в 11:13

1 ответ

Лучший ответ

Изменить: Хорошо, не знаю, зачем вам это нужно, но у меня это работает: https://plnkr.co/edit/uR9s5vUJxQoviTiUD2vj?p=preview

И то же самое, но с использованием Директивы: https://plnkr.co/edit/Onh2WonmarpUscnFFLGK?p=preview

Конец редактирования

Вы должны передать переменную в my-options (назовем ее dropDownOptions):

<my-bootstrap-drop-down 
    my-label="Some label" 
    my-options="dropDownOptions" >
</my-bootstrap-drop-down> 

И массив dropDownOptions должен содержать данные, которые вам нужны в директиве, но только данные, а не функцию: [{"label": "Option 1", "params": {"p1": 1, "p2": 25}}, ...]

Теперь внутри вашей директивы у вас есть доступ к данным и вы можете работать над частью действия / функции. Пример:

var testApp = angular.module('testApp', []);


testApp.controller('mainCtrl', ['$scope',
    function ($scope) {

        $scope.test = "Hi";
        $scope.dropDownOptions = [{"name": "yes", "value": 2}, {"name": "no", "value": 25}];

    }]);


testApp.directive('myBootstrapDropDown', function () {
    return {
        restrict: 'E',
        scope: {
            myLabel: '@',
            myOptions: '='
        },
        controller: function ($scope) {

            $scope.myMethod = function (val) {
                alert("There was a change, new value: " + val);
            };
        },
        template: '<label>{{myLabel}}</label> <select name="myLabel" ng-model="myValue" ng-options="opt.value as opt.name for opt in myOptions" ng-change="myMethod(myValue)"><option value=""> </option></select>'
    };
});
<!DOCTYPE html>
<html lang="en" ng-app="testApp">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <title>Sample</title>

    <style>
        .starter-template {
          padding: 10px 15px;
          text-align: center;
        }
        a {
            font-size: 11px;
            cursor: pointer;
        }
    </style>

</head>
<body>
    <div ng-controller="mainCtrl">

        <div class="container">

          <div class="starter-template">
            <h1>Example</h1>
            <p class="lead">{{test}}</p>
            <my-bootstrap-drop-down 
                my-label="Some label" 
                my-options="dropDownOptions" >
            </my-bootstrap-drop-down> 
          </div>

        </div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script src="app.js"></script>
</body>
</html>

Таким образом, вам не нужно связывать функцию с вашей директивой, если только директива не должна запускать что-то обратно в контроллере, и в этом случае вы должны поместить функцию в отдельный атрибут (который в этом случае будет связан с & как вы упомянули).

Например:

var testApp = angular.module('testApp', []);


testApp.controller('mainCtrl', ['$scope',
    function ($scope) {

        $scope.test = "Hi";
        $scope.dropDownOptions = [{"name": "yes", "value": 2}, {"name": "no", "value": 25}];
        $scope.runThis = function (val) {
            //Do Something here
            alert("There was a change, new value: " + val);
        };

    }]);


testApp.directive('myBootstrapDropDown', function () {
    return {
        restrict: 'E',
        scope: {
            myLabel: '@',
            myOptions: '=',
            myFunction: "&"
        },
        controller: function ($scope) {

            $scope.myMethod = function (val) {
                $scope.myFunction()(val);
            };
        },
        template: '<label>{{myLabel}}</label> <select name="myLabel" ng-model="myValue" ng-options="opt.value as opt.name for opt in myOptions" ng-change="myMethod(myValue)"><option value=""> </option></select>'
    };
});
    <!DOCTYPE html>
    <html lang="en" ng-app="testApp">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta charset="utf-8">
        <title>Sample</title>

        <style>
            .starter-template {
              padding: 10px 15px;
              text-align: center;
            }
            a {
                font-size: 11px;
                cursor: pointer;
            }
        </style>

    </head>
    <body>
        <div ng-controller="mainCtrl">

            <div class="container">

              <div class="starter-template">
                <h1>Example</h1>
                <p class="lead">{{test}}</p>
                <my-bootstrap-drop-down 
                    my-label="Some label" 
                    my-options="dropDownOptions"
                    my-function="runThis" >
                </my-bootstrap-drop-down> 
              </div>

            </div>
        </div>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
        <script src="app.js"></script>
    </body>
    </html>

Обратите внимание на «() (val)» в контроллере директив. Если вам не нужно передавать какое-либо значение обратно в исходный контроллер, просто замените его на «() ()».

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

1
BoDeX 24 Сен 2018 в 11:15