I have a page where I display all usernames. Now I want to click on one of these usernames make a call to server to retrieve more information and display it on separate User page. (First name, last name, etc)
My problem is that when I click on username page opens but fields are not populated. Could you please review my code and suggest what I am doing wrong there?
app.config(function($routeProvider) {
$routeProvider
.when("/", {
templateUrl : "pages/login_page.html"
})
.when("/userpage", {
controller : 'UserController',
templateUrl : "pages/user_page.html"
})
.when("/allusers", {
controller : 'AllUserController',
templateUrl : "pages/all_users.html"
});
});
This is my login code. After user authenticated it can see all other users. So I am changing view to #allusers
app.directive("loginForm", function (AuthorizedHttp, ConfigurationRepository, UserObj) {
return {
restrict: 'E',
scope: {
},
templateUrl: 'templates/login-template.html',
replace: 'true',
controller: ['$scope', '$http', '$window',
function($scope, $location, $window) {
$scope.loginError = false;
$scope.login = function () {
$scope.loginError = false;
UserObj.setState(null, null, $scope.username, $scope.password, null);
AuthorizedHttp.get('http://{0}/account/user/login'.format(ConfigurationRepository.getBackendHostName()))
.success(function (response) {
UserObj.setState(response.first_name, response.last_name, response.email, $scope.password, response.role, response.timezones);
$window.location = "#allusers";
})
.error(function (err, status) {
$scope.username = '';
$scope.password = '';
$scope.loginError = true;
})
}
}
]
}
});
Code below responsible to make a call and retrieve all users. Works fine.
app.controller('AllUserController', function ($scope, AuthorizedHttp, ConfigurationRepository, UserObj, UserCurrent, TimezoneService) {
$scope.init = function () {
TimezoneService.getAllUsers()
.success(function (response) {
$scope.users_emails = response.map(function (item) {return item.email})
})
.error(function (err, status) {
alert('Error loading all users ')
});
};
});
HTML to display all usernames. Also set ng-click to pass a username as parameter to retrieve required user.
<div ng-controller="AllUserController" ng-init="init()">
<div ng-controller="UserController">
<div ng-repeat="email in users_emails" class="item-unchecked">
<a ng-click="getUser(email)">{{email}}</a>
</div>
</div>
</div>
User controller. Executed every time I click on username link.
app.controller('UserController', function ($scope, AuthorizedHttp, ConfigurationRepository, UserObj, UserCurrent, TimezoneService, $window) {
$scope.user_display_name = 'Now set yet';
$scope.getUser = function(username) {
TimezoneService.getUser(username)
.then(function (response) {
$scope.required_user = response.data;
$scope.user_display_name = '{0} ({1})'.format(response.data.first_name, response.data.email);
$scope.user_timezones = response.data.timezones.map(function (item) {
return item.timezone_name
});
$scope.user_role = response.data.role;
$window.location = '#userpage';
});
};
});
As a result user_page.html is loaded but all fields are not set. I don't understand why since I am setting a scope value before I change a $window.location.
Remove ng-controller="UserController" from your HTML
Create a function in your AllUserController like that
$scope.customNavigate = function(routeToNavigate, routeParameter){
$location.path("/" + routeToNavigate + "/" + routeParameter);
}
Change .when("/userpage", { to .when("/userpage/:email", {
Change ng-click="getUser(email)" to ng-click="customNavigate("userpage", email)"
Inject $routeParams to your UserController
Change $scope.getUser = function(username) { to function getUser (username) {
Call getUser($routeParams.email) in your UserController.
Related
I am writing a rather simple crud app, however, i seem to be stuck on the edit (Edit Controller) portion code. i have a list of student, i select one for update . but i get the error "Expected response to contain an object but got an array".
When i query the webservice directly, i get
But when i inspect elements and go to the network tab, i see this
here is my code.
var StudentManagement = angular.module('StudentManagement', ['ngRoute','ngResource','ui.bootstrap']);
StudentManagement.config(function ($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "list.html",
controller: "HomeController"
})
.when("/add", {
templateUrl: "add.html",
controller: "AddController"
})
.when("/edit/:editId", {
templateUrl: "edit.html",
controller: "EditController"
})
.otherwise({
redirectTo: "/"
});
});
StudentManagement.factory('Student', function ($resource) {
return $resource('/api/Student/:id', { id: '#id' }, { update: { method: 'PUT' } });
});
StudentManagement.controller("HomeController",
function ($scope, $location, Student) {
$scope.search = function () {
$scope.students = Student.query();
};// end search
$scope.reset = function ()
{
$scope.search();
}// end reset function
$scope.search();
});
StudentManagement.controller("EditController",
function ($scope, $location, $routeParams, Student) {
// get the student given the specific id
var id = $routeParams.editId;
$scope.student=Student.get({id: id});
$scope.updateForm = function () {
Student.update({id:id}, $scope.student, function () {
$location.path('/');
});
}// end edit function
$scope.cancelForm = function () {
$location.path('/');
}// end cancel function
});
You are returning array of object from server.
So,you should add isArray : true in resource defination.
$resource('/api/Student/:id', { id: '#id' },
{ update: { method: 'PUT',isArray : true}
});
Or
you can return object of object from server
if you want to make current code workable
I have set up a service to return a listing of clients from my API. Using UI-router, I can successfully pass a client's id to the details state - however, it seems unnecessary here to make another API call to retrieve a single client when I have all the necessary data in my controller.
What is the best way to use the ID in my detail state URL to show data for that client? Also - if a user browses directly to a client detail URL - I'll need to then make a call to the API to get just that client data - or is there a better way?
EDIT: I am not looking to load the two views on the same 'page', but completely switch views here, from a listing page to a detail page.
Routes in App.js
$stateProvider
.state('root', {
abstract: true,
url: '',
views: {
'#': {
templateUrl: '../partials/icp_index.html',
controller: 'AppController as AppCtrl'
},
'left-nav#root': {
templateUrl: '../partials/left-nav.html'
},
'right-nav#root': {
templateUrl: '../partials/right-nav.html'
},
'top-toolbar#root': {
templateUrl: '../partials/toolbar.html'
}
/*'footer': {
templateUrl: '../partials/agency-dashboard.html',
controller: 'AppController as AppCtrl'
}*/
}
})
.state('root.clients', {
url: '/clients',
views: {
'content#root': {
templateUrl: '../partials/clients-index.html',
controller: 'ClientsController as ClientsCtrl'
}
}
})
.state('root.clients.detail', {
url: '/:clientId',
views: {
'content#root': {
templateUrl: '../partials/client-dashboard.html',
//controller: 'ClientsController as ClientsCtrl'
}
}
})
// ...other routes
Service, also in app.js
.service('ClientsService', function($http, $q) {
this.index = function() {
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients')
.then(function successCallback(response) {
console.log(response.data);
deferred.resolve(response.data);
},
function errorCallback(response) {
// will handle error here
});
return deferred.promise;
}
})
And my controller code in ClientsController.js
.controller('ClientsController', function(ClientsService) {
var vm = this;
ClientsService.index().then(function(clients) {
vm.clients = clients.data;
});
});
And finally, my listing page clients-index.html
<md-list-item ng-repeat="client in ClientsCtrl.clients" ui-sref="clients-detail({clientId : client.id })">
<div class="list-item-with-md-menu" layout-gt-xs="row">
<div flex="100" flex-gt-xs="66">
<p ng-bind="client.name"></p>
</div>
<div hide-xs flex="100" flex-gt-xs="33">
<p ng-bind="client.account_manager"></p>
</div>
</div>
</md-list-item>
You can use inherited states like suggested here.
$stateProvider
// States
.state("main", {
controller:'mainController',
url:"/main",
templateUrl: "main_init.html"
})
.state("main.details", {
controller:'detailController',
parent: 'main',
url:"/:id",
templateUrl: 'form_details.html'
})
Your service does not change.
Your controllers check if the Model has been retrieved:
app.controller('mainController', function ($scope, ClientsService) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
});
})
app.controller('detailController', function ($q, $scope, ClientsService, $stateParams) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
$scope.Item = data[$stateParams.id];
});
})
See
http://plnkr.co/edit/I4YMopuTat3ggiqCoWbN?p=preview
[UPDATE]
You can also, if you must, combine both controllers:
app.controller('mainController', function ($q, $scope, ClientsService, $stateParams) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
$scope.Item = data[$stateParams.id];
});
})
I would change the service to cache the data. With $q.when() you can return a promise from a variable. So you save your response in a variable, and before doing the API call you check if the cache has been set. If there is any cache, you return the data itself. Otherwise, you do the usual promise call.
.service('ClientsService', function($http, $q) {
var clients = null;
this.getClient = function(id) {
if (clients !== null) {
return $q.when(id ? clients[id] : clients);
}
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients').then(function(response) {
clients = response.data;
deferred.resolve(id ? clients[id] : clients);
}, function (response) {
// will handle error here
});
return deferred.promise;
}
})
I am trying to route my page to another page once the controller is accessed but its not working. I can route the first two pages but the third one is not working. Can someone help me on this.
This is my routing code:
$routeProvider', function ($routeProvider) {
$routeProvider.
when('/category', {
//templateUrl : 'js/partials/course-list.html',
controller : 'CategoryController'
}).
when('/category/:categoryid', {
templateUrl : 'js/partials/film-list.html',
controller : 'MovieController'
}).
when('/actor/:filmid', {
templateUrl : 'js/partials/actor-list.html',
controller : 'ActorController'
}).
otherwise({
redirectTo : '/'
});
}
Currently my ActorController is not working. Once i click on the movies it should show the actor of the films but in my case its not working
This is my partial html file for the movie-list.html
<section>
<h3>{{movieCount}}</h3>
<table>
<tr data-ng-repeat="movie in movies" data-ng-click="selectFilm($event,movie)" style="cursor: pointer;">
<td>{{movie.title}}</td>
</tr>
<strong>{{successMessage}}</strong>
</table>
And this is my controller code
).controller('ActorController',
[
'$scope',
'dataService',
'$routeParams',
function ($scope, dataService, $routeParams){
$scope.actors = [ ];
$scope.actorCount = 0;
var getActors = function (moviecode) {
dataService.getActors(moviecode).then(
function (response) {
$scope.actorCount = response.rowCount + ' actors';
$scope.actors = response.data;
$scope.showSuccessMessage = true;
$scope.successMessage = "Actor Success";
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
// only if there has been a courseid passed in do we bother trying to get the students
if ($routeParams && $routeParams.filmid) {
console.log($routeParams.filmid);
getActors($routeParams.filmid);
}
}
]
)
This is the selectFilm() code from the MovieController
$scope.selectedFilm = {};
$scope.selectFilm = function ($event,movie) {
$scope.selectedFilm = movie;
$location.path('/actor/' + movie.film_id);
}
This is the movie controller code
).controller('MovieController',
[
'$scope',
'dataService',
'$routeParams',
'$location',
function ($scope, dataService, $routeParams, $location){
$scope.movies = [ ];
$scope.movieCount = 0;
var getMovies = function (moviecode) {
dataService.getMovies(moviecode).then(
function (response) {
$scope.movieCount = response.rowCount + ' movies';
$scope.movies = response.data;
$scope.showSuccessMessage = true;
$scope.successMessage = "Movies Success";
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
$scope.selectedFilm = {};
$scope.selectFilm = function ($event,movie) {
$scope.selectedFilm = movie;
$location.path('/actor/' + movie.film_id);
// $window.location.href = '/actor/' + movie.film_id
console.log(movie.film_id);
}
// only if there has been a courseid passed in do we bother trying to get the students
if ($routeParams && $routeParams.categoryid) {
console.log($routeParams.categoryid);
getMovies($routeParams.categoryid);
}
}
]
)
I solved the problem by myself wher first of all the $location variable was not defined in the function and later on the movie object dont have the film_id so I had to readjust my SQL query to make it work. After changing the SQL query i can route my page now.
I am trying to send an ID through to a controller using $routeParams via a factory but it is not working.
My $routeProvider:
.when('/event/:eventId', {
templateUrl : 'pages/event_detail.html',
controller : 'eventPageCtrl'
});
My factory:
myApp.factory('eventRepo', ['$http', function($http) {
var urlBase = 'php/api.php';
var eventRepo = {};
eventRepo.getEvent = function (id) {
return $http.get(urlBase + '?eventID=' + id);
};
return eventRepo;
}]);
My Controller:
myApp.controller('eventPageCtrl', ['$scope', '$routeParams', 'eventRepo',
function ($scope, $routeParams, eventRepo) {
$scope.getEvent = function (id) {
eventRepo.getEvent($routeParams.eventId)
.success(function (data) {
$scope.eventsDetail = data;
})
.error(function (error) {
$scope.status = 'Error retrieving event! ' + error.message;
});
};
}]);
When handling $http.get() inside the controller and not with the factory it works fine so I think I am not passing my $routeParams correctly? Perhaps this line is causing the issue eventRepo.getEvent($routeParams.eventId)?
This works currently, but trying to use $http.get() outside the controller:
myApp.controller('eventPageCtrl', function($scope, $http, $routeParams) {
$http.get("php/api.php?eventID="+$routeParams.eventId).success(function(data){
$scope.eventsDetail = data;
});
});
how about using resolve in your routeProver and returning the eventId and then injecting it in the controller .. example :
$routeProvider:
.when('/event/:eventId', {
templateUrl : 'pages/event_detail.html',
controller : 'eventPageCtrl',
resolve : {
eventId: function($route, $location) {
var eventId = $route.current.params.eventId;
return eventId;
});
Controller:
myApp.controller('eventPageCtrl', ['$scope', 'eventId', 'eventRepo',
function ($scope, eventId, eventRepo) { //add it as a dependency
$scope.eventId = eventId; //you can check this to see if its being assigned
$scope.getEvent = function (eventId) { //Edit: eventId added here
eventRepo.getEvent(eventId) //Edit: eventId passed
.success(function (data) {
$scope.eventsDetail = data;
})
.error(function (error) {
$scope.status = 'Error retrieving event! ' + error.message;
});
};
}]);
Lets say i list all users in a list, when i click a user i want to route to a new view and get the data for the selected person.
What is the preferred way? Should i move the data i already got when i listed the users or should i create a new server call?
My first thought is to pass the data, but the problem with this is that the data the gets lost if the user refreshes the page.
What is the best practice to solve this?
Small example:
(function() {
var app = angular.module('app');
var controllerId = 'app.controllers.views.userList';
app.controller(controllerId, [
'$scope', 'UserService',function ($scope, userService) {
var vm = this;
vm.users = [];
userService.getAllUsers().success(function (data) {
vm.users= data.users;
});
var gotoUser = function(user) {
// Pass the user to UserDetail view.
}
}
]);
})();
<div data-ng-repeat="user in vm.users" ng-click="vm.gotoUser(user)">
<span>{{customer.firstname}} {{customer.lastname}}</span>
</div>
i now list the user details in UserDetail view, this view is now vulnerable against a browser refresh.
Typically most people just create a new server call, but I'll assume you're worried about performance. In this case you could create a service that provides the data and caches it in local storage.
On controller load, the controller can fetch the data from the service given the route params and then load the content. This will achieve both the effect of working on page refresh, and not needing an extra network request
Here's a simple example from one of my apps, error handling left out for simplicity, so use with caution
angular.
module('alienstreamApp')
.service('api', ['$http', '$q','$window', function($http, $q, $window) {
//meta data request functions
this.trending = function() {
}
this.request = function(url,params) {
var differed = $q.defer();
var storage = $window.localStorage;
var value = JSON.parse(storage.getItem(url+params))
if(value) {
differed.resolve(value);
} else {
$http.get("http://api.alienstream.com/"+url+"/?"+params)
.success(function(result){
differed.resolve(result);
storage.setItem(url+params,JSON.stringify(result))
})
}
return differed.promise;
}
}]);
I would say that you should start off simple and do a new server call when you hit the new route. My experience is that this simplifies development and you can put your effort on optimizing performance (or user experience...) where you will need it the most.
Something like this:
angular.module('app', ['ngRoute', 'ngResource'])
.factory('Users', function ($resource) {
return $resource('/api/Users/:userid', { userid: '#id' }, {
query: { method: 'GET', params: { userid: '' }, isArray: true }
});
});
.controller("UsersController",
['$scope', 'Users',
function ($scope, Users) {
$scope.loading = true;
$scope.users = Users.query(function () {
$scope.loading = false;
});
}]);
.controller("UserController",
['$scope', '$routeParams', 'Users',
function ($scope, $routeParams, Users) {
$scope.loading = true;
$scope.user = Users.get({ userid: $routeParams.userid }, function () {
$scope.loading = false;
});
$scope.submit = function () {
$scope.user.$update(function () {
alert("Saved ok!");
});
}
}]);
.config(
['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/users', {
templateUrl: '/users.html',
controller: 'UsersController'
})
.when('/users/:userid', {
templateUrl: '/user.html',
controller: 'UserController'
})
.otherwise({ redirectTo: '/users' });
}
]
);