AngularJS - Jak programowo utworzyć nowy, izolowany zakres?

81

Chcę utworzyć AlertFactory za pomocą Angular.factory. Zdefiniowałem szablon HTML w następujący sposób

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

Tytuł jest dostarczany przez wywołujący kontroler i stosowany w następujący sposób

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

Jak więc mogę przekazać izolowany zakres ze sterownika do fabryki? Używam w kontrolerze wykonaj kod

AlertFactory.open($scope);

Ale $ scope to globalna zmienna o zasięgu kontrolera. Chcę tylko przekazać mały zakres dla fabryki z samą własnością tytułu.

Dziękuję Ci.

Premier
źródło

Odpowiedzi:

107

Możesz ręcznie utworzyć nowy zakres.

Możesz utworzyć nowy zakres, $rootScopejeśli go wstrzykniesz, lub po prostu z zakresu kontrolera - nie powinno to mieć znaczenia, ponieważ będziesz go izolować.

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

AlertFactory.open(alertScope);

Klucz jest tutaj przekazywany truedo $new, który akceptuje jeden parametr dla isolate, co pozwala uniknąć dziedziczenia zakresu po rodzicu.

Więcej informacji można znaleźć pod adresem : http://docs.angularjs.org/api/ng.$rootScope.Scope#$new

Alex Osborn
źródło
10
Jeśli ręcznie utworzysz nowy lunetę, prawdopodobnie będziesz musiał go również zniszczyć ręcznie. Zwykle lepiej nie tworzyć zakresów ręcznie.
Mark Rajcok
Zrobiłem test, ale to nie działa. Proszę spojrzeć na stackoverflow.com/questions/15565462/ ...
Premier
3
@MarkRajcok wspomniałeś, że zwykle lepiej nie tworzyć zakresów ręcznie. Jaka jest jednak alternatywa, jeśli chcesz dynamicznie tworzyć kod HTML i chcesz użyć dyrektywy angular w tym html?
Lostintranslation
Oszczędza życie! Musiałem to zrobić, ponieważ pisałem opakowanie dla Angular Bootstrap Modal .
Jonathan
9
Kiedy tworzysz zakres podrzędny (izoluj lub nie izoluj), Angular automatycznie niszczy go, gdy jego element nadrzędny zostanie zniszczony. Tak więc w tym przykładzie, kiedy $scopezostanie zniszczony, alertScopezostanie również automatycznie zniszczony. Tak więc @MarkRajcok jest to doskonale uzasadniony przypadek użycia i całkowicie bezpieczny.
jkjustjoshing
22

Jeśli potrzebujesz tylko interpolacji, użyj usługi $ interpolate zamiast $ compile, a wtedy nie będziesz potrzebować zakresu:

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
        }
    }
});

Kontroler testowy:

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

Skrzypce

Mark Rajcok
źródło
2
Jaka jest różnica między $ compile i $ interpolate? $ interpolate zamienia tylko tekst?
Premier
3
@Premier, to jest moje zrozumienie. Zobacz także stackoverflow.com/a/13460295/215945 Jeśli Twój szablon zawiera dyrektywy, to $ interpolate nie zadziała - musisz użyć do tego $ compile.
Mark Rajcok,
Ok dziękuję. To nie jest wystarczające do mojego zadania, potrzebuję kontrolera z pełnym zakresem.
Premier
1
@Premier, sugeruję, abyś spróbował stworzyć skrzypce lub plunker tego, co próbujesz. Nie jest jasne, skąd pochodzi twój izolowany zakres.
Mark Rajcok
O tak, zrobiłem jeden stackoverflow.com/questions/15565462/ ...
Premier
2

Oto kroki:

  1. Dodaj swój kod HTML do DOM za pomocą var comiledHTML = angular.element(yourHTML);
  2. Utwórz nowy zakres, jeśli chcesz var newScope = $rootScope.$new();
  3. Wywołaj $ comile (); funkcja, która zwraca funkcję łączavar linkFun = $compile(comiledHTML);
  4. Powiąż nowy zakres, wywołując linkFun var finalTemplate = linkFun(newScope);
  5. Dołącz finalTemplate do swojego DOM YourHTMLElemet.append(finalTemplate);
Amit Prabhu Parrikar
źródło
1
Od var linkFun = $compile(comiledHTML);kroku 2
iamdevlinph
2

sprawdź mój plunkr. Programowo generuję dyrektywę widgetów z dyrektywą renderującą.

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);
    }
  }
}
Richard Lin
źródło
1

Zakładam, że kiedy mówisz o zakresie izolowanym, masz na myśli dyrektywę.

Oto przykład, jak to zrobić. 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() {

        }
    };

});

Zasadniczo należy dołączyć kontroler do dyrektywy, która zdefiniowała izolowany zakres. Zakres wstrzyknięty do kontrolera dyrektywy będzie zakresem izolowanym. W kontrolerze dyrektywy możesz wstrzyknąć swoją AlertFactory, do której możesz przekazać zakres izolacji.

rgaskill
źródło
grrr..plnkr.co wydaje się być teraz offline. Miejmy nadzieję, że link nadal działa, gdy wróci.
rgaskill
Mmmm, myślę, że to nie jest dobre rozwiązanie dla mnie, nie potrzebuję dyrektywy. Potrzebuję możliwości fabrycznych, aby wyświetlić modalne okno dialogowe ostrzeżenia. Muszę więc dołączyć szablon html do nakładki dom i użyć kontrolera do zarządzania danymi w widoku alertu.
Premier
2
Twierdziłbym, że nie powinieneś manipulować domem w fabryce. Mieszasz logikę widoku z logiką biznesową. Możesz zrobić dokładnie to, co opisałeś w dyrektywie, w której powinna mieć miejsce manipulacja domem.
rgaskill
Zobacz odpowiedź Brada Greena dla Andy'ego dotyczącą korzystania z usługi dla modalu, a nie dyrektywy: blog.angularjs.org/2012/11/about-those-directives.html
Mark Rajcok
... Ale w tym konkretnym przypadku bootstrap myślę, że najlepiej rozwiązać ten problem za pomocą dyrektywy. plnkr.co/edit/z4J8jH?p=preview
rgaskill