AngularJS - Как я могу программно создать новую изолированную область видимости?


81

Я хочу создать AlertFactory с Angular.factory. Я определил шаблон html, например, follow

var template = "<h1>{{title}}</h1>";

Заголовок предоставляется вызывающим контроллером и применяется следующим образом

var compiled = $compile(template)(scope);
body.append(compiled);

Итак, как я могу передать изолированную область видимости от контроллера к фабрике? Я использую в контроллере следующий код

AlertFactory.open($scope);

Но $ scope - это глобальная переменная области действия контроллера. Я просто хочу передать небольшую область для factory с одним свойством title.

Спасибо.

Ответы:


107

Вы можете создать новую область действия вручную.

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

var alertScope = $scope.$new(true);
alertScope.title = 'Hello';

AlertFactory.open(alertScope);

Ключевым моментом здесь является переход trueк $new, который принимает один параметр для isolate, что позволяет избежать наследования области видимости от родителя.

Дополнительную информацию можно найти по адресу: http://docs.angularjs.org/api/ng.$rootScope.Scope#$new.


10
Если вы создаете новый прицел вручную, вам, вероятно, также придется вручную его уничтожить. Обычно лучше не создавать области вручную.
Mark Rajcok

Я сделал тест, но он не работает. См. Stackoverflow.com/questions/15565462/…
Премьер

3
@MarkRajcok, вы упомянули, что обычно лучше не создавать области вручную. Однако какова альтернатива, если вам нужно динамически создавать html и вы хотите использовать директиву angular в этом html?
lostintranslation

Спасатель жизни! Пришлось сделать это, так как я писал оболочку для Angular Bootstrap Modal .
Джонатан

9
Когда вы создаете дочернюю область видимости (изолировать или не изолировать), Angular автоматически уничтожает ее, когда ее родительская область уничтожается. Итак, в этом примере, когда $scopeуничтожается alertScope, автоматически уничтожается. Итак, @MarkRajcok - это вполне допустимый вариант использования и совершенно безопасный.
jkjustjoshing

22

Если вам нужно только интерполировать вещи, используйте службу $ interpolate вместо $ compile, и тогда вам не понадобится область видимости:

myApp.factory('myService', function($interpolate) {
    var template = "<h1>{{title}}</h1>";
    var interpolateFn = $interpolate(template);
    return {
        open: function(title) {
            var html = interpolateFn({ title: title });
            console.log(html);
            // append the html somewhere
        }
    }
});

Контроллер тестирования:

function MyCtrl($scope, myService) {
    myService.open('The Title');
}

Скрипка


2
В чем разница между $ compile и $ interpolate? $ interpolate заменяет только текст?
Премьера

3
@Premier, я так понимаю. См. Также stackoverflow.com/a/13460295/215945 Если ваш шаблон содержит директивы, то $ interpolate не будет работать - для этого нужно использовать $ compile.
Mark Rajcok

Спасибо. Для моей задачи этого недостаточно, мне нужен контроллер с полным набором функций.
Премьера

1
@Premier, я предлагаю вам попробовать создать скрипку или плунжер из того, что вы пытаетесь. Непонятно, откуда взялся ваш изолирующий прицел.
Mark Rajcok

О да, я сделал один stackoverflow.com/questions/15565462/…
Премьер

2

Следующие шаги:

  1. Добавьте свой HTML-код в DOM с помощью var comiledHTML = angular.element(yourHTML);
  2. Создайте новую область, если хотите var newScope = $rootScope.$new();
  3. Вызов $ comile (); функция, которая возвращает функцию ссылкиvar linkFun = $compile(comiledHTML);
  4. Свяжите новую область, вызвав linkFun var finalTemplate = linkFun(newScope);
  5. Добавьте finalTemplate в вашу DOM YourHTMLElemet.append(finalTemplate);

1
From var linkFun = $compile(comiledHTML);on step 2
iamdevlinph

2

проверьте мой plunkr. Я программно генерирую директиву виджета с директивой рендеринга.

https://plnkr.co/edit/5T642U9AiPr6fJthbVpD?p=preview

angular
  .module('app', [])
  .controller('mainCtrl', $scope => $scope.x = 'test')
  .directive('widget', widget)
  .directive('render', render)

function widget() {
  return {
    template: '<div><input ng-model="stuff"/>I say {{stuff}}</div>'
  }
}

function render($compile) {
  return {
    template: '<button ng-click="add()">{{name}}</button><hr/>',
    link: linkFn
  }

  function linkFn(scope, elem, attr) {
    scope.name = 'Add Widget';
    scope.add = () => {
      const newScope = scope.$new(true);
      newScope.export = (data) => alert(data);
      const templ = '<div>' +
                      '<widget></widget>' +
                      '<button ng-click="export(this.stuff)">Export</button>' +
                    '</div>';
      const compiledTempl = $compile(templ)(newScope);
      elem.append(compiledTempl);
    }
  }
}

1

Я предполагаю, что когда вы говорите об изолированной области видимости, вы говорите о директиве.

Вот пример того, как это сделать. http://jsfiddle.net/rgaskill/PYhGb/

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

app.controller('TestCtrl', function ($scope) {
    $scope.val = 'World';
});

app.factory('AlertFactory', function () {

    return {
        doWork: function(scope) {
            scope.title = 'Fun';    
            //scope.title = scope.val;  //notice val doesn't exist in this scope
        }
    };

});

app.controller('DirCtrl', function ($scope, AlertFactory) {
    AlertFactory.doWork($scope);  
});

app.directive('titleVal',function () {
    return {
        template: '<h1>Hello {{title}}</h1>',
        restrict: 'E',
        controller: 'DirCtrl',
        scope: {
            title: '='
        },
        link: function() {

        }
    };

});

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


grrr..plnkr.co, похоже, сейчас не в сети. Надеюсь, ссылка все еще будет работать, когда она вернется.
rgaskill

Мммм, я думаю, это не лучшее решение для меня, мне не нужна директива. Мне нужны фабричные возможности для отображения модального диалогового окна с предупреждениями. Поэтому мне нужно прикрепить шаблон html в наложении dom и использовать контроллер для управления данными в представлении предупреждений.
Премьера

2
Я бы сказал, что вам не следует манипулировать домом на фабрике. Вы смешиваете логику представления с бизнес-логикой. Вы можете сделать именно то, что вы описали в директиве, в которой должно происходить манипулирование dom.
rgaskill

См. Ответ Брэда Грина Энди относительно использования службы для модального окна
Mark Rajcok

... Но для этого конкретного случая начальной загрузки, я думаю, лучше всего решить эту проблему с помощью директивы. plnkr.co/edit/z4J8jH?p=preview
rgaskill
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.