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

265

У меня есть контроллер, ответственный за связь с 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 %} распечатать идентификатор, отправленный с сервера. но я получаю ошибки.

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

  • 4
    если у вас есть идентификатор как часть URL, то вы можете просто прочитать URL
  • 0
    У меня была очень похожая проблема, и я решил ее, как я написал в своем ответе. Иногда используя библиотеки, мы упускаем из виду простую фундаментальную концепцию вызова функций JavaScript.
Показать ещё 2 комментария
Теги:

15 ответов

346
Лучший ответ

Примечания:

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

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

Я ответил на это Да, вы можете сделать это с помощью 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)
  };


});
  • 4
    Спасибо за это - избавил меня от много головных болей, и это отлично сработало для моей ситуации. Мне пришлось инициализировать мой контроллер с идентификатором объекта, и это было просто правильно.
  • 0
    Есть ли способ передать объект в метод init?
Показать ещё 17 комментариев
141

Я очень опаздываю на это, и я понятия не имею, если это хорошая идея, но вы можете включить $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
    Как бы я передал объект, используя этот подход? var obj = {a: 1, b: 2};
  • 3
    Это очень разумное решение проблемы создания гибридного приложения.
Показать ещё 11 комментариев
38

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

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>
  • 3
    Это хорошее решение, которое работает с существующим механизмом инжекции конструктора. По сути, вы создаете две простые службы, называемые «имя» и «идентификатор». Инжектор заботится о сопоставлении их по имени во время строительства. См. Раздел «Рецепт ценности» на docs.angularjs.org/guide/providers.
  • 1
    Ницца. Обратите внимание, что это работает и с объектами, а не только с примитивными типами, такими как строки.
Показать ещё 3 комментария
15

Как и @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.

13

В представлении не должно указываться 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.

8

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

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

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

Если 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>

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

  • 0
    Для справки это прекрасно работает, но в текущей версии angular это $ attrs (против attrs)
7

Я нашел пропущенные переменные из $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);

    }
5

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

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

4

Есть еще один способ передать параметры контроллеру, введя $routeParams в ваш контроллер, а затем используя параметры URL-адреса, описанные здесь Каков наиболее краткий способ чтения параметров запроса в AngularJS?

3

Этот вопрос старый, но я долго пытался изо всех сил пытаться получить ответ на эту проблему, которая будет работать для моих нужд и не легко ее найти. Я считаю, что мое следующее решение намного лучше, чем принятое в настоящее время, возможно, потому, что 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 в отдельный файл, полностью разделяя ваш вид модели и контроллер на отдельные модульные элементы.

2

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

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

1

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

  • 0
    Лучший ответ, ИМО. В настоящее время я также использую хранилище браузера в некоторых случаях для тех пользователей, которые осмеливаются нажать F5, поэтому это состояние сохраняется.
0

Вот решение (основанное на предложении 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 с гостевым пользователем по умолчанию. Любые советы по поводу того, почему этот подход был бы плохим, приветствуются - он чувствует себя слишком легко, чтобы быть разумным!)

0

Нет, это невозможно. Я думаю, вы можете использовать ng-init как hack http://docs.angularjs.org/api/ng.directive:ngInit.

  • 4
    Я позволю себе не согласиться, но вы можете сделать это, используя ng-init и используя функцию init.
  • 2
    Но это взлом (как сейчас указывают документы).
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню