AngularJS - Як я можу програмно створити новий, ізольований обсяг?


81

Я хочу створити AlertFactory з Angular.factory. Я визначив шаблон html, як слід

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

Заголовок надається викликом контролера та застосовується наступним чином

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

Отже, як я можу передати ізольований діапазон від контролера до заводу? Я використовую в контролері наступний код

AlertFactory.open($scope);

Але $ scope - це глобальна змінна області контролю контролера. Я просто хочу передати невеликий обсяг для заводу з лише властивістю заголовка.

Дякую.

Відповіді:


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?
загублений переклад

Рятувальник життя! Довелося це зробити, оскільки я писав обгортку для 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


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
З var linkFun = $compile(comiledHTML);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 у накладеному домі та використовувати контролер для управління даними у поданні сповіщення.
Прем'єр-міністр

2
Я стверджую, що вам не слід маніпулювати будинком на заводі. Ви змішуєте свою логіку погляду з вашою бізнес-логікою. Ви можете зробити саме те, що описали в директиві, де має відбуватися маніпуляція будинком.
rgaskill

Дивіться відповідь Бреда Гріна Енді щодо використання служби для модального, а не директивного режиму
Марк Райкок,

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