TL; DR
1) Ao usar um Factory, você cria um objeto, adiciona propriedades a ele e retorna o mesmo objeto. Quando você passar esta fábrica para o seu controlador, essas propriedades no objeto estarão agora disponíveis nesse controlador através da sua fábrica.
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory('myFactory', function(){
var _artist = 'Shakira';
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2) Ao usar o Serviço , o Angular o instancia nos bastidores com a palavra-chave 'new'. Por isso, você adicionará propriedades a 'this' e o serviço retornará 'this'. Quando você passa o serviço para o seu controlador, essas propriedades agora estarão disponíveis nesse controlador através do seu serviço.
app.controller('myServiceCtrl', function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
Não TL; DR
1)
Fábricas de fábrica são a maneira mais popular de criar e configurar um serviço. Realmente não há muito mais do que o TL; DR disse. Você acabou de criar um objeto, adicionar propriedades a ele e retornar o mesmo objeto. Então, quando você passar a fábrica para o seu controlador, essas propriedades no objeto estarão agora disponíveis nesse controlador através da sua fábrica. Um exemplo mais extenso está abaixo.
app.factory('myFactory', function(){
var service = {};
return service;
});
Agora, quaisquer propriedades que anexarmos a 'service' estarão disponíveis quando passarmos 'myFactory' para o nosso controlador.
Agora vamos adicionar algumas variáveis 'privadas' à nossa função de retorno de chamada. Eles não estarão diretamente acessíveis no controlador, mas, eventualmente, configuraremos alguns métodos getter / setter no 'service' para poder alterar essas variáveis 'privadas' quando necessário.
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
return _finalUrl
}
return service;
});
Aqui você notará que não estamos anexando essas variáveis / funções ao 'serviço'. Estamos simplesmente criando-os para usá-los ou modificá-los posteriormente.
- baseUrl é o URL base que a API do iTunes requer
- _artist é o artista que queremos pesquisar
- _finalUrl é o URL final e totalmente construído para o qual faremos a chamada para o iTunes makeUrl é uma função que cria e retorna nosso URL amigável do iTunes.
Agora que nossas variáveis e funções auxiliares / particulares estão em vigor, vamos adicionar algumas propriedades ao objeto 'service'. O que colocarmos em 'serviço', poderemos usar diretamente em qualquer controlador em que passarmos 'myFactory'.
Vamos criar métodos setArtist e getArtist que simplesmente retornam ou definem o artista. Também vamos criar um método que chamará a API do iTunes com nosso URL criado. Esse método retornará uma promessa que será cumprida assim que os dados voltarem da API do iTunes. Se você não teve muita experiência no uso de promessas no Angular, eu recomendo fazer um mergulho profundo nelas.
Abaixo, o setArtist aceita um artista e permite que você o defina. O getArtist retorna o artista callItunes primeiro chama makeUrl () para criar o URL que usaremos com nossa solicitação $ http. Em seguida, ele configura um objeto de promessa, faz uma solicitação $ http com nosso URL final e, como $ http retorna uma promessa, podemos chamar .success ou .error após nossa solicitação. Em seguida, resolvemos nossa promessa com os dados do iTunes ou a rejeitamos com uma mensagem dizendo 'Houve um erro'.
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
service.setArtist = function(artist){
_artist = artist;
}
service.getArtist = function(){
return _artist;
}
service.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
return service;
});
Agora nossa fábrica está completa. Agora podemos injetar 'myFactory' em qualquer controlador e, em seguida, poderemos chamar nossos métodos que anexamos ao nosso objeto de serviço (setArtist, getArtist e callItunes).
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.data = {};
$scope.updateArtist = function(){
myFactory.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myFactory.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
No controlador acima, estamos injetando no serviço 'myFactory'. Em seguida, definimos propriedades em nosso objeto $ scope que são provenientes de dados de 'myFactory'. O único código complicado acima é se você nunca lidou com promessas antes. Como o callItunes está retornando uma promessa, podemos usar o método .then () e definir apenas $ scope.data.artistData quando nossa promessa for cumprida com os dados do iTunes. Você notará que nosso controlador é muito "fino". Todos os nossos dados lógicos e persistentes estão localizados em nosso serviço, não em nosso controlador.
2) Serviço
Talvez a maior coisa a saber ao lidar com a criação de um Serviço seja o fato de ele ser instanciado com a palavra-chave 'new'. Para os gurus do JavaScript, isso deve fornecer uma grande dica sobre a natureza do código. Para aqueles com conhecimentos limitados em JavaScript ou para aqueles que não conhecem muito bem o que a palavra-chave 'new' realmente faz, vamos revisar alguns fundamentos do JavaScript que eventualmente nos ajudarão a entender a natureza de um Serviço.
Para realmente ver as alterações que ocorrem quando você invoca uma função com a palavra-chave 'new', vamos criar uma função e invocá-la com a palavra-chave 'new', depois vamos mostrar o que o intérprete faz quando vê a palavra-chave 'new'. Os resultados finais serão os mesmos.
Primeiro, vamos criar nosso Construtor.
var Person = function(name, age){
this.name = name;
this.age = age;
}
Essa é uma função típica do construtor JavaScript. Agora, sempre que invocarmos a função Pessoa usando a palavra-chave 'new', 'this' será vinculado ao objeto recém-criado.
Agora, vamos adicionar um método ao protótipo de nossa Person, para que fique disponível em todas as instâncias da nossa classe de Person.
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
Agora, como colocamos a função sayName no protótipo, todas as instâncias de Person poderão chamar a função sayName para alertar o nome dessa instância.
Agora que temos nossa função construtora Person e nossa função sayName em seu protótipo, vamos criar uma instância de Person e chamar a função sayName.
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
Portanto, todo o código para criar um construtor Person, adicionar uma função a seu protótipo, criar uma instância Person e chamar a função em seu protótipo se parece com isso.
var Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
Agora, vamos ver o que realmente está acontecendo quando você usa a palavra-chave 'new' em JavaScript. A primeira coisa que você deve notar é que, depois de usar 'new' em nosso exemplo, podemos chamar um método (sayName) em 'tyler' como se fosse um objeto - é porque é. Então, primeiro, sabemos que nosso construtor Person está retornando um objeto, se podemos ver isso no código ou não. Segundo, sabemos que, como nossa função sayName está localizada no protótipo e não diretamente na instância Person, o objeto que a função Person está retornando deve delegar ao seu protótipo em pesquisas com falha. Em termos mais simples, quando chamamos tyler.sayName (), o intérprete diz: “OK, vou olhar para o objeto 'tyler' que acabamos de criar, localize a função sayName e chame-a. Espere um minuto, não o vejo aqui - tudo o que vejo é nome e idade, deixe-me verificar o protótipo. Sim, parece que está no protótipo, deixe-me chamá-lo. ”.
Abaixo está o código de como você pode pensar sobre o que a palavra-chave 'nova' está realmente fazendo em JavaScript. É basicamente um exemplo de código do parágrafo acima. Coloquei a 'visão do intérprete' ou a maneira como o intérprete vê o código dentro das notas.
var Person = function(name, age){
//The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
//var obj = Object.create(Person.prototype);
//The line directly below this sets 'this' to the newly created object
//this = obj;
this.name = name;
this.age = age;
//return this;
}
Agora, tendo esse conhecimento do que a palavra-chave 'new' realmente faz em JavaScript, a criação de um Serviço em Angular deve ser mais fácil de entender.
A principal coisa a entender ao criar um Serviço é saber que os Serviços são instanciados com a palavra-chave 'new'. Combinando esse conhecimento com nossos exemplos acima, você deve reconhecer agora que anexará suas propriedades e métodos diretamente a 'this', que serão devolvidos pelo próprio Serviço. Vamos dar uma olhada nisso em ação.
Diferentemente do que fizemos originalmente com o exemplo do Factory, não precisamos criar um objeto e, em seguida, retorná-lo, porque, como mencionado muitas vezes antes, usamos a palavra-chave 'new' para que o intérprete crie esse objeto, faça-o delegar para é protótipo e, em seguida, devolva-o para nós sem que tenhamos que fazer o trabalho.
Primeiramente, vamos criar nossa função 'privada' e auxiliar. Isso deve parecer muito familiar, já que fizemos exatamente a mesma coisa com nossa fábrica. Não vou explicar o que cada linha faz aqui, porque fiz isso no exemplo de fábrica; se você estiver confuso, releia o exemplo de fábrica.
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
});
Agora, anexaremos todos os nossos métodos que estarão disponíveis em nosso controlador a 'this'.
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.setArtist = function(artist){
_artist = artist;
}
this.getArtist = function(){
return _artist;
}
this.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
});
Agora, assim como em nossa fábrica, setArtist, getArtist e callItunes estarão disponíveis em qualquer controlador em que passarmos o myService. Aqui está o controlador myService (que é quase exatamente o mesmo que o nosso controlador de fábrica).
app.controller('myServiceCtrl', function($scope, myService){
$scope.data = {};
$scope.updateArtist = function(){
myService.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myService.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
Como mencionei antes, quando você realmente entende o que "novo" faz, os Serviços são quase idênticos às fábricas da Angular.