O $scope
que você vê sendo injetado nos controladores não é um serviço (como o resto das coisas injetáveis), mas é um objeto Scope. Muitos objetos de escopo podem ser criados (geralmente herdando prototipicamente de um escopo pai). A raiz de todos os escopos é o $rootScope
e você pode criar um novo escopo filho usando o $new()
método de qualquer escopo (incluindo o $rootScope
).
O objetivo de um Escopo é "unir" a apresentação e a lógica de negócios de seu aplicativo. Não faz muito sentido passar um $scope
para um serviço.
Serviços são objetos singleton usados (entre outras coisas) para compartilhar dados (por exemplo, entre vários controladores) e geralmente encapsulam pedaços de código reutilizáveis (uma vez que podem ser injetados e oferecer seus "serviços" em qualquer parte de seu aplicativo que precise deles: controladores, diretivas, filtros, outros serviços, etc.).
Tenho certeza de que várias abordagens funcionariam para você. Uma é a seguinte:
como o StudentService
é responsável por lidar com os dados dos alunos, você pode StudentService
manter uma matriz de alunos e deixá-la "compartilhá-la" com quem estiver interessado (por exemplo, o seu $scope
). Isso faz ainda mais sentido, se houver outras visualizações / controladores / filtros / serviços que precisam ter acesso a essas informações (se não houver nenhuma agora, não se surpreenda se elas começarem a aparecer logo).
Cada vez que um novo aluno é adicionado (usando o save()
método do serviço ), a própria matriz de alunos do serviço será atualizada e todos os outros compartilhamentos de objetos dessa matriz também serão atualizados automaticamente.
Com base na abordagem descrita acima, seu código pode ser assim:
angular.
module('cfd', []).
factory('StudentService', ['$http', '$q', function ($http, $q) {
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 and
// wait for a response.
// This method returns a promise to emulate what would happen
// when actually communicating with 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;
}
}
}
return $q.resolve(student);
};
// Populate the students array with students from the server.
$http.get(path).then(function (response) {
response.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.
// Once the operation is completed, the $scope's `students`
// array will be automatically updated, since it references
// the StudentService's `students` array.
StudentService.save(student).then(function () {
// Do some more $scope-specific stuff,
// e.g. show a notification.
}, function (err) {
// Handle the error.
});
};
}
]);
Uma coisa que você deve ter cuidado ao usar essa abordagem é nunca reatribuir o array do serviço, porque então quaisquer outros componentes (por exemplo, escopos) ainda farão referência ao array original e seu aplicativo será interrompido.
Por exemplo, para limpar a matriz em StudentService
:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
Veja, também, esta pequena demonstração .
LITTLE UPDATE:
Algumas palavras para evitar a confusão que pode surgir ao falar sobre o uso de um serviço, mas não criá-lo com a service()
função.
Citando os documentos em$provide
:
Um serviço Angular é um objeto singleton criado por uma fábrica de serviços . Essas fábricas de serviços são funções que, por sua vez, são criadas por um provedor de serviços . Os provedores de serviços são funções construtoras. Quando instanciados, eles devem conter uma propriedade chamada $get
, que contém a função de fábrica de serviço .
[...]
... o $provide
serviço tem métodos auxiliares adicionais para registrar serviços sem especificar um provedor:
- provedor (provedor) - registra um provedor de serviços com o $ injetor
- constante (obj) - registra um valor / objeto que pode ser acessado por provedores e serviços.
- valor (obj) - registra um valor / objeto que só pode ser acessado por serviços, não por provedores.
- factory (fn) - registra uma função de fábrica de serviço, fn, que será empacotada em um objeto de provedor de serviço, cuja propriedade $ get conterá a função de fábrica fornecida.
- serviço (classe) - registra uma função construtora, classe que será envolvida em um objeto de provedor de serviço, cuja propriedade $ get instanciará um novo objeto usando a função construtora fornecida.
Basicamente, o que ele diz é que todo serviço Angular é registrado usando $provide.provider()
, mas existem métodos de "atalho" para serviços mais simples (dois dos quais são service()
e factory()
).
Tudo se resume a um serviço, então não faz muita diferença o método que você usa (contanto que os requisitos para seu serviço possam ser cobertos por esse método).
BTW, provider
vs service
vs factory
é um dos conceitos mais confusos para os novatos do Angular, mas felizmente existem muitos recursos (aqui no SO) para tornar as coisas mais fáceis. (Basta pesquisar.)
(Espero que isso esclareça - me avise se não resolver).
service
oufactory
- você terminará com um serviço Angular . Apenas certifique-se de entender como cada um funciona e se ele atende às suas necessidades.$scope.students
ficará vazio se a chamada ajax não for concluída? Ou$scope.students
será parcialmente preenchido, se este bloco de código estiver funcionando em andamento?students.push(student);
Em vez de tentar modificar o
$scope
dentro do serviço, você pode implementar um$watch
em seu controlador para observar uma propriedade em seu serviço quanto a alterações e, em seguida, atualizar uma propriedade no$scope
. Aqui está um exemplo que você pode experimentar em um controlador:Uma coisa a observar é que dentro do seu serviço, para que a
students
propriedade seja visível, ela precisa estar no objeto Serviço ou algothis
assim:fonte
Bem (longa) ... se você insiste em ter
$scope
acesso dentro de um serviço, você pode:Crie um serviço getter / setter
Injete-o e armazene o escopo do controlador nele
Agora, pegue o escopo dentro de outro serviço
fonte
Os serviços são singletons e não é lógico que um escopo seja injetado no serviço (o que é o caso, você não pode injetar o escopo no serviço). Você pode passar o escopo como um parâmetro, mas isso também é uma escolha de design ruim, porque você teria o escopo sendo editado em vários lugares, dificultando a depuração. O código para lidar com variáveis de escopo deve ir para o controlador e as chamadas de serviço vão para o serviço.
fonte
Você pode tornar seu serviço completamente inconsciente do escopo, mas em seu controlador permitir que o escopo seja atualizado de forma assíncrona.
O problema que você está tendo é porque você não sabe que as chamadas http são feitas de forma assíncrona, o que significa que você não obtém um valor imediatamente como deveria. Por exemplo,
Existe uma maneira simples de contornar isso e é fornecer uma função de retorno de chamada.
A forma:
Isso removeu parte da sua lógica de negócios por brevidade e, na verdade, não testei o código, mas algo como isso funcionaria. O conceito principal é passar um retorno de chamada do controlador para o serviço que será chamado posteriormente no futuro. Se você estiver familiarizado com o NodeJS, este é o mesmo conceito.
fonte
.then
métodos de promessa são um antipadrão .Enfrentou a mesma situação. Acabei com o seguinte. Portanto, aqui não estou injetando o objeto de escopo na fábrica, mas configurando o $ escopo no próprio controlador usando o conceito de promessa retornado pelo serviço $ http .
fonte
O código para lidar com variáveis de escopo deve ir para o controlador e as chamadas de serviço vão para o serviço.
Você pode injetar
$rootScope
com o propósito de usar$rootScope.$broadcast
e$rootScope.$on
.Caso contrário, evite injetar
$rootScope
. Vejo$rootScope
existe, mas pode ser usado para o mal .fonte