Basically I got a scenario where the API i'm accessing uses a :groupId as a part of the url in many cases. Is there a easy way to avoid passing $routeParams.groupId in each function call?
var app = angular.module('plunker', []);
app.config(["$routeProvider", function($routeProvider) {
$routeProvider.
when("/:groupId/location", {templateUrl: "partials/location.html", controller: "LocationCtrl"});
}]);
/* Controllers */
app.controller('LocationCtrl', function($scope, $routeParams, Location) {
Location.groupId = $routeParams.groupId; /* Is this even possible? I want to have it available to Location instead of the below*/
Location.query();
Location.query({groupId: $routeParams.groupId});
});
/* Services */
app.service("Location", function($resource, $routeParams) {
return $resource("/api/:groupId/location", {}, {
query: {method: "GET", isArray: true}
});
});
I think you can do this:
app.service("Location", function($resource, $routeParams) {
return $resource("/api/:groupId/location", {groupId:12345}, {
query: {method: "GET", isArray: true}
});
});
I haven't tested it, but I believe that's how the documentation reads
If it's any help, since the above answer didn't work for me, I appended .query to the end of the call.
app.service("Location", function($resource, $routeParams) {
return $resource("/api/:groupId/location", {groupId:'#groupId'})
.query({ groupId: 12345 });
});
Related
Doing this MEAN stack tutorial which basically creates a rudimentary reddit-like clone with a main page showing all posts and, for each separate post, a separate page showing the post and its comments.
Everything worked great until I added the resolve property to the 'home' state of $stateProvider, which (as far as I understand) ensures that all posts are loaded from the database before the view is rendered. After I added resolve, the inline HTML for 'home', which prints out the main page, stopped working. If I comment 'resolve' out, it works again.
I have a hard time debugging this because I'm still learning how everything works (total beginner although I have a basic grasp of OOP).
Can anyone catch what's going on? If you want me to format this post better please let me know.
Link to my Github with the code for this project
The files that I have made edits to are:
app.js (i just added mongoose here)
public/javascripts/angularApp.js (angular module file)
models/posts.js (model for posts)
models/comments.js (model for comments)
routes/index.js (get/post requests etc)
views/index.ejs (main view file)
Here's the angular app javascript code (i have commented out 'resolve' here):
var app = angular.module('flapperNews', ['ui.router']);
<!-- POSTS-service -->
app.factory('posts', ['$http', function($http){
// create object o with an array called posts
var o = {
posts: []
};
o.getAll = function() {
return $http.get('/posts').success(function(data) {
// copy the received data into object o's posts array
angular.copy(data, o.posts);
});
};
return o;
}]);
app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl'
/* resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
} */
})
.state('posts', {
url: '/posts/{id}',
templateUrl: '/posts.html',
controller: 'PostsCtrl'
})
$urlRouterProvider.otherwise('home');
}
]);
<!-- MAIN CONTROLLER -->
app.controller('MainCtrl', ['$scope', 'posts',
function($scope, posts) {
$scope.posts = posts.posts;
<!-- 2way binding eli servicen array päivittyy myös -->
$scope.addPost = function() {
if(!$scope.title || $scope.title == '') { return; }
$scope.posts.push({
title: $scope.title,
upvotes: 0,
link:
$scope.link,
comments: [
{author: 'Joe', body: 'Cool post!', upvotes: 0},
{author: 'Bob', body: 'Great idea but everything is wrong!', upvotes: 0}
]
});
$scope.title='';
$scope.link='';
};
$scope.incrementUpvotes = function (post) {
post.upvotes += 1;
};
}]
);
<!-- POSTS CONTROLLER -->
app.controller('PostsCtrl', ['$scope', '$stateParams', 'posts',
function($scope, $stateParams, posts){
// fetch post with correct ID from posts service and save it into $scope.post
$scope.post = posts.posts[$stateParams.id];
}]);
You can pass the following:
resolve:{
posts: "posts"
}
And then you can use posts as a dependency in your controller. Alternatively you can just make an $http call in the resolve object to return the promise like this and pass the result into the angular controller.
posts: function($http){
return $http({method: 'GET', url: '/postt'})
.then (function (data) {
return doSomeStuffFirst(data);
});
},
If you want to keep current structure in return of posts factory (and not to get only http.data reponse), you can use $q like this:
app.factory('posts', ['$http', '$q', function($http, $q){
var deferred = $q.defer();
// create object o with an array called posts
var o = {
posts: []
};
o.getAll = function() {
$http.get('/posts').then(function(data) {
// copy the received data into object o's posts array
angular.copy(data, o.posts);
deferred.resolve(o);
});
};
return deferred.promise;
}]);
In this case, you'll get full o object in return of promise (using .then function), so you'll be able to reuse this data.
Hi SO angular community !
I'm very confused, I think I have understand the factory purpose and concept, but seems not ...
Here is my problem (surely simple for you) :
I want to use my REST API (working perfectly) using Angular and .factory ...
rest.js
var app = angular.module('urlShortener', ['ngRoute', 'ngResource']);
app.factory('API', ['$resource',
function($resource){
return $resource('/link'});
}],{
get: {method:GET},
post: {method:POST},
put: {method:PUT},
delete: {method:DELETE},
}
);
app.controller('GetAll', function ($scope) {
$scope.links = API.get();
});
index.ejs
<div ng-controller="GetAll">
<ul>
<li ng-repeat="link in links">
<p>{{link.itemId}} --> {{link.url}}</p>
</li>
</ul>
</div>
Not working ... 2 hours I'm consulting the Angular API, and no solutions :/
Please help me I'm wasting time :'(
\\\\ SOLUTION ////
rest.js
app.factory('API', ['$resource', function($resource) { return $resource('/link'); }]);
app.controller('GetAll', ['$scope', 'API', function ($scope, API) {
API.query().$promise.then(function(links) {
$scope.links = links;
});
}]);
Thanks to #dfsq help :)
You can't just assign $resource instance to $scope.links, you need to do it when underlying promise resolves:
app.controller('GetAll', ['$scope', 'API', function ($scope, API) {
API.get().$promise.then(function(links) {
$scope.links = links;
});
}]);
You have to inject "API" in your controller.
app.controller('GetAll', function ($scope, API) {
$scope.links = API.get();
});
If your rest service returns an array of objects you need to use query function.
$scope.links = API.query(); // instead of API.get()
If you need to do anything else when the promise returns use something like this:
API.query().$promise.then(function(result){
$scope.links = result;
// any other operation related to the request here
});
if you want to do api requests, use $http
this is a piece of code I use in my app:
angular
.module('myApp')
.factory('apiFactory', apiFactory);
function apiFactory($http) {
return {
getDataFromApi: getDataFromApi,
};
function getDataFromApi(url) {
return $http({
method: 'GET', // or post or whatever
url: url,
headers: {
...
}
})
.then(success)
.catch(fail);
function success(response) {
return response.data;
}
function fail(response) {
// handle error
}
}
}
Is this what you are looking for?
API For Resources
services.factory('Api', ['$resource',
function($resource) {
return {
Recipe: $resource('/recipes/:id', {id: '#id'}),
Users: $resource('/users/:id', {id: '#id'}),
Group: $resource('/groups/:id', {id: '#id'})
};
}]);
function myCtrl($scope, Api){
$scope.recipe = Api.Recipe.get({id: 1});
$scope.users = Api.Users.query();
...
}
Using angular ui-router I'm trying to use $state.go() to change to the blogEdit state after creating a new entry with blogCreate to continue editing after saving. When I click to save and trigger addPost() method, it doesnt redirect correctly and I see /#/null as the route in the address bar instead of the expected /blog/post/:postId/edit.
blogModule.controller('PostCreateController', ['$scope', '$state', '$stateParams', 'PostResource',
function ($scope, $state, $stateParams, PostResource) {
$scope.post = new PostResource();
$scope.addPost = function () {
$scope.post.$save(function () {
$state.go('blogEdit', {postId: $stateParams.postId}); // THIS SHOULD REDIRECT TO CONTINUE EDITING POST
});
}
}
]);
blogModule.controller('PostEditController', ['$scope', '$stateParams', 'PostResource',
function ($scope, $stateParams, PostResource) {
$scope.post = PostResource.get({postId: $stateParams.postId});
$scope.updatePost = function () {
$scope.post.$update({postId: $stateParams.postId});
}
}
]);
State route configuration:
var app = angular.module('app', [
'ui.router',
'blogModule'
]);
app.config(['$stateProvider', function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('blog', {
url: '/blog',
templateUrl: 'app/blog/view/blog-list.html',
controller: 'PostListController'
})
.state('blogView', {
url: '/blog/post/{postId:[0-9]}',
templateUrl: 'app/blog/view/blog-detail.html',
controller: 'PostViewController'
})
.state('blogCreate', {
url: '/blog/post/new',
templateUrl: 'app/blog/view/blog-create.html',
controller: 'PostCreateController'
})
.state('blogEdit', {
url: '/blog/post/{postId:[0-9]}/edit',
templateUrl: 'app/blog/view/blog-edit.html',
controller: 'PostEditController'
});
}]);
It seems to do this regardless of what state I try to change to.
I suppose you are saving your post on backend. When you perform save (PUT) operation your backend should return you some response. The response should be like HTTP 201 Entity created and there should be location attribute set (f.e. http://example.com/blog/post/1). Then you can get the id from location header like this:
$scope.post.$save(function (createdPost, headers) {
var postId = headers.location.split("/").pop();
$state.go('blogEdit', {postId: postId});
});
Another way is to just ignore headers and return json response from your backend. F.e. {"postId": 1, "title": "New post", ...}. Then you can do something like:
$scope.post.$save(function (createdPost) {
$state.go('blogEdit', {postId: createdPost.postId});
});
The most important is to know API of your backend (what "it returns").
I've had a problem in my previous topic, that I couldn't consume my service.
After doing some research I could finally figure out a way to consume my service after all. Still I was wondering why my other approach with the javascript object as method container didn't work out. I have some guesses but can't find an appropriate solution.
Hopefully you guys can lead me on the right path.
controller.js (Working solution)
angular.module('TodoApp.controllers', []).
controller('listCtrl', function ($scope, $location, todoApiService) {
$scope.todos = todoApiService.query();
});
services.js (Working solution)
angular.module('TodoApp.services', []).
factory('todoApiService', function ($resource) {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
});
controllers.js (Not working solution)
angular.module('TodoApp.controllers', []).
controller('listCtrl', function ($scope, $location, todoApiService) {
$scope.todos = todoApiService.getMyTodos.query();
});
services.js (Not working solution)
angular.module('TodoApp.services', []).
factory('todoApiService', function () {
var todoApi = {};
todoApi.getMyTodos = function ($resource) {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
};
return todoApi;
});
You should either:
Inject $resource to your factory function, just like you did in the working version. And then you can remove the $resource as a parameter for getMyTodos.
angular.module('TodoApp.services', []).
factory('todoApiService', function ($resource) {
var todoApi = {};
todoApi.getMyTodos = function () {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
};
return todoApi;
});
And then from the controller:
angular.module('TodoApp.controllers', []).
controller('listCtrl', function ($scope, $location, todoApiService) {
$scope.todos = todoApiService.getMyTodos().query();
});
Or, you can pass the $resource from the controller to getMyTodos (after injecting it to the controller) - so your controller would look like:
angular.module('TodoApp.controllers', []).
controller('listCtrl', function ($scope, $location, todoApiService, $resource) {
$scope.todos = todoApiService.getMyTodos($resource).query();
});
I didn't check to see that this is working, but it should :)
I'm setting up an access control system in angular. This is how it looks so far.
It's doing the ajax to return the current user's role, then checking that role with the access array to see if the user has permission. If not it redirects.
That all works fine, but the view is still being shown for a split second before the redirect.
It may also be important to note that the ajax request is necessary because the user auth is being handled with Laravel, so I made an API for Angular to talk to get information about the user's session.
var app = angular.module('application', ['ngResource']);
app.config(function($routeProvider){
$routeProvider
.when('/admin', {
controller: 'showAdmin',
templateUrl: 'admin.html',
access: ['Admin', 'Manager'],
resolve: AppCtrl.resolve
});
});
function AppCtrl ($scope, getUser, $location, $rootScope) {
}
AppCtrl.resolve = {
getUser : function($q, $http, $location, $rootScope) {
return $http({
method: 'GET',
url: '/api/getUser'
})
.success(function(data, status) {
$rootScope.user = data;
if($rootScope.access.indexOf(data.permissions[0].role_name) < 0) $location.path('/');
});
}
};
app.run(function ($rootScope, sessionFactory, $location){
$rootScope.$on('$routeChangeStart', function (event, next) {
$rootScope.access = next.access;
});
});
Use an ng-cloak directive on the containing element to eliminate the flicker. See this page for an example along with some CSS/browser-specific gotchas.