Можете ли вы передать параметры контроллеру AngularJS при создании?

У меня есть контроллер, ответственный за связь с API для обновления свойств пользователя, имени, электронной почты и т.д. У каждого пользователя есть 'id', который передается с сервера при просмотре страницы профиля.

Я хотел бы передать это значение контроллеру AngularJS, чтобы он знал, что точка входа API для текущего пользователя. Я пробовал передать значение в ng-controller. Например:

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

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

и в HTML

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

где {% id %} распечатать идентификатор, отправленный с сервера. но я получаю ошибки.

Каков правильный способ передать значение в контроллер при его создании?

Ответ 1

Примечания:

Этот ответ старый. Это просто доказательство того, как можно достичь желаемого результата. Однако это может быть не лучшее решение в соответствии с некоторыми комментариями ниже. У меня нет документации для поддержки или отказа от следующего подхода. Пожалуйста, ознакомьтесь с некоторыми комментариями ниже для дальнейшего обсуждения этой темы.

Исходный ответ:

Я ответил на это Да, вы можете сделать это с помощью ng-init и простой функции init.

Вот пример этого на 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)
  };


});

Ответ 2

Я очень опаздываю на это, и я понятия не имею, если это хорошая идея, но вы можете включить $attrs в функцию контроллера, позволяющую инициализировать контроллер, используя "аргументы", предоставляемые на элементе, например,

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>

Опять же, не знаю, если это хорошая идея, но, похоже, это работает и является другой альтернативой.

Ответ 3

Это также работает.

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>

Ответ 4

Как и @akonsu и Найджел Findlater, вы можете прочитать URL-адрес, где url index.html#/user/:id с $routeParams.id и использовать его внутри контроллера.

ваше приложение:

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

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

служба ресурсов

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

контроллер

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

тогда elm доступен в представлении в зависимости от id.

Ответ 5

В представлении не должно указываться config

В Angular шаблон никогда не должен диктовать конфигурацию, которая по сути является желанием людей, когда они хотят передать аргументы контроллерам из файла шаблона. Это становится скользким уклоном. Если параметры конфигурации жестко закодированы в шаблонах (например, атрибутом директивы или аргумента контроллера), вы больше не сможете повторно использовать этот шаблон для чего-либо, кроме одного использования. Вскоре вы захотите повторно использовать этот шаблон, но с другой конфигурацией, и теперь для этого вы либо будете предварительно обрабатывать шаблоны, чтобы вставлять переменные, прежде чем они будут переданы в angular, либо с помощью массивных директив для сплющивания из огромных блоков HTML, чтобы вы повторно использовали весь HTML-контроллер, за исключением div-оболочки и аргументов. Для небольших проектов это не имеет большого значения. Для чего-то большого (что angular превосходит), он становится уродливым быстро.

Альтернатива: Модули

Этот тип конфигурации - это то, какие модули были разработаны для обработки. Во многих учебниках angular у людей есть единый модуль для всего их приложения, но на самом деле система спроектирована и полностью поддерживает множество небольших модулей, каждая из которых обертывает небольшие части всего приложения. В идеале, контроллеры, модули и т.д. Будут объявлены в отдельных файлах и сшиты вместе в конкретных повторно используемых фрагментах. Когда ваше приложение разработано таким образом, вы получаете много повторного использования в дополнение к простым аргументам контроллера.

В приведенном ниже примере есть 2 модуля, повторно использующих один и тот же контроллер, но каждый со своими настройками конфигурации. Эти настройки конфигурации передаются через инъекцию зависимостей с помощью module.value. Это относится к способу angular, потому что у нас есть следующее: встраивание зависимостей конструктора, многоразовый код контроллера, многоразовые шаблоны контроллеров (контроллер может легко включаться в ng-include), легко тестировать систему без HTML и, наконец, повторно - доступные модули как средство для сшивания деталей вместе.

Вот пример:

<!-- 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>

Смотрите в действии: jsFiddle.

Ответ 6

Если ng-init не для передачи объектов в $scope, вы всегда можете написать свою собственную директиву. Итак, вот что я получил:

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>

Но мой подход может только изменять объекты, которые уже определены в контроллере.

Ответ 7

Похоже, что лучшим решением для вас является директива. Это позволяет вам по-прежнему иметь свой контроллер, но определять для него настраиваемые свойства.

Используйте это, если вам нужен доступ к переменным в области обтекания:

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>

Используйте это, если вам не нужен доступ к переменным в области переноса:

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

Я нашел пропущенные переменные из $routeProvider полезным.

Например, вы используете один контроллер MyController для нескольких экранов, передавая внутри себя очень важную переменную mySuperConstant.

Используйте эту простую структуру:

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

    }

Ответ 9

Это можно сделать при настройке маршрутов, например,

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

И позже используйте его как

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

Итак, когда мы установили маршрут, который мы отправили: itemType и выведем его позже из $routeParams.

Ответ 11

Этот вопрос старый, но я долго пытался изо всех сил пытаться получить ответ на эту проблему, которая будет работать для моих нужд и не легко ее найти. Я считаю, что мое следующее решение намного лучше, чем принятое в настоящее время, возможно, потому, что angular добавил функциональность, поскольку этот вопрос изначально был поставлен.

Короткий ответ, используя метод Module.value, позволяет передавать данные в конструктор контроллера.

Смотрите мой плункер здесь

Я создаю объект модели, а затем связываю его с контроллером модуля, ссылаясь на него с именем "model"

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>

Конструктор в моем контроллере затем принимает параметр с тем же идентификатором 'model', который он может получить.

контроллер

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

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

Примечания:

Я использую ручную инициализацию bootstrapping, которая позволяет мне инициализировать мою модель перед ее отправкой на angular. Это намного лучше работает с существующим кодом, так как вы можете подождать, чтобы настроить ваши соответствующие данные, и только компилируйте подмножество angular вашего приложения по запросу, когда захотите.

В плунтере я добавил кнопку, чтобы предупредить значения объекта модели, которые изначально были определены в javascript и переданы в angular, чтобы подтвердить, что angular действительно ссылается на объект модели, а не на копирование он и работает с копией.

В этой строке:

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

Я передаю объект MyController в функцию Module.controller, а не объявляю как функцию inline. Я думаю, что это позволяет нам гораздо более четко определять наш объект контроллера, но документация angular имеет тенденцию делать это inline, поэтому я подумал, что он нуждается в разъяснении.

Я использую синтаксис "controller as" и присваиваю значения свойству "this" MyController, вместо использования переменной "$ scope". Я считаю, что это отлично работает с использованием $scope точно так же, назначение контроллера будет выглядеть примерно так:

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

и конструктор контроллера должен иметь такую ​​подпись:

function MyController ($scope, model) {

Если по какой-либо причине вы хотели бы добавить эту модель в качестве значения второго модуля, который затем присоединяется как зависимость от вашего основного модуля.

Я считаю, что его решение намного лучше, чем принятое в настоящее время, потому что

  • Модель, переданная контроллеру, фактически является объектом javascript, а не строкой, которая оценивается. Это истинная ссылка на объект, и изменения в нем влияют на другие ссылки на этот объект модели.
  • Angular говорит, что принятое использование ответа ng-init является неправильным использованием, которое это решение не делает.

Похоже, что способ angular работает в большинстве других примеров, которые я видел, имеет контроллер, определяющий данные модели, что никогда не имело смысла для меня, нет разделения между моделью и контроллером, Мне действительно кажется MVC. Это решение позволяет действительно иметь полностью отдельный объект модели, который вы передаете в контроллер. Также следует отметить, что если вы используете директиву ng-include, вы можете поместить весь свой angular html в отдельный файл, полностью разделяя ваш вид модели и контроллер на отдельные модульные элементы.

Ответ 12

Если вы используете angular -ui-router, то это правильное решение: https://github.com/angular-ui/ui-router/wiki#resolve

В принципе, вы объявляете набор зависимостей для "разрешения" перед созданием контроллера. Вы можете объявлять зависимости для каждого из ваших "состояний". Эти зависимости затем передаются в "конструкторе" контроллера.

Ответ 13

Один из способов сделать это - иметь отдельную услугу, которая может использоваться как "сосуд" для тех аргументов, где они являются публичными членами данных.

Ответ 15

Вот решение (основанное на предложении Marcin Wyszynski), которое работает там, где вы хотите передать значение в свой контроллер, но вы явно не объявляете контроллер в своем html (что, по-видимому, требует ng-init) - если, например, вы создаете свои шаблоны с ng-view и объявляете каждый контроллер для соответствующего маршрута через 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>

В этом решении CurrentUser - это служба, которая может быть введена в любой контроллер, тогда доступное свойство .name.

Две заметки:

  • Проблема, с которой я столкнулся, заключается в том, что .name устанавливается после загрузки контроллера, так как обходной путь у меня есть короткий тайм-аут перед отображением имени пользователя в области контроллера. Есть ли опрятный способ ожидания, пока на службе не будет установлено имя.

  • это очень простой способ получить текущего пользователя в ваше приложение Angular со всей аутентификацией, хранящейся вне Angular. У вас может быть параметр before_filter, чтобы пользователи, не входящие в систему, не попали в html, где было загружено ваше приложение Angular, и внутри этого html вы могли просто интерполировать имя пользователя и даже их идентификатор, если вы хотите взаимодействовать с данные пользователя через http-запросы из вашего приложения Angular. Вы можете разрешить пользователям без регистрации использовать приложение Angular с гостевым пользователем по умолчанию. Любые советы по поводу того, почему этот подход был бы плохим, приветствуются - он чувствует себя слишком легко, чтобы быть разумным!)

Ответ 16

Это расширение @Michael Tiller отличный ответ. Его ответ работает для инициализации переменных из представления путем введения объекта $attrs в контроллер. Проблема возникает, если тот же контроллер вызывается из $routeProvider при навигации по маршрутизации. Затем вы получаете ошибку инжектора. Unknown provider: $attrsProvider потому что $attrs доступен только для внедрения при компиляции представления. Решение состоит в том, чтобы передать переменную (foo) через $routeParams при инициализации контроллера из маршрута и через $attrs при инициализации контроллера из представления. Здесь мое решение.

С маршрута

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

Это обрабатывает URL, такой как '/mypage/bar'. Как вы можете видеть, foo передается через url param, и мы предоставляем $injector пустой объект для $attrs поэтому ошибок инжектора нет.

С точки зрения

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

</div>

Теперь контроллер

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 been initialized from view
   //else find it from $routeParams
   var foo = $attrs.foo? $attrs.foo : $routeParams.foo;
   console.log(foo); //prints 'bar'

});

Ответ 17

Мне не очень понравились какие-либо решения здесь для моего конкретного случая использования, поэтому я решил опубликовать то, что я сделал, потому что я не видел его здесь.

Я просто хотел использовать контроллер, больше похожий на директиву, внутри цикла ng-repeat:

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

Теперь, чтобы получить доступ к objParameter при создании в каждом DirectiveLikeController (или получить актуальный objParameter в ЛЮБОЕ время), все, что мне нужно сделать, это ввести $ scope и вызвать $scope.$eval('objParameter'):

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('objArgument').id); 
});