$ injector: unpr при попытке добавить службу [duplicate]

Короткий ответ НЕТ, у нас на этом этапе в CSS нет parent selector, но если вы все равно не хотите менять элементы или классы, второй вариант использует JavaScript, что-то вроде этого:

var activeATag = Array.prototype.slice.call(document.querySelectorAll('a.active'));

activeATag.map(function(x) {
  if(x.parentNode.tagName === 'LI') {
    x.parentNode.style.color = 'red'; //your property: value;
  }
});

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

$('a.active').parents('li').css('color', 'red'); //your property: value;
101
задан Devid Farinelli 25 October 2016 в 07:40
поделиться

6 ответов

$scope, который вы видите впрыснутым в контроллеры, - это не какая-то услуга (например, остальная часть инъектируемого материала), а объект Scope. Многие объекты области могут быть созданы (обычно прототипно наследуется от родительской области). Корнем всех областей является $rootScope, и вы можете создать новую область с помощью метода $new() любой области (включая $rootScope).

Целью области является «склеить» презентацию и бизнес-логику вашего приложения. Не имеет смысла передавать $scope в службу.

Службы - это одноэлементные объекты, используемые (среди прочего) для обмена данными (например, среди нескольких контроллеров) и, как правило, инкапсулировать многократно используемые фрагменты кода ( поскольку они могут быть введены и предлагать свои «услуги» в любой части вашего приложения, которая им нужна: контроллеры, директивы, фильтры, другие сервисы и т. д.).

Я уверен, что для вас будут работать различные подходы. Один из них таков: поскольку StudentService отвечает за работу со студенческими данными, вы можете иметь StudentService держать массив учеников и позволить ему «делиться» с кем-то, кто может быть заинтересован (например, ваш $scope). Это имеет еще больший смысл, если есть другие представления / контроллеры / фильтры / службы, которые должны иметь доступ к этой информации (если нет сейчас, не удивляйтесь, если они скоро появятся). Каждый раз, когда добавляется новый ученик (с использованием метода save() службы], собственный массив учеников будет обновляться, и каждый другой объект, совместно использующий этот массив, будет автоматически обновляться.

Основываясь на описанный выше, ваш код может выглядеть следующим образом:

angular.module('cfd', [])

.factory('StudentService', ['$http', function ($http) {
    var path = 'data/people/students.json';
    var students = [];

    /* In the real app, instead of just updating the students array
     * (which will be probably already done from the controller)
     * this method should send the student data to the server */
    var save = function (student) {
        if (student.id === null) {
            students.push(student);
        } else {
            for (var i = 0; i < students.length; i++) {
                if (students[i].id === student.id) {
                    students[i] = student;
                    break;
                }
            }
        }
    };

    /* Populate the students array with students from the server */
    $http.get(path).success(function (data) {
        data.forEach(function (student) {
            students.push(student);
        });
    });

    return {
        students: students,
        save:     save
    };     
}])

.controller('someCtrl', ['$scope', 'StudentService', 
    function ($scope, StudentService) {
        $scope.students = StudentService.students;
        $scope.saveStudent = function (student) {
            // Do some $scope-specific stuff

            // Do the actual saving using the StudentService
            StudentService.save(student);
            // The $scope's `students` array will be automatically updated
            // since it references the StudentService's `students` array

            // Do some more $scope-specific stuff, 
            // e.g. show a notification 
        };
    }
]);

. При использовании этого подхода вы должны быть осторожны, чтобы никогда не переписывать службы массив, потому что тогда любые другие компоненты (например, области) будут по-прежнему ссылаться на исходный массив, и ваше приложение сломается. Например. чтобы очистить массив в StudentService:

/* DON'T DO THAT   */  
var clear = function () { students = []; }

/* DO THIS INSTEAD */  
var clear = function () { students.splice(0, students.length); }

См. также эту короткую демонстрацию .


LITTLE UPDATE:

Несколько слов, чтобы избежать путаницы, которая может возникнуть при разговоре об использовании службы, но не создавая ее с помощью функции service().

Цитирование документов на $provide :

Угловая служба - это одноэлементный объект, созданный фабрикой услуг. Эти сервисные заводы - это функции, которые, в свою очередь, создаются поставщиком услуг. Поставщики услуг - это функции конструктора. При создании экземпляра они должны содержать свойство, называемое $get, которое содержит функцию фабрики услуг. [...] ... служба $provide имеет дополнительные вспомогательные методы для регистрации служб без указания поставщика:

  • поставщик (поставщик) - регистрирует поставщика услуг с помощью $ injector
  • constant (obj) - регистрирует значение / объект, к которому могут обращаться провайдеры и службы.
  • Значение
  • (obj) - регистрирует значение / объект, к которому могут обращаться только службы, а не провайдеры.
  • factory (fn) - регистрирует функцию фабрики услуг fn, которая будет обернута в объект поставщика услуг, свойство $ get будет содержать заданную заводскую функцию.
  • service (класс) - регистрирует функцию-конструктор, класс, который будет обернут в объект поставщика услуг, свойство $ get будет создавать экземпляр нового объекта с использованием данной функции конструктора.

В основном, то, что он говорит, заключается в том, что каждая функция Angular регистрируется с помощью $provide.provider(), но для более простых сервисов существуют «короткие» методы (две из которых - service() и factory()). Все это «сводится» к сервису, поэтому не имеет большого значения, какой метод вы используете (до тех пор, пока требования к вашему сервису могут быть покрыты этим методом).

BTW, provider vs service vs factory - одна из самых запутанных концепций для угловых новичков, но, к счастью, есть много ресурсов (здесь на SO), чтобы облегчить ситуацию. (Просто найдите вокруг.)

(надеюсь, что это очистит - дайте мне знать, если это не так.)

176
ответ дан Amir 19 August 2018 в 19:20
поделиться
  • 1
    Один вопрос. Вы говорите, что сервис, но ваш пример кода использует фабрику. Я только начинаю понимать разницу между фабриками, услугами и провайдерами, просто хочу быть уверенным, что работа с фабрикой - лучший вариант, поскольку я использовал сервис. Многому научился из вашего примера. Спасибо за скрипку и ОЧЕНЬ ясное объяснение. – chris Frisina 15 April 2014 в 03:17
  • 2
    @chrisFrisina: Обновлен ответ с небольшим объяснением. В принципе, это не имеет большого значения, если вы используете service или factory - вы закончите с и Угловой сервис . Просто убедитесь, что вы понимаете , как работают каждый, и если это соответствует вашим потребностям. – gkalpak 15 April 2014 в 08:19
  • 3
    Хороший пост! Это мне очень помогает ! – Oni1 10 December 2014 в 14:40
  • 4
    @Ias: Проверьте, что? – gkalpak 15 December 2014 в 12:01
  • 5
    Спасибо брат! вот хорошая статья по аналогичному вопросу stsc3000.github.io/blog/2013/10/26/… – Terafor 14 August 2015 в 09:24

Вы можете полностью отключить свой сервис от области видимости, но в вашем контроллере можно обновить область асинхронно.

Проблема, с которой вы сталкиваетесь, заключается в том, что вы не знаете, что HTTP-вызовы выполняются асинхронно, а это значит, что вы не получаете значение сразу, как вы могли. Например,

var students = $http.get(path).then(function (resp) {
  return resp.data;
}); // then() returns a promise object, not resp.data

Существует простой способ обойти это, и он должен обеспечить функцию обратного вызова.

.service('StudentService', [ '$http',
    function ($http) {
    // get some data via the $http
    var path = '/students';

    //save method create a new student if not already exists
    //else update the existing object
    this.save = function (student, doneCallback) {
      $http.post(
        path, 
        {
          params: {
            student: student
          }
        }
      )
      .then(function (resp) {
        doneCallback(resp.data); // when the async http call is done, execute the callback
      });  
    }
.controller('StudentSaveController', ['$scope', 'StudentService', function ($scope, StudentService) {
  $scope.saveUser = function (user) {
    StudentService.save(user, function (data) {
      $scope.message = data; // I'm assuming data is a string error returned from your REST API
    })
  }
}]);

Форма:

<div class="form-message">{{message}}</div>

<div ng-controller="StudentSaveController">
  <form novalidate class="simple-form">
    Name: <input type="text" ng-model="user.name" /><br />
    E-mail: <input type="email" ng-model="user.email" /><br />
    Gender: <input type="radio" ng-model="user.gender" value="male" />male
    <input type="radio" ng-model="user.gender" value="female" />female<br />
    <input type="button" ng-click="reset()" value="Reset" />
    <input type="submit" ng-click="saveUser(user)" value="Save" />
  </form>
</div>

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

3
ответ дан 2upmedia 19 August 2018 в 19:20
поделиться

Службы - это синглтоны, и не логично вводить область в службу (что действительно так, вы не можете вводить область в службу). Вы можете передать область как параметр, но это также плохой выбор дизайна, потому что у вас будет область, редактируемая в нескольких местах, что затрудняет ее отладку. Код для обработки переменных области должен идти в контроллере, а служебные вызовы идут на службу.

8
ответ дан Ermin Dedovic 19 August 2018 в 19:20
поделиться
  • 1
    Я понимаю, что вы говорите. Однако в моем случае у меня много контроллеров, и я бы хотел настроить их области с очень похожим набором часов. Как / где вы это сделаете? В настоящее время я действительно передаю объем как параметр службе, которая устанавливает часы $. – moritz 28 October 2015 в 22:09
  • 2
    Возможно, @moritz реализует вторичную директиву (та, которая имеет область: false, поэтому использует область действия, определенную другими директивами), и это делает привязку часов, а также все, что вам нужно. Таким образом, вы можете использовать эту другую директиву в любом месте, где вам нужно определить такие часы. Потому что передача области в службу действительно ужасна :) (поверьте мне, я был там, сделал это, ударил головой о стену в конце) – tfrascaroli 17 May 2016 в 14:04
  • 3
    @TIMINeutron, который звучит намного лучше, чем прохождение вокруг области, я попробую, чтобы в следующий раз сценарий появился! Благодаря! – moritz 17 May 2016 в 14:23
  • 4
    Конечно. Я все еще изучаю себя, и эта конкретная проблема - это проблема, с которой я недавно столкнулся в этом конкретном ключе, и она работала как прелесть для меня. – tfrascaroli 17 May 2016 в 14:30

Хорошо (длинный) ... если вы настаиваете на , чтобы иметь доступ к $scope внутри службы, вы можете:

Создать услугу getter / setter

ngapp.factory('Scopes', function (){
  var mem = {};
  return {
    store: function (key, value) { mem[key] = value; },
    get: function (key) { return mem[key]; }
  };
});

Вставить его и сохранить в нем область управления

ngapp.controller('myCtrl', ['$scope', 'Scopes', function($scope, Scopes) {
  Scopes.store('myCtrl', $scope);
}]);

Теперь получить область внутри другой службы

ngapp.factory('getRoute', ['Scopes', '$http', function(Scopes, $http){
  // there you are
  var $scope = Scopes.get('myCtrl');
}]);
10
ответ дан Jonatas Walker 19 August 2018 в 19:20
поделиться

Вместо того, чтобы пытаться изменить $scope внутри службы, вы можете реализовать $watch в своем контроллере, чтобы просмотреть свойство в своей службе для изменений, а затем обновить свойство на $scope. Вот пример, который вы можете попробовать в контроллере:

angular.module('cfd')
    .controller('MyController', ['$scope', 'StudentService', function ($scope, StudentService) {

        $scope.students = null;

        (function () {
            $scope.$watch(function () {
                return StudentService.students;
            }, function (newVal, oldVal) {
                if ( newValue !== oldValue ) {
                    $scope.students = newVal;
                }
            });
        }());
    }]);

Следует отметить, что в вашей службе, чтобы свойство students было видимым, оно должно быть на службе объект или this следующим образом:

this.students = $http.get(path).then(function (resp) {
  return resp.data;
});
17
ответ дан Keith Morris 19 August 2018 в 19:20
поделиться

Попал в такое же затруднительное положение. Я закончил со следующим. Таким образом, здесь я не вводя объект области в фабрику, но устанавливая $ scope в самом контроллере, используя концепцию обещания, возвращаемую сервисом $ http.

(function () {
    getDataFactory = function ($http)
    {
        return {
            callWebApi: function (reqData)
            {
                var dataTemp = {
                    Page: 1, Take: 10,
                    PropName: 'Id', SortOrder: 'Asc'
                };

                return $http({
                    method: 'GET',
                    url: '/api/PatientCategoryApi/PatCat',
                    params: dataTemp, // Parameters to pass to external service
                    headers: { 'Content-Type': 'application/Json' }
                })                
            }
        }
    }
    patientCategoryController = function ($scope, getDataFactory) {
        alert('Hare');
        var promise = getDataFactory.callWebApi('someDataToPass');
        promise.then(
            function successCallback(response) {
                alert(JSON.stringify(response.data));
                // Set this response data to scope to use it in UI
                $scope.gridOptions.data = response.data.Collection;
            }, function errorCallback(response) {
                alert('Some problem while fetching data!!');
            });
    }
    patientCategoryController.$inject = ['$scope', 'getDataFactory'];
    getDataFactory.$inject = ['$http'];
    angular.module('demoApp', []);
    angular.module('demoApp').controller('patientCategoryController', patientCategoryController);
    angular.module('demoApp').factory('getDataFactory', getDataFactory);    
}());
0
ответ дан VivekDev 19 August 2018 в 19:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: