Правильно ли я разделил состояние вне директивы angular? (проблема с родительской областью / $ rootScope)

У меня есть два элемента на моей странице, которые каким-то образом не вложены друг в друга. С атрибутами первого я хотел бы установить содержимое второго, например:

<body>
    <input info="This is some info"></input>
    <div>{{info}}</div>
</body>

Я знаю, что что-то подобное можно сделать, как описано в «AngularJS — изменение входного значения директивы атрибута", но в моей ситуации содержимое атрибута является значением модели. Более того, я уже использую ng-model для обработки входного значения.

В итоге решил проблему так:

<body>
    <input infobox info="This is some info"></input>
    <div>{{info}}</div>
</body>
app.directive('infobox', function() {
    return {
        restrict : 'A',
        scope : {
            info : '@info'
        },
        controller: function($rootScope, $scope) {
            $rootScope.info = $scope.info;
        }
    };
});

jsfiddle

Поэтому я передаю значение, минуя любую изолированную область, перейдя непосредственно к $rootScope, но это похоже на отговорку. По сути, я не знаю, какой контроллер или область действия могут быть выше по цепочке, поскольку мне нужно иметь возможность использовать этот компонент где угодно.

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

Есть ли «чистый» способ сделать это?

/изменить

Вот чистое решение, которое должно работать (и в jsfiddle оно работает):

<body>
     <div info="this is some info"></div>
     <div>{{info}}</div>
</body>
app.directive('info', function() {
    return {
        restrict : 'A',
        link: function(scope, element, attrs, ngModel) {
            scope.info = attrs.info;
        }
    };
});

jsfiddle.

Однако в моем сценарии реальной жизни значение застряло где-то в цепочке. Возможно ли, что родительская директива с изолированной областью действия полностью блокирует каскад?

/edit2

Вот решение, которое, как я думал, наверняка сработает. Он включает в себя три директивы, одна из которых является родительской для обеих других. Комбинация предложенных решений в комментариях:

jsfiddle

Однако в моем собственном коде это все еще не работает. Как бы просто это ни казалось, infoManager.info остается undefined.


person Benny Bottema    schedule 31.12.2013    source источник
comment
Назначения дочерним областям вообще не каскадируются.   -  person Karolis Juodelė    schedule 01.01.2014
comment
Да, наверное, я имел в виду следующее: как только создается изолированная область, вы фактически делаете значения, добавленные в область, невидимыми для «внешнего» мира.   -  person Benny Bottema    schedule 01.01.2014


Ответы (3)


Есть еще один подход, который я недавно использовал в похожей ситуации (также я возвращаюсь к шаблонам «побег из тюрьмы rootscope» и «выдача/прослушивание событий» для решения других проблем), который может сработать для вас: скажем, у нас есть эта директива:

app.directive('infobox', function() {
    return {
        restrict : 'A',
        scope : {
            info : '@info'
        },
        controller: function($rootScope, $scope) {
            //$rootScope.info = $scope.info;
            this.setInfo = function(data){
              $scope.info = data;
            }
            this.getInfo = function(){
                return $scope.info;
            }

        }
    };
})

теперь я могу добавить еще одну директиву для отображения {{info}}:

с html:

app.directive('showinfo', function(){
   return {
    restrict:'A',
    template:'<div>{{info}}</div>',
    require:'^infobox', //this is where the magic happens
    link:function(scope,element,attrs,parentCtrl){ //notice that now er have a "parentctrl, with everything that is on 'this' in the required direcive
//you can set a watch for this etc..
  scope.info = parentCtrl.getInfo();
  }

 }

})

надеюсь это поможет!

person alonisser    schedule 31.12.2013
comment
Можете ли вы опубликовать скрипку? Это выглядит многообещающе, но я думал, что это работает только в том случае, если (в данном случае) showinfo на самом деле является родителем infobox. - person Benny Bottema; 31.12.2013
comment
Вот что-то, что очень похоже на предложенное вами решение, но учитывает родительский контроллер (как предложил Каролис). jsfiddle.net/8M6m7/3 - person Benny Bottema; 01.01.2014
comment
Решил, обновил мой вопрос окончательным решением. jsfiddle. - person Benny Bottema; 01.01.2014
comment
@Plantface здорово! хорошее решение, также я бы не пропустил всю область применения и предпочел бы, чтобы геттер/сеттер был делом вкуса, я думаю - person alonisser; 01.01.2014

Напишите третью директиву, которая будет действовать либо как маркер для области, которая должна знать о переменных, либо как альтернатива области. В любом случае вам нужно, чтобы третья директива была над {{info}} и infobox, и вам нужно, чтобы две нижние директивы требовали ее в качестве родителя.

person Karolis Juodelė    schedule 31.12.2013
comment
Точно... как $rootScope (или псевдоверсия). На самом деле, применение «require» звучит как хорошее решение. Брб со скрипкой. - person Benny Bottema; 01.01.2014
comment
Что ж, похоже, в jsfiddle он отлично работает, однако в моем собственном коде значение infomanager.info остается пустым. - person Benny Bottema; 01.01.2014
comment
Решил, обновил мой вопрос окончательным решением. jsfiddle. - person Benny Bottema; 01.01.2014

ОК, я понял, наконец. В последнем редактировании он был закрыт, но я читал информацию с контроллера, а не с области, для которой был контроллер.

Решение выглядит следующим образом:

<body>
    <div infomanager>
        <div info="'this is some info'"></div>
        <div showinfo></div>
    </div>
</body>
var app = angular.module('app', []);

app.directive('infomanager', function() {
    return {
        controller: function($scope) {
            this.scope = $scope;
        }
    };
});

app.directive('info', function() {
    return {
        restrict : 'A',
        require: '^infomanager',
        scope : {
            info : '=info'
        },
        link: function(scope, element, attrs, infoManager) {
            infoManager.scope.info = scope.info;
        }
    };
})

app.directive('showinfo', function(){
    return {
        template:'{{info}}',
        require:'^infomanager'
    }
})

jsfiddle

Вот лучший пример, чтобы продемонстрировать это: jsfiddle

person Benny Bottema    schedule 21.07.2016