Im simply grabbing my Github information using Github's API. When I log my http request, it displays the right information.. Im not sure why it's not displaying on the page. I'm not getting any errors. (The partial is displaying, just not the requested data)
Service:
myApp.factory('githubApi', ['$http',
function($http) {
//Declaring a promise that will or will not return a users github information.
return {
async: function() {
return $http.get('https://api.github.com/users/joshspears3');
}
}
}
]);
Controller:
myApp.controller('githubCtrl', [ 'githubApi', '$scope',
function(githubApi, $scope){
$scope.data = githubApi.async();
}
]);
Directive:
myApp.directive('githubRequest', [
function() {
return {
scope: {},
restrict: 'E',
controller: 'githubCtrl',
templateUrl: 'public/views/partials/github-request.html'
}
}
]);
github-request.html (partial):
<p class="component-example-header">Creating a service. Grabbing information based on the github API.</p>
<div>
Making a $http request to grab my personal Github information.
<p>Avatar:</p>
<img width="20%"src="{{data.avatar_url}}" alt="" />
<p>Username: {{data.login}}</p>
<p>Followers: {{data.followers}}, Following: {{data.following}}</p>
</div>
Index.html:
<div>
<global-header></global-header>
<div ui-view></div>
<github-request></github-request>
</div>
You haven't used a promise here. Change your async function to:
return $http.get('https://api.github.com/users/joshspears3').success(function(response) {
return response.data
});
and your controller to:
function(githubApi, $scope){
githubApi.async().then(function(data) {
$scope.data = data;
});
}
Related
You will have to bear with me because I am a self-taught noob.
I have a php page on my server that need a $_get variable to sort the data on the server side rather than the client. I am using routeParams in Angular for the variable to send over. This works, however it only works when you refresh the webpage. Please can someone help me as my head hurts from hitting the wall.
Controller:
app.controller('JuiceController', ['$scope', 'juices', function($scope, juices) {
juices.success(function(data){
$scope.juices = data;
});
}]);
Service:
app.factory('juices', ['$http', '$routeParams',function($http, $routeParams) {
return $http.get('http://madcow-app.dev/application/backend/api/products.php', {
params: {prod: $routeParams.prod}
})
.success(function(data) {
return data;
})
.error(function(err){
return err;
});
}]);
Html output (juice view):
<div class="juice-wrap" ng-repeat="juice in juices">
<div class="juice-img"><img ng-src="{{ juice.imgpath }}" width="163" height="176" alt=""/></div>
<div class="juice-rght">
<div class="juice-title">{{ juice.name }}</div>
<div class="juice-desc">{{ juice.descrip }}</div>
Route provider
$routeProvider
.when('/', {
templateUrl: 'script/views/home.html'
})
.when('/categories/', {
controller: 'CatController',
templateUrl: 'script/views/categories.html'
})
.when('/juice/:prod', {
controller: 'JuiceController',
templateUrl: 'script/views/juice.html'
})
.when('/events/', {
controller: 'EventController',
templateUrl: 'script/views/events.html'
})
.when('/qr/', {
templateUrl: 'script/views/qr.html'
})
.when('/feedback/', {
templateUrl: 'script/views/feedback.html'
})
.otherwise({
redirectTo: '/'
php function outputs json (this is outputted to the php controller below and takes the category id as a variable:)
return json_encode($results);
To the php controller (this is the page that the angular service/factory pulls the json array of products from:
<?php
include "../../init.php";
if (isset($_GET['prod']))
{
echo $MC->Api->getProductsApi($_GET['prod']);
}
else
{
echo 'error';
}
This is the category html:
<div class="cat-btn" ng-repeat="cat in cats">
<a href="#/juice/{{cat.catid}}">
<img ng-src="{{ cat.imgpath }}" width="363" height="195" alt=""/>
<div class="cat-btn-text"> {{ cat.name }} </div>
</a>
basically what I want to achieve is when a user clicks a category in the frontend, angular routes to the product view using the category id as a filter for the php function to populate the json output with only the juices in that category.
I'm not sure if I should be doing it this way around, or whether I need to hit it from another angle. Please bear in mind that i am a complete javascript noob and laymans would be great for the answer.
Thank you in advance.....
Factory:
app.factory('juices', [
'$http', '$routeParams', function ($http, $routeParams) {
var self = this;
function getJuices() {
$http.get('http://madcow-app.dev/application/backend/api/products.php', {
params: {prod: $routeParams.prod}
})
.success(function (data) {
self.juices = data.data;
})
.error(function (err) {
});
}
return {
getJuices: getJuices,
juices: self.juices
}
}
]);
Controller:
app.controller('JuiceController', [
'$scope', 'juices', function ($scope, juices) {
juices.getJuices();
$scope.$watch(function () {
return juices.juices
}, function (newJuices, oldJuices) {
// This is triggered when juices.juices changes
// newJuices containes the juices retrieved from server
// This is needed because it is asynchronous
});
}
]);
Goal:
In my application every controller should be initialized after a user has a session / is logged in, because in this controller I use the data of logged in user.
Code:
app.js
app.run(function($q, $rootScope, AuthSvc){
$rootScope.ajaxCall = $q.defer();
AuthSvc.reloadSession().then(
function(response){
if(response!=null && response!=undefined){
$rootScope.activeUserSession = response;
$rootScope.ajaxCall.resolve();
}else{
$rootScope.activeUserSession = null;
$rootScope.ajaxCall.reject();
}
});
return $rootScope.ajaxCall.promise;
});
routes.js
.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/timeTracking', {
templateUrl: 'partials/timeTracking/projectView.html',
controller: 'timeTrackingController',
resolve: {
response: function($rootScope, $q) {
var defer = $q.defer();
$rootScope.ajaxCall.promise.then(
function(){
defer.resolve();
return defer.promise;
});
}
}
}).
Problem: Controller gets initialized sometimes before the user has a session, I do not understand why.
Sorry I am new to Angular and my english is also crap, so I hope nevertheless you can understand what is my problem.
I think placing your session reload into the app.run is not the right place. Add it directly to resolve and checkout the docs for $q to learn how promises are working.
Because you can't call a promise or defer. You need to call a function that's returning a promise then you can add your then method to do your stuff after the promise is resolved.
Please have a look at the demo below or here at jsfiddle.
It's just an asynchronous method with $timeout to simulate the auth because I don't have a backend to add in the demo.
You can add your AuthSvc directly into resolve.
angular.module('demoApp', ['ngRoute'])
.controller('timeTrackingController', function($scope, response) {
$scope.data = response;
})
.factory('authService', function($q, $timeout) {
return {
reloadSession: function() {
var deferred = $q.defer();
$timeout(function() {
// just simulate session reload
// real api would to the job here
console.log('resolved defer now!!');
deferred.resolve({dummyData: 'hello from service'});
}, 1000);
return deferred.promise;
}
}
})
.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/timeTracking', {
templateUrl: 'partials/timeTracking/projectView.html',
controller: 'timeTrackingController',
resolve: {
response: function(authService) {
return authService.reloadSession().then(function(data) {
return data;
})
}
}
})
.otherwise('/timeTracking');
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular-route.js"></script>
<div ng-app="demoApp">
<script type="text/ng-template" id="partials/timeTracking/projectView.html">
project view: {{data | json}}
</script>
<div ng-view=""></div>
</div>
I'm working on a mobile app using AngularJS as a framework, currently I have a structure similar to this:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'pages/home.html',
controller : 'homeCtrl'
})
.when('/one', {
templateUrl : 'pages/one.html',
controller : 'oneCtrl'
})
.when('/two', {
templateUrl : 'pages/two.html',
controller : 'twoCtrl'
});
}]);
app.controller('homeCtrl', ['$scope', function($scope) {
}]);
app.controller('oneCtrl', ['$scope', function($scope) {
}]);
app.controller('twoCtrl', ['$scope', function($scope) {
}]);
And then I'm displaying the content with an ng-view:
<div class="ng-view></div>
Things are working well but I need to load data from a JSON file to populate all the content of the app. What I want is to make and an AJAX call only once and then pass the data through all my different controllers. In my first attempt, I thought to create a Service with an $http.get() inside of it and include that in every controller, but it does not work because it makes a different ajax request everytime I inject and use the service. Since I'm new using angular I'm wondering what is the best way or the more "angular way" to achieve this without messing it up.
Edit: I'm adding the code of the service, which is just a simple $http.get request:
app.service('Data', ['$http', function($http) {
this.get = function() {
$http.get('data.json')
.success(function(result) {
return result;
})
}
});
Initialize the promise once, and return a reference to it:
No need to initialize another promise. $http returns one.
Just tack a .then() call on your promise to modify the result
angular.module('app', [])
.service('service', function($http){
this.promise = null;
function makeRequest() {
return $http.get('http://jsonplaceholder.typicode.com/posts/1')
.then(function(resp){
return resp.data;
});
}
this.getPromise = function(update){
if (update || !this.promise) {
this.promise = makeRequest();
}
return this.promise;
}
})
Codepen example
Edit: you may consider using $http cache instead. It can achieve the same results. From the docs:
If multiple identical requests are made using the same cache, which is not yet populated, one request will be made to the server and remaining requests will return the same response.
Try this to get JSON Data from a GET Link:
(function (app) {
'use strict';
app.factory('myService', MyService);
MyService.$inject = ['$q', '$http'];
function MyService($q, $http) {
var data;
var service = {
getData: getData
};
return service;
//////////////////////////////////////
function getData(refresh) {
if (refresh || !data) {
return $http.get('your_source').then(function(data){
this.data = data;
return data;
})
}
else {
var deferrer = $q.defer();
deferrer.resolve(data);
return deferrer.promise;
}
}
}
}(angular.module('app')));
Now you can add this dependency in your controller file and use:
myService.getData().then(function(data){
//use data here
}, function(err){
//Handle error here
});
I am receiving an external object from WordPress, in one view I have the post.title, and if you click in that title you can go to another view and see te entire post.content.
So far, I can not see the entire post because I am getting a couple errors, posts is undefined.
I made a Plunkr, CodePen and one JSBin for you to understand easier. If you use JSBin is better because you can use the console which is integrated there. All you have to do is click on the title of the post, and you are going to realize that can not go to the other view.
Here is the code regarding my issue, which is the same you will see in the online editors I post above
.state('tabs', {
url: "/tabs",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.news', {
url: "/news",
views: {
'tab-news': {
templateUrl: "tab-news.html",
controller: 'NewsCtrl'
}
}
})
.state('tabs.post-detail', {
url: '/news/:postId',
views: {
'tab-news': {
templateUrl: 'tab-post-detail.html',
controller: 'PostDetailCtrl'
}
}
})
the html for the main view, news
<a ng-href="#/tabs/news/{{post.ID}}">
<h2 ng-bind-html="post.title"></h2>
<p>{{post.date | date}}</p>
</a>
and here is the view where you can not enter yet, the view where are redirected after clicking in the title on the main view
<div>
<h3>{{:: post.title}}</h3>
<p>{{:: post.content}}</p>
</div>
now the controller for the main view
.controller('NewsCtrl', function($scope, $ionicLoading, FreshlyPressed) {
$scope.posts = [];
$scope.doRefresh = function() {
$scope.posts = FreshlyPressed.getBlogs($scope);
}
});
here the service
angular.module('urbanet.app.service', [])
.service('FreshlyPressed', function($http) {
return {
getBlogs: function($scope) {
$scope.posts = [];
$http.jsonp('https://public-api.wordpress.com/rest/v1.1/freshly-pressed?callback=JSON_CALLBACK')
.success(function(result) {
$scope.posts = result.posts;
});
},
get: function(postId, $scope) {
console.log(postId);
console.log($scope.posts);
for (var i = 0; i < $scope.posts.length; i++) {
if ($scope.posts[i].id === parseInt(postId)) {
return $scope.posts[i];
}
}
return null;
}
}
})
and controller for the second view, the view of the entire post
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed) {
$scope.post = FreshlyPressed.get($stateParams.postId, $scope);
});
You are going to have to make a new request for individual posts
The freshly-pressed api returns to you a siteID and postId.
You then combine those to use the posts API to get the single post.
Since freshly-pressed is a constantly changing list, this is the only way you would ever be able to bookmark posts. Tomorrow you may not get the same set of main posts, so wouldn't be able to replicate links that were used today the way you are attempting to
Service method:
getPostById: function(siteId,postId ) {
var url ='https://public-api.wordpress.com/rest/v1.1/sites/'+siteId+'/posts/'+postId+'?callback=JSON_CALLBACK'
return $http.jsonp(url)
}
controller
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed) {
var postId = $stateParams.postId,
siteId = $stateParams.siteId;
FreshlyPressed.getPostById(siteId,postId).success(function(response){
$scope.post = response
})
});
Modify links accordingly
<a ng-href="#/tabs/news/{{post.site_ID}}/{{post.ID}}">
Working demo
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed) {
$scope.post = FreshlyPressed.get($stateParams.postId, $scope);
});
This is a problem. You are trying to pass the entire $scope object as a parameter?
What exactly do you want to pass to the get call?
You have this:
get: function(postId, $scope) { //<- that shouldn't be $scope, make it 'post' or something
console.log(postId);
console.log($scope.posts); //Notice that this comes back undefined?
for (var i = 0; i < $scope.posts.length; i++) { //<- length throws an error because there isn't anything there.
if ($scope.posts[i].id === parseInt(postId)) {
return $scope.posts[i];
}
}
return null;
}
You shouldn't be trying to pass $scope as a param to your service then access $scope inside the factory.
Have your service store your posts data and return that data to your controllers. You can do something like this:
How to make multiple http requests?
Don't try to pass in $scope. Just send the post id to the service and return the data you need.
As #tpie and #charlietfl already mentioned, the current approach (passing $scope as parameter to the service) doesn't work. Another alternative to what tpie suggested (caching the posts data in the service) might be using resolve and promises:
Service code
.service('FreshlyPressed', function($q, $http) {
return {
getBlogs: function() {
var deferred = $q.defer();
$http.jsonp('https://public-api.wordpress.com/rest/v1.1/freshly-pressed?callback=JSON_CALLBACK')
.success(function(result) {
deferred.resolve(result.posts);
});
return deferred.promise;
},
get: function (postId, posts) {
/* snipped, this wasn't the problematic part */
}
}
})
This won't cache the posts result in your service, which might be a downside if the $http call you are making is heavy, but using promises is a nice way of setting up asynchronous requests.
Then corresponding state config
.state('tabs', {
url: "/tabs",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.news', {
url: "/news",
views: {
'tab-news': {
templateUrl: "tab-news.html",
controller: 'NewsCtrl'
}
},
resolve: {
posts: function (FreshlyPressed) {
return FreshlyPressed.getBlogs();
}
}
})
.state('tabs.post-detail', {
url: '/news/:postId',
views: {
'tab-news': {
templateUrl: 'tab-post-detail.html',
controller: 'PostDetailCtrl'
}
},
resolve: {
posts: function (FreshlyPressed) {
return FreshlyPressed.getBlogs();
}
}
})
This will invoke the getBlogs function in your service and wait for the promise to be resolved. After that, you can inject the resolved posts variable into your controllers:
and finally controllers
.controller('NewsCtrl', function($scope, $ionicLoading, FreshlyPressed, posts) {
$scope.posts = posts;
$scope.doRefresh = function() {
FreshlyPressed.getBlogs()
.then(function (posts) {
$scope.posts = posts;
});
}
});
Here we can set up the $scope.posts variable directly using the resolved posts variable. The doRefresh function needs then to be modified so that it will invoke the service function and after the promise is resolved, set the $scope.posts data accordingly.
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed, posts) {
$scope.post = FreshlyPressed.get($stateParams.postId, posts);
});
And here we give the service get function the resolved posts variable as parameter, as in this case the service isn't caching that data.
This isn't problem-free approach, for example consider following scenario: you get one set of posts data into your main controller. Then, there are new posts posted into the wordpress you are querying, before your user clicks on a title. This might cause the PostDetailCtrl to receive a new set of posts data, that doesn't any longer contain the certain post that your user clicked.
I think this is anyway a viable alternative, and at least a bit of food for thought.
At the moment, the url localhost/view/titles will use the route, controller and service below, and the server will return a list of all title objects. How do I extend the service to allow for additional query params, such as a result limit etc?
// main app module with route
var app = angular.module('app', ['ngResource']).
config(function ($routeProvider, $locationProvider) {
$routeProvider.when(
'/view/:obj/:limit',
{
templateUrl: '/static/templates/titles.html',
controller: 'titlesController'
}
)})
// list service
var listService = app.factory('listService', function ($q, $resource) {
return {
getList: function (obj) {
var deferred = $q.defer();
$resource('/api/view/' + obj).query(
function (response) {
console.log('good')
deferred.resolve(response);
}
,
function (response) {
console.log('bad ' + response.status)
deferred.reject(response);
}
)
return deferred.promise;
}
}
}
)
// controller
var titlesController = bang.controller('titlesController', function($scope, listService, $routeParams){
$scope.titles = listService.getList($routeParams.obj);
})
Below is the sample code:
angular.module('phonecatServices', ['ngResource']).
factory('Phone', function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
});
This is a broader answer to the question of how to pass params to your backend api with a query string using the ngResource module since I couldn't find straight forward instructions anywhere else.
ngResource Setup:
Install the ngResource package from the command line with bower or npm bower install angular-resource.
In the head element of the index.html page add the angular-resource script
<script src="lib/angular-resource/angular-resource.min.js"></script>
js/app.js: Add the dependencies. I'm leaving out the routes since I use ui-router which is a separate topic.
angular.module('app', ['app.controllers', 'app.services', 'ngResource'])
The view: templates/list.html
<input type="search" ng-model="filters.title" placeholder="Search">
<button ng-click="searchList(filters)">Submit</button>
<div ng-repeat="item in list">
<p>{{item.title}} - {{item.description}}</p>
</div>
The controller: js/controllers.js
.controller('ListCtrl', function($scope, ListService) {
$scope.searchList = function(filters){
$scope.filters = { title: '' }; //This will clear the search box.
$scope.list = ListService.query({title: filters.title});
}
})
The factory: js/services.js. Assumes you also will be doing get requests by the item id. If not leave out /:id, {id: '#id'}
.factory('ListService', function($resource) {
return $resource('http://localhost:3000/api/view/:id', { id: '#id' });
})