it-swarm.com.de

Können Sie bei der Erstellung Parameter an einen AngularJS-Controller übergeben?

Ich habe einen Controller, der für die Kommunikation mit einer API verantwortlich ist, um die Eigenschaften eines Benutzers, des Namens, der E-Mail usw. zu aktualisieren. Jeder Benutzer verfügt über einen 'id', der vom Server übergeben wird, wenn die Profilseite angezeigt wird. 

Ich möchte diesen Wert an den AngularJS-Controller übergeben, damit er weiß, was der API-Einstiegspunkt für den aktuellen Benutzer ist. Ich habe versucht, den Wert in ng-controller zu übergeben. Zum Beispiel:

function UserCtrl(id, $scope, $filter) {

$scope.connection = $resource('api.com/user/' + id)

und im HTML

<body ng-controller="UserCtrl({% id %})">

dabei {% id %} die vom Server gesendete ID ausgeben. aber ich bekomme fehler. 

Was ist der richtige Weg, um einen Wert bei seiner Erstellung an einen Controller zu übergeben?

276
nickponline

Anmerkungen:

Diese Antwort ist alt. Dies ist nur ein Beweis für das Erreichen des gewünschten Ergebnisses. Es ist jedoch möglicherweise nicht die beste Lösung, wie in den folgenden Kommentaren beschrieben. Ich habe keine Dokumentation, um den folgenden Ansatz zu unterstützen oder abzulehnen. In den folgenden Kommentaren finden Sie weitere Informationen zu diesem Thema.

Ursprüngliche Antwort:

Ich habe dies auf .__ geantwortet. Ja, Sie können dies mit ng-init und einer einfachen Init-Funktion tun.

Hier ist das Beispiel auf plunker

HTML 

<!DOCTYPE html>
<html ng-app="angularjs-starter">
  <head lang="en">
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
  </head>  
  <body ng-controller="MainCtrl" ng-init="init('James Bond','007')">
    <h1>I am  {{name}} {{id}}</h1>
  </body>
</html>

JavaScript

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {

  $scope.init = function(name, id)
  {
    //This function is sort of private constructor for controller
    $scope.id = id;
    $scope.name = name; 
    //Based on passed argument you can make a call to resource
    //and initialize more objects
    //$resource.getMeBond(007)
  };


});
357
Jigar Patel

Ich bin sehr spät dran und habe keine Ahnung, ob dies eine gute Idee ist, aber Sie können die $attrs injizierbare Funktion in die Controller-Funktion aufnehmen, sodass der Controller mit "Argumenten" initialisiert werden kann, die auf einem Element angegeben sind, z.

app.controller('modelController', function($scope, $attrs) {
    if (!$attrs.model) throw new Error("No model for modelController");

    // Initialize $scope using the value of the model attribute, e.g.,
    $scope.url = "http://example.com/fetch?model="+$attrs.model;
})

<div ng-controller="modelController" model="foobar">
  <a href="{{url}}">Click here</a>
</div>

Keine Ahnung, ob dies eine gute Idee ist, aber es scheint zu funktionieren und ist eine weitere Alternative.

141
Michael Tiller

Das funktioniert auch.

Javascript:

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

app.controller('MainCtrl', function($scope, name, id) {
    $scope.id = id;
    $scope.name = name;
    // and more init
});

Html:

<!DOCTYPE html>
<html ng-app="angularApp">
  <head lang="en">
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
    <script>
       app.value("name", "James").value("id", "007");
    </script>
  </head>
  <body ng-controller="MainCtrl">
    <h1>I am  {{name}} {{id}}</h1>
  </body>
</html>
39
Jonghee Park

Wie @akonsu und Nigel Findlater vorschlagen, können Sie die URL mit index.html#/user/:id und $routeParams.id lesen und im Controller verwenden. 

ihre App: 

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

app.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/:type/:id', {templateUrl: 'myView.html', controller: 'myCtrl'});
}]);

der Ressourcendienst

app.factory('MyElements', ['$resource', function($resource) {
     return $resource('url/to/json/:type/:id', { type:'@type', id:'@id' });
}]);

der Controller 

app.controller('MyCtrl', ['$scope', '$routeParams', 'MyElements', function($scope, $routeParams, MyElements) {
    MyElements.get({'type': $routeParams.type, "id": $routeParams.id }, function(Elm) {
        $scope.Elm = Elm;
    })
}]);

dann ist Elm in der Ansicht abhängig von der id verfügbar. 

15

Die Ansicht sollte nicht config diktieren

In Angular sollte die Vorlage niemals die Konfiguration vorgeben. Dies ist inhärent das, was sich die Leute wünschen, wenn sie Argumente aus einer Vorlagendatei an Controller übergeben möchten. Dies wird zu einem rutschigen Abhang. Wenn Konfigurationseinstellungen in Vorlagen hartcodiert sind (z. B. durch eine Direktive oder ein Controller-Argumentattribut), können Sie diese Vorlage nicht mehr für andere Zwecke als diese einmalige Verwendung verwenden. Bald möchten Sie diese Vorlage wiederverwenden, aber mit einer anderen Konfig. Jetzt müssen Sie entweder die Vorlagen vorverarbeiten, um Variablen einzuspeisen, bevor sie an Angular übergeben werden, oder massive Anweisungen zum Ausspucken von Riesen verwenden HTML-Blöcke, damit Sie den gesamten Controller-HTML-Code bis auf das Wrapper-Div und dessen Argumente wiederverwenden können. Für kleine Projekte ist das keine große Sache. Für etwas Großes (bei dem sich Winkel eindrucksvoll auszeichnet) wird es schnell hässlich.

Die Alternative: Module

Diese Art von Konfiguration wurde für die Module entwickelt. In vielen eckigen Tutorials haben die Leute ein einziges Modul für ihre gesamte Anwendung, aber tatsächlich ist das System so konzipiert und unterstützt viele kleine Module, die jeweils kleine Teile der gesamten Anwendung umschließen. Im Idealfall werden Controller, Module usw. in separaten Dateien deklariert und in bestimmten wiederverwendbaren Blöcken zusammengefügt. Wenn Ihre Anwendung auf diese Weise entwickelt wurde, erhalten Sie zusätzlich zu den einfachen Controller-Argumenten eine Menge Wiederverwendung.

Das folgende Beispiel enthält zwei Module, die denselben Controller wiederverwenden, jedoch jeweils mit eigenen Konfigurationseinstellungen. Diese Konfigurationseinstellungen werden über die Abhängigkeitsinjektion mit module.value übergeben. Dies hängt von der Winkelform ab, da wir über Folgendes verfügen: Konstruktorabhängigkeitspritze, wiederverwendbarer Controller-Code, wiederverwendbare Controller-Vorlagen (der Controller div könnte leicht in ng-include enthalten sein), einfach Unit-testbares System ohne HTML und schließlich wiederverwendbar Module als Fahrzeug zum Zusammennähen der Teile.

Hier ist ein Beispiel:

<!-- index.html -->
<div id="module1">
    <div ng-controller="MyCtrl">
        <div>{{foo}}</div>
    </div>
</div>
<div id="module2">
    <div ng-controller="MyCtrl">
        <div>{{foo}}</div>
    </div>
</div>
<script>
    // part of this template, or a JS file designed to be used with this template
    angular.element(document).ready(function() {
        angular.bootstrap(document.getElementById("module1"), ["module1"]);
        angular.bootstrap(document.getElementById("module2"), ["module2"]);
    });
</script>

<!-- scripts which will likely in be in their seperate files -->
<script>
    // MyCtrl.js
    var MyCtrl = function($scope, foo) {
    $scope.foo = foo;
    }

    MyCtrl.$inject = ["$scope", "foo"];

    // Module1.js
    var module1 = angular.module('module1', []);
    module1.value("foo", "fooValue1");
    module1.controller("MyCtrl", MyCtrl);

    // Module2.js file
    var module2 = angular.module('module2', []);
    module2.value("foo", "fooValue2");
    module2.controller("MyCtrl", MyCtrl);
</script>

Sehen Sie es in Aktion: jsFiddle .

14
Nucleon

Es sieht so aus, als wäre die beste Lösung für Sie eigentlich eine Direktive. Auf diese Weise können Sie Ihren Controller weiterhin verwenden, jedoch benutzerdefinierte Eigenschaften dafür definieren.

Verwenden Sie dies, wenn Sie Zugriff auf Variablen im Wrapping-Bereich benötigen:

angular.module('myModule').directive('user', function ($filter) {
  return {
    link: function (scope, element, attrs) {
      $scope.connection = $resource('api.com/user/' + attrs.userId);
    }
  };
});

<user user-id="{% id %}"></user>

Verwenden Sie dies, wenn Sie keinen Zugriff auf Variablen im Wrapping-Bereich benötigen:

angular.module('myModule').directive('user', function ($filter) {
  return {
    scope: {
      userId: '@'
    },
    link: function (scope, element, attrs) {
      $scope.connection = $resource('api.com/user/' + scope.userId);
    }
  };
});

<user user-id="{% id %}"></user>
8
btesser

Wenn ng-init nicht für die Übergabe von Objekten in $scope vorgesehen ist, können Sie immer Ihre eigene Direktive schreiben. Also hier ist was ich habe:

http://jsfiddle.net/goliney/89bLj/

Javasript:

var app = angular.module('myApp', []);
app.directive('initData', function($parse) {
    return function(scope, element, attrs) {
        //modify scope
        var model = $parse(attrs.initData);
        model(scope);
    };
});

function Ctrl1($scope) {
    //should be defined
    $scope.inputdata = {foo:"east", bar:"west"};
}

Html:

<div ng-controller="Ctrl1">
    <div init-data="inputdata.foo=123; inputdata.bar=321"></div>
</div>

Mein Ansatz kann jedoch nur Objekte verändern, die bereits im Controller definiert sind.

8
Sergey Goliney

Ich fand die Übergabe von Variablen aus $ routeProvider nützlich.

Beispielsweise verwenden Sie einen Controller MyController für mehrere Bildschirme und übergeben eine sehr wichtige Variable "mySuperConstant".

Verwenden Sie diese einfache Struktur:

Router:

$routeProvider
            .when('/this-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "123"
            })
            .when('/that-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "456"
            })
            .when('/another-page', {
                templateUrl: 'common.html',
                controller: MyController,
                mySuperConstant: "789"
            })

MyController:

    MyController: function ($scope, $route) {
        var mySuperConstant: $route.current.mySuperConstant;
        alert(mySuperConstant);

    }
7
Dmitri Algazin

Sie können dies tun, wenn Sie die Routen für z.

 .when('/newitem/:itemType', {
            templateUrl: 'scripts/components/items/newEditItem.html',
            controller: 'NewEditItemController as vm',
            resolve: {
              isEditMode: function () {
                return true;
              }
            },
        })

Und später als verwenden 

(function () {
  'use strict';

  angular
    .module('myApp')
    .controller('NewEditItemController', NewEditItemController);

  NewEditItemController.$inject = ['$http','isEditMode',$routeParams,];

  function NewEditItemController($http, isEditMode, $routeParams) {
    /* jshint validthis:true */

    var vm = this;
    vm.isEditMode = isEditMode;
    vm.itemType = $routeParams.itemType;
  }
})();

Wenn wir also die Route eingerichtet haben, haben wir: itemType gesendet und später von $ routeParams abgerufen.

6
ssaini

Es gibt eine andere Möglichkeit, Parameter an einen Controller zu übergeben, indem Sie $ routeParams in Ihren Controller einfügen und dann die hier beschriebenen URL-Parameter verwenden Wie lassen sich Abfrageparameter in AngularJS am genauesten lesen?

4
Nigel Findlater

Diese Frage ist alt, aber ich habe lange versucht, eine Antwort auf dieses Problem zu finden, das für meine Bedürfnisse geeignet wäre und es nicht leicht fand. Ich glaube, dass meine folgende Lösung viel besser ist als die derzeit akzeptierte, vielleicht weil Angus seit der ursprünglichen Fragestellung zusätzliche Funktionalität hinzugefügt hat. 

Eine kurze Antwort mit der Module.value-Methode ermöglicht das Übergeben von Daten an einen Controller-Konstruktor.

Siehe meinen Plunker hier

Ich erstelle ein Modellobjekt und ordne es dann dem Controller des Moduls zu, wobei es mit dem Namen 'model' referenziert wird.

HTML/JS

  <html>
  <head>
    <script>
      var model = {"id": 1, "name":"foo"};

      $(document).ready(function(){
        var module = angular.module('myApp', []);
        module.value('model', model);
        module.controller('MyController', ['model', MyController]);
        angular.bootstrap(document, ['myApp']);
      });

      function confirmModelEdited() {
        alert("model name: " + model.name + "\nmodel id: " + model.id);
      }
    </script>

  </head>
  <body >
      <div ng-controller="MyController as controller">
        id: {{controller.model.id}} <br>
        name: <input ng-model="controller.model.name"/>{{controller.model.name}}
        <br><button ng-click="controller.incrementId()">increment ID</button>
        <br><button onclick="confirmModelEdited()">confirm model was edited</button>
    </div>
  </body>

</html>

Der Konstruktor in meinem Controller akzeptiert dann einen Parameter mit dem gleichen Bezeichner 'Modell', auf den er zugreifen kann. 

Controller

function MyController (model) {
  this.model = model;
}

MyController.prototype.incrementId = function() {
  this.model.id = this.model.id + 1;
}

Anmerkungen: 

Ich verwende die manuelle Initialisierung von bootstrapping , wodurch ich mein Modell initialisieren kann, bevor es an angle gesendet wird. Dies funktioniert viel besser mit vorhandenem Code, da Sie mit dem Einrichten Ihrer relevanten Daten warten können und das Winkel-Subset Ihrer App nur bei Bedarf kompilieren können, wenn Sie möchten.

Im Plunker habe ich eine Schaltfläche hinzugefügt, um die Werte des Modellobjekts zu warnen, das anfangs in Javascript definiert und an angle übergeben wurde, nur um zu bestätigen, dass Winkel das Modellobjekt wirklich referenziert, anstatt es zu kopieren und mit einer Kopie zu arbeiten. 

In dieser Zeile:

module.controller('MyController', ['model', MyController]);

Ich übergebe das MyController-Objekt an die Module.controller-Funktion, anstatt es als Funktion inline zu deklarieren. Ich denke, das erlaubt uns, unser Controller-Objekt viel klarer zu definieren, aber die Angular-Dokumentation tendiert dazu, es inline zu machen, also dachte ich, es hätte Klarheit.

Ich verwende die "controller as" -Syntax und weise der "this" -Eigenschaft von MyController Werte zu, anstatt die "$ scope" -Variable zu verwenden. Ich glaube, das würde gut funktionieren, wenn man $ scope verwendet. Die Controller-Zuweisung würde dann ungefähr so ​​aussehen:

module.controller('MyController', ['$scope', 'model', MyController]);

und der Controller-Konstruktor hätte eine Signatur wie folgt:

function MyController ($scope, model) {

Wenn Sie es aus irgendeinem Grund auch wollten, können Sie dieses Modell auch als Wert eines zweiten Moduls anhängen, das Sie dann als Abhängigkeit an Ihr Hauptmodul anhängen.

Ich glaube, dass seine Lösung viel besser ist als die derzeit akzeptierte, weil

  1. Das an den Controller übergebene Modell ist eigentlich ein Javascript-Objekt und keine Zeichenfolge, die ausgewertet wird. Dies ist eine echte Referenz auf das Objekt, und Änderungen daran wirken sich auf andere Referenzen auf dieses Modellobjekt aus.
  2. Angular sagt, dass die akzeptierte Antwort von ng-init ein Missbrauch ist, was diese Lösung nicht tut.

Die Art und Weise, wie Angular in den meisten anderen Beispielen funktioniert, die ich gesehen habe, hat den Controller dazu bestimmt, die Daten des Modells zu definieren, was für mich nie sinnvoll war. Es gibt keine Trennung zwischen dem Modell und dem Controller, das scheint nicht wirklich zu sein MVC für mich. Diese Lösung ermöglicht es Ihnen, ein vollständig separates Modellobjekt zu haben, das Sie in den Controller übergeben. Wenn Sie die ng-include-Direktive verwenden, können Sie alle Ihre eckigen HTML-Dateien in einer separaten Datei ablegen, sodass Modellansicht und Controller vollständig in separate modulare Teile unterteilt werden.

3
O. Winter

Wenn Sie einen angle-ui-router verwenden, ist dies die richtige Lösung: https://github.com/angular-ui/ui-router/wiki#resolve

Grundsätzlich deklarieren Sie eine Reihe von Abhängigkeiten, die "aufgelöst" werden, bevor der Controller instanziiert wird. Sie können Abhängigkeiten für jeden Ihrer "Zustände" festlegen. Diese Abhängigkeiten werden dann im "Konstruktor" des Controllers übergeben.

2
Juan

Ich mochte keine der hier aufgeführten Lösungen für meinen speziellen Anwendungsfall wirklich, also dachte ich, ich würde das, was ich getan habe, posten, weil ich es hier nicht gesehen habe.

Ich wollte einfach einen Controller eher wie eine Direktive innerhalb einer ng-repeat-Schleife verwenden:

<div ng-repeat="objParameter in [{id:'a'},{id:'b'},{id:'c'}]">
  <div ng-controller="DirectiveLikeController as ctrl"></div>
</div>

Jetzt muss ich nur noch $ scope einfügen und $scope.$eval('objParameter') aufrufen, um auf die objParameter beim Erstellen in jedem DirectiveLikeController zuzugreifen (oder um den aktuellen objParameter zu JEDER Zeit zu erhalten):

var app = angular.module('myapp', []);
app.controller('DirectiveLikeController',['$scope'], function($scope) {
   //print 'a' for the 1st instance, 'b' for the 2nd instance, and 'c' for the 3rd.
   console.log($scope.$eval('objParameter').id); 
});

Der einzige Nachteil, den ich sehe, ist, dass der übergeordnete Controller wissen muss, dass der Parameter objParameter heißt.

1
JohnTD

Eine Möglichkeit, dies zu erreichen, wäre ein separater Dienst, der als "Schiff" für die Argumente verwendet werden kann, bei denen sie öffentliche Datenmitglieder sind.

1

Dies ist eine Erweiterung von @Michael Tillers ausgezeichnete Antwort . Seine Antwort dient zum Initialisieren von Variablen aus der Ansicht, indem das $attrs-Objekt in die Steuerung eingefügt wird. Das Problem tritt auf, wenn derselbe Controller von $routeProvider aufgerufen wird, wenn Sie durch Routing navigieren. Dann erhalten Sie den Injektorfehler Unknown provider : $attrsProvider, da $attrs nur für die Injektion verfügbar ist, wenn die Ansicht kompiliert wird. Die Lösung besteht darin, die Variable (foo) durch $routeParams zu übergeben, wenn der Controller aus der Route initialisiert wird, und durch $attrs, wenn der Controller aus der Ansicht initialisiert wird. Hier ist meine Lösung.

Von der Route

$routeProvider.
        when('/mypage/:foo', {
            templateUrl: 'templates/mypage.html',
            controller: 'MyPageController',
            caseInsensitiveMatch: true,
            resolve: {
                $attrs: function () {
                    return {};
                }
            }
        });

Dies behandelt eine URL wie '/mypage/bar'. Wie Sie sehen, wird foo von url param übergeben und wir geben $injector ein leeres Objekt für $attrs, damit keine Injektorfehler auftreten.

Aus Ansicht

<div ng-controller="MyPageController" data-foo="bar">

</div>

Nun die Steuerung

var app = angular.module('myapp', []);
app.controller('MyPageController',['$scope', '$attrs', '$routeParams'], function($scope, $attrs, $routeParams) {
   //now you can initialize foo. If $attrs contains foo, it's been initialized from view
   //else find it from $routeParams
   var foo = $attrs.foo? $attrs.foo : $routeParams.foo;
   console.log(foo); //prints 'bar'

});
0
Moses Machua

Hier ist eine Lösung (basierend auf dem Vorschlag von Marcin Wyszynski), die funktioniert, wenn Sie einen Wert an Ihren Controller übergeben möchten, den Controller jedoch nicht explizit in Ihrer HTML-Datei deklarieren (was ng-init anscheinend erfordert) - wenn beispielsweise Sie rendern Ihre Vorlagen mit ng-view und deklarieren jeden Controller für die entsprechende Route über den routeProvider.

JS

messageboard.directive('currentuser', ['CurrentUser', function(CurrentUser) {
  return function(scope, element, attrs) {
    CurrentUser.name = attrs.name;
  };
}]);

html

<div ng-app="app">
  <div class="view-container">
    <div ng-view currentuser name="testusername" class="view-frame animate-view"></div>
  </div>
</div>

In dieser Lösung ist CurrentUser ein Dienst, der in jeden Controller eingefügt werden kann, wobei die .name -Eigenschaft dann verfügbar ist. 

Zwei Notizen:

  • ein Problem, auf das ich gestoßen bin, ist, dass .name nach dem Laden des Controllers festgelegt wird. Als Problemumgehung habe ich ein kurzes Timeout, bevor der Benutzername im Controller-Bereich gerendert wird. Gibt es eine nette Art zu warten, bis .name für den Dienst festgelegt wurde?

  • das fühlt sich an wie eine sehr einfache Möglichkeit, einen aktuellen Benutzer in Ihre Angular App zu bringen, wobei die Authentifizierung außerhalb von Angular erfolgt. Sie könnten über einen before_filter verfügen, um zu verhindern, dass nicht angemeldete Benutzer zu der HTML-Datei gelangen, in der Ihre Angular-App bootet, und innerhalb dieser HTML-Datei können Sie einfach den Namen des angemeldeten Benutzers und sogar seine ID interpolieren, wenn Sie mit den Benutzerdaten interagieren möchten über http-Anfragen von Ihrer Angular-App. Sie können nicht angemeldeten Benutzern die Verwendung der Angular App mit einem Standardbenutzer erlauben. Irgendwelche Ratschläge, warum dieser Ansatz schlecht wäre, wäre zu begrüßen - es ist zu einfach, um vernünftig zu sein!)

0
Ollie H-M

Nein, das ist nicht möglich. Ich denke, Sie können ng-init als Hack http://docs.angularjs.org/api/ng.directive:ngInit verwenden.

0
SunnyShah