AngularJS - Wie kann ich programmgesteuert einen neuen, isolierten Bereich erstellen?

81

Ich möchte eine AlertFactory mit Angular.factory erstellen. Ich habe eine HTML-Vorlage wie folgt definiert

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

Der Titel wird vom aufrufenden Controller bereitgestellt und wie folgt angewendet

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

Wie kann ich also einen isolierten Bereich vom Controller an die Fabrik übergeben? Ich verwende im Controller den folgenden Code

AlertFactory.open($scope);

$ Scope ist jedoch eine globale Controller-Scope-Variable. Ich möchte nur einen kleinen Bereich für die Fabrik mit nur Titeleigenschaft übergeben.

Danke dir.

Premier
quelle

Antworten:

107

Sie können einen neuen Bereich manuell erstellen.

Sie können einen neuen Bereich erstellen $rootScope, indem Sie ihn einfügen oder nur aus Ihrem Controller-Bereich - dies sollte keine Rolle spielen, da Sie ihn isoliert machen.

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

AlertFactory.open(alertScope);

Der Schlüssel hier ist die Übergabe truean $new, die einen Parameter für akzeptiert isolate, wodurch vermieden wird, dass der Bereich vom übergeordneten Element übernommen wird.

Weitere Informationen finden Sie unter: http://docs.angularjs.org/api/ng.$rootScope.Scope#$new

Alex Osborn
quelle
10
Wenn Sie einen neuen Bereich manuell erstellen, müssen Sie ihn wahrscheinlich auch manuell zerstören. Normalerweise ist es besser, Bereiche nicht manuell zu erstellen.
Mark Rajcok
Ich habe einen Test gemacht, aber es funktioniert nicht. Bitte schauen Sie auf stackoverflow.com/questions/15565462/…
Premier
3
@MarkRajcok Sie erwähnen, dass es normalerweise besser ist, Bereiche nicht manuell zu erstellen. Was ist jedoch die Alternative, wenn Sie HTML dynamisch erstellen und eine Winkelanweisung in diesem HTML verwenden möchten?
Verlorene Übersetzung
Lebensretter! Musste dies tun, als ich einen Wrapper für Angular Bootstrap Modal schrieb .
Jonathan
9
Wenn Sie einen untergeordneten Bereich erstellen (isolieren oder nicht isolieren), zerstört Angular ihn automatisch, wenn sein übergeordneter Bereich zerstört wird. In diesem Beispiel $scopewird alertScopealso auch automatisch zerstört , wenn es zerstört wird. @MarkRajcok Dies ist also ein absolut gültiger Anwendungsfall und absolut sicher.
jkjustjoshing
22

Wenn Sie nur Dinge interpolieren müssen, verwenden Sie den Dienst $ interpolate anstelle von $ compile, und dann benötigen Sie keinen Bereich:

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

Testregler:

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

Geige

Mark Rajcok
quelle
2
Was ist der Unterschied zwischen $ compile und $ interpolate? $ interpolate lässt nur Text ersetzen?
Premier
3
@Premier, das ist mein Verständnis. Siehe auch stackoverflow.com/a/13460295/215945 Wenn Ihre Vorlage Anweisungen enthält, funktioniert $ interpolate nicht - Sie müssten dafür $ compile verwenden.
Mark Rajcok
OK danke. Es ist nicht genug für meine Aufgabe, ich brauche einen Controller mit vollem Umfang.
Premier
1
@Premier, ich schlage vor, Sie versuchen, eine Geige oder einen Plunker von dem zu erstellen, was Sie versuchen. Es ist nicht klar, woher Ihr isolierter Bereich stammt.
Mark Rajcok
Oh ja, ich habe einen stackoverflow.com/questions/15565462/…
Premier
2

Folgendes sind die Schritte:

  1. Fügen Sie Ihren HTML-Code mithilfe von zum DOM hinzu var comiledHTML = angular.element(yourHTML);
  2. Erstellen Sie einen neuen Bereich, wenn Sie möchten var newScope = $rootScope.$new();
  3. Rufen Sie $ comile () auf; Funktion, die die Link-Funktion zurückgibtvar linkFun = $compile(comiledHTML);
  4. Binden Sie den neuen Bereich, indem Sie linkFun aufrufen var finalTemplate = linkFun(newScope);
  5. Hängen Sie finalTemplate an Ihr DOM an YourHTMLElemet.append(finalTemplate);
Amit Prabhu Parrikar
quelle
1
Ab var linkFun = $compile(comiledHTML);Schritt 2
iamdevlinph
2

Schau dir meinen Plunkr an. Ich generiere programmgesteuert eine Widget-Direktive mit einer Render-Direktive.

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
quelle
1

Ich nehme an, wenn Sie über einen isolierten Bereich sprechen, sprechen Sie über eine Richtlinie.

Hier ist ein Beispiel dafür. 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() {

        }
    };

});

Fügen Sie einen Controller grundsätzlich einer Direktive hinzu, die einen isolierten Bereich definiert hat. Der in den Direktivencontroller eingefügte Bereich ist ein isolierter Bereich. Im Direktiven-Controller können Sie Ihrer AlertFactory injizieren, an die Sie den Isolate-Bereich übergeben können.

rgaskill
quelle
grrr..plnkr.co scheint gerade offline zu sein. Hoffentlich funktioniert der Link immer noch, wenn er zurückkommt.
Rgaskill
Mmmm ich denke es ist keine gute Lösung für mich, ich brauche keine Direktive. Ich benötige eine Factory-Funktion, um einen modalen Warndialog anzuzeigen. Also muss ich Vorlage HTML in Dom Overlay anhängen und einen Controller verwenden, um Daten in der Warnansicht zu verwalten.
Premier
2
Ich würde argumentieren, dass Sie den Dom in einer Fabrik nicht manipulieren sollten. Sie mischen Ihre Ansichtslogik mit Ihrer Geschäftslogik. Sie könnten genau das tun, was Sie in einer Anweisung beschrieben haben, in der die Dom-Manipulation stattfinden soll.
Rgaskill
Siehe Brad Green's Antwort an Andy bezüglich der Verwendung eines Dienstes für ein Modal anstelle einer Direktive: blog.angularjs.org/2012/11/about-those-directives.html
Mark Rajcok
... Aber für diesen speziellen Bootstrap-Fall denke ich, dass er am besten mit einer Direktive gelöst werden kann. plnkr.co/edit/z4J8jH?p=preview
rgaskill