I have a scenario where when click on searched item i need to load different state
Here is my html code
<span class="item-title" ng-click="fnViewProfile()">
<span> {{item.name}} </span>
</span>
below is my controller code:
$scope.fnViewProfile = function() {
$state.go("ViewProfile", {
"id": 10215
});
}
In my app.js i have a resolve function where i did ajax call to get the data before the html gets loaded
.state('ViewProfile', {
parent: 'home',
url: '/ViewProfile:id',
templateUrl: 'views/ViewProfileView.html',
controller: 'ProfileViewCtrl',
resolve: {
profile: function(DashboardService, $stateParams, $scope) {
debugger;
var userId = $stateParams.id;
var userData;
DashboardService.fnGetUser(userId).then(function(oResponse) {
userData = oResponse.data;
})
return userData;
}
}
In controller of ViewProfile state i am passing the profile service
angular.module('talentGraphApp')
.controller('ProfileViewCtrl', function($scope, $state, DashboardService, profile) {
debugger;
$scope.profile = profile;
console.log(profile);
});
But i am unable to get profile in the console.
I dont understand where i am going wrong
Any help would be appreciated.
In the resolve you return userData before it has been assigned inside the promise callback, so it is undefined when you pass it to controller
Return the promise instead
Change to
resolve: {
profile: function(DashboardService, $stateParams) {
var userId = $stateParams.id;
return DashboardService.fnGetUser(userId).then(function(oResponse) {
// return as resolved value
return oResponse.data;
});
}
}
Also there is no $scope in the routing config
Related
so I'm trying to pass my mongodb data from state to state using ui-router but having trouble making the links and controller as i'm making an app where users have a profile and are able to click on other people profile to see them. I'm able to get the entire list of users profiles but when click, it doesn't get the data so the user profile is blank.
app.js
angular.module('MyApp', ['ui.router']).config(function($stateProvider, $urlRouterProvider, $authProvider) {
/**
* App routes
*/
$stateProvider
.state('home', {
url: '/',
controller: 'HomeCtrl',
templateUrl: 'partials/home.html'
})
.state('about', {
url: '/about',
templateUrl: 'partials/about.html'
})
.state('match', {
url: '/match',
controller: 'matchCtrl',
templateUrl: 'partials/match.html'
})
.state('match.list', {
url: '/list',
controller: 'matchCtrl',
templateUrl: 'partials/match.list.html'
})
//this part is where I need help on most with the controller as it is not working
.state('match.profile', {
url: '/:displayName',
templateUrl: 'partials/match.profile.html',
controller: function($scope, $stateParams) {
$scope.user = $scope.getUserProfile[$stateParams.displayName];
}
});
$urlRouterProvider.otherwise('/');
account.js
angular.module('MyApp').factory('Account',function($http,$stateParams) {
return {
getProfile: function() {
return $http.get('/api/me/:id');
},
getAllProfile: function() {
return $http.get('/api/me');
},
getUserProfile: function() {
return $http.get('/api/me' + $stateParams.displayName);
},
updateProfile: function(profileData) {
return $http.put('/api/me/:id', profileData);
}
};
});
this part works where the mongodb data shows up on the list of users
match.list.html
<div ng-repeat="user in user">
<div class="col-xs-12 col-sm-6 col-md-6">
<div class="well well-sm">
<div class="row">
<h1>{{user.displayName}}</h1>
<h1>{{user.age}} </h1>
<a ng-href="#/match/{{user.displayName}}">
See {{user.displayName}}!
</a>
</div>
</div>
</div>
</div>
the profile part doesn't work as clicking on the a ng-href only lead to a blank profile without data.
match.profile.html
<h1>{{user.displayName}}</h1>
<h1>{{user.age}}</h1>
etc...
how would I go about fixing this so when i click on a user profile using ng-href on the list part. It go to the user profile with the data? Are there any examples that I find similar to this problem with ui-router?
edit
does it have something to do with my controller?
match.js
angular.module('MyApp')
.controller('matchCtrl', function($scope, toastr, Account) {
// set up the filter
$scope.sortUser = 'displayName';
$scope.sortReverse = false;
$scope.searchUser = '';
// get all of the users
$scope.getAllProfile = function () {
Account.getAllProfile()
.then(function (response) {
$scope.user = response.data;
})
.catch(function (response) {
toastr.error(response.data.message, response.status);
});
};
$scope.getUserProfile = function () {
Account.getUserProfile()
.then(function(response) {
$scope.user = response.data;
})
.catch(function (response) {
toastr.error(response.data.message, response.status);
});
};
// get the users
$scope.getAllProfile();
$scope.getUserProfile();
});
the rest api i'm using on node
app.get('/api/me/', function(req, res) {
User.find(function(err, user) {
res.send(user);
});
});
app.get('/api/me/:id', ensureAuthenticated, function(req, res) {
User.findById(req.user, function(err, user) {
res.send(user);
});
});
app.put('/api/me/:id', ensureAuthenticated, function(req, res) {
User.findById(req.user, function(err, user) {
if (!user) {
return res.status(400).send({ message: 'User not found' });
}
user.picture = req.body.picture || user.picture;
user.displayName = req.body.displayName || user.displayName;
user.email = req.body.email || user.email;
user.save(function(err) {
res.status(200).end();
});
});
});
Your match.profile controller is never resolving the promise that's returned from the API by getUserProfile, which is why the UI is blank.
First off, the controller needs the Account service injected into it, as others have noted. The getUserProfile method needs to be called correctly (use () instead of []).
controller: function($scope, $stateParams, Account) {
$scope.user = Account.getUserProfile($stateParams.displayName);
}
I'm also not sure that defining your Account factory to rely on $stateParams is going to work properly, since a factory is a singleton and $stateParams may not update properly as you change states; you'd have to check your Network tab in developer tools to ensure the API endpoint is being built correctly (or just log $stateParams inside the getUserProfile method). I think the better option though would be to take in the url variable as an argument. You're trying to pass it in anyway, but the method isn't expecting any arguments.
getUserProfile: function(displayName) {
return $http.get('/api/me' + displayName);
}
So finally, your controller should look like this
controller: function($scope, $stateParams, Account) {
Account.getUserProfile($stateParams.displayName)
.then(function (profile) {
$scope.user = profile;
});
}
A few other tips with UI-Router
With UI-Router, you should be concerned primarily with states of the application, not URLs. The correct way to transition between states in UI-Router then is to use ui-sref instead of ng-href. Note that ui-sref takes a state name, not a url, so instead of <a ng-href="#/match/{{user.displayName}}">, it'd be better to do <a ui-sref='match.profile({displayName: user.displayName})'> (note how you can still pass in your displayName variable to the $stateParams as an argument.
Your match.profile state is a perfect use case for a resolve function. Resolve functions allow you to load data before the state loads. This ensures that your data is always available to your state before the UI ever renders.
.state('match.profile', {
url: '/:displayName',
templateUrl: 'partials/match.profile.html',
resolve: {
profile: function ($stateParams, Account) {
return Account.getUserProfile($stateParams.displayName)
.then(function (profile) {
return profile;
});
}
},
controller: function($scope, profile) {
$scope.user = profile;
}
});
Notice how you can name the resolve function to be whatever you want, in this case profile. You can inject this directly into your controller and know for certain that your data will already be available to the UI as soon as the controller loads. No loading data, no resolving promises. This is much closer to the proper separation of concerns for a controller in the MVC architecture of Angular where a controller should not be concerned with loading its own data.
You aren't calling method correctly getUserProfile, It not available there in $scope, you have to call it from Account service. Method call happens by parenthesis () not like []. Next thing is, you can get data from getUserProfile method by putting .then function over it.
Code
.state('match.profile', {
url: '/:displayName',
templateUrl: 'partials/match.profile.html',
controller: function($scope, $stateParams, Account) {
Account.getUserProfile($stateParams.displayName)
.then(function(res){
var data = res.data;
$scope.user = data;
}, function(error){
console.log(error);
});
}
});
getUserProfile is a method in Account service. You have used
$scope.getUserProfile[$stateParams.displayName]
Change it to
Account.getUserProfile($stateParams.displayName);
It looks something like this
.state('match.profile', {
url: '/:displayName',
templateUrl: 'partials/match.profile.html',
controller: function($scope, $stateParams, Account) {
$scope.user = Account.getUserProfile[$stateParams.displayName];
}
});
and you have missed a slash in getUserProfile function:
getUserProfile: function() {
return $http.get('/api/me' + $stateParams.displayName);
},
which should be
getUserProfile: function(){
return $http.get('/api/me/' + $stateParams.displayName).then(function(res){
return res.data;
});
}
I'm using bellow code to reload the route but it does reload only the view. i have a resolve object defined in my routes definitions file. How could i reload the route with resolve ?
$rootScope.changeRoute = function(){
var currentPageTemplate = $route.current.templateUrl;
$templateCache.remove(currentPageTemplate);
$route.reload();
};
route.js
.when('/invoice/', {
title: 'invoice',
templateUrl: 'views/invoice/index.html',
controller: 'invoiceController',
resolve: {
getInvoices: function (getInvoicesService, getDashbordService, dataService, $q, $rootScope) {
var invoices = getInvoicesService;
var userDefaults = getDashbordService || dataService.get();
return $q.all([invoices, userDefaults]).then(function(results){
userDefaults.then(function(data){
$rootScope.menus = data.user_menu;
});
return {
'data' : results[0],
'defaults' : results[1]
};
})
}
}
})
I'm trying to retrieve data from Angularfire using a service, and then setting the returned value to my scope in my controller.
When I run the code below, I get undefined back for scope.sessions.
SERVICE:
app.factory('sessions', function(){
var refToSessions = new Firebase('myFireBaseURL');
var allSessions = [];
return {
getSessions: function () {
refToSessions.on("value", function (sessions) {
allSessions.push(sessions.val());
return allSessions;
});
}
};
});
CONTROLLER:
app.controller('SessionsCtrl', ['$scope', '$state', 'Auth', 'sessions', function($scope, $state, Auth, sessions){
$scope.sessions = sessions.getSessions();
$scope.submitSession = function() {
console.log($scope.sessions);
}
});
You're trying to return asynchronous data.
You are logging allSessions to the console before the data has downloaded from Firebase.
Use $firebaseArray from AngularFire.
app.constant('FirebaseUrl', '<my-firebase-url>');
app.service('rootRef', ['FirebaseUrl', Firebase);
app.factory('Sessions', function(rootRef, $firebaseArray){
var refToSessions = ref.child('sessions');
return $firebaseArray('sessions');
}
Then injection Sessions into your controller:
app.controller('SessionsCtrl', function($scope, $state, Auth, Sessions){
$scope.sessions = Sessions; // starts downloading the data
console.log($scope.sessions); // still empty
$scope.submitSession = function() {
// likely by the time you click here it will be downloaded
console.log($scope.sessions);
$scope.sessions.$add({ title: 'new session' });
};
});
The data starts downloading once it's injected into your controller. When it's downloaded, $firebaseArray knows to trigger $digest, so it appears on the page.
Since you're using ui-router, you can use resolve to make sure the data exists before injecting it into your controller:
app.config(function ($stateProvider) {
$stateProvider
.state("session", {
controller: "SessionsCtrl",
templateUrl: "views/sessions.html",
resolve: {
sessions: function(Sessions) {
// return a promise that will fulfill the data
return Sessions.$loaded();
}
}
})
});
Now you would change your controller code to this:
app.controller('SessionsCtrl', function($scope, $state, Auth, sessions){
$scope.sessions = sessions; // data is available since injected by router
console.log($scope.sessions); // logs the appropriate data
$scope.submitSession = function() {
$scope.sessions.$add({ title: 'new session' });
};
});
I am having some trouble getting to the controller for my state param. I am using the correct state to link to the next view.
<td><a ui-sref="orders({customerId: cust.id})">View Orders</a></td>
In my config file I am referencing the state that name and the route params. I commented out the resolve object for now. My goal is to get into the controller then pass the correct data. Notice that I am using controllerAs
My initial thought was ({customerId: ctrl.cust.id }) However that did not change the url route.
The url is changing to match the url name but is not connecting to the controller and is not giving me the view.
(function() {
'use strict';
angular
.module('app.orders')
.config(config);
function config($stateProvider) {
$stateProvider
.state('orders',{
// params: {customerid: null},
url:'/customers:customerId',
templateUrl: './components/orders/orders.html',
controller: 'OrdersController',
controllerAs: 'ctrl',
resolve: {
customerFactory: 'customerFactory',
customerInfo: function( customerFactory, $stateParams) {
return customerFactory.getCustomers($stateParams.id);
}
}
************** my main problem is the resolve. This is blocking me from getting into the next controller. *****************
resolve: {
customerId:[ '$stateParams','customerFactory', function( $stateParams, customerFactory) {
return customerFactory.getCustomers($stateParams.id);
}]
}
})
};
})();
For now my controller is very small. I just want to connect to it. I have checked my networks tab and see GET for the files.
(function() {
// 'use strict';
angular
.module('app.orders')
.controller('OrdersController', OrdersController);
function OrdersController($stateParams) {
console.log('in');
var vm = this;
vm.title = "Customer Orders";
vm.customer = null;
}
}());
I have referenced my module in the main javascript file.
(function () {
'use strict';
angular.module('app', ['app.services',
'app.customers',
'app.orders','ui.router']);
})();
When I comment out the resolve I am able to access the controller. So I know the problem is in the resolve. Here is my service. I am making a request to a Json file with $http request and using .then
Updates Here is my refactored service call I am getting back the correct customer in the console each time.
(function() {
angular
.module('app.services',[])
.constant('_', window._)
.factory('customersFactory', customersFactory);
function customersFactory($http, $log) {
return {
getCustomers: getCustomers,
getCustomer: getCustomer
};
function getCustomers(){
return $http.get('./Services/customers.json',{catch: true})
.then(getCustomerListComplete)
.catch(getCustomerListFailed);
function getCustomerListComplete(response) {
console.log('response.data',response.data);
return response.data;
}
function getCustomerListFailed(error) {
console.log('error', error);
}
}
function getCustomer(id) {
var url = './Services/customers.json';
return $http.get(url, {
catch: true
})
.then(function(response) {
console.log('promise id',id);
var data = response.data;
for(var i =0, len=data.length;i<len;i++) {
console.log('data[i].id',data[i].id);
if(data[i].id === parseInt(id)) {
console.log('data[i]', data[i]);
return data[i];
}
}
})
}
}
}());
There is a working example with your code
It is very hard to guess what is wrong. Based on suggestion I gave you here Have a expression error in ui-sref ... your code seems to be completely valid.
I placed your stuff into this app.orders.js file (the ONLY change is templateUrl path, just for plunker purposes):
angular
.module('app.orders', ['ui.router'])
'use strict';
angular
.module('app.orders')
.config(['$stateProvider', config]);
//config.$inject = ['$stateProvider'];
function config($stateProvider) {
$stateProvider
.state('orders',{
// params: {customerid: null},
url:'/customers:customerId',
//templateUrl: './components/orders/orders.html',
templateUrl: 'components/orders/orders.html',
controller: 'OrdersController',
controllerAs: 'ctrl'
// resolve: {
// customerId:[ '$stateParams','customerFactory', function( $stateParams, customerFactory) {
// return customerFactory.getCustomers($stateParams.id);
// }]
// }
})
};
// 'use strict';
angular
.module('app.orders')
.controller('OrdersController', OrdersController);
OrdersController.$inject = ['$stateParams'];
function OrdersController($stateParams) {
console.log('in');
var vm = this;
vm.title = "Customer Orders " + $stateParams.customerId;
vm.customer = null;
}
And this is the working template components/orders/orders.html:
<div >
<h3>current state name: <var>{{$state.current.name}}</var></h3>
<h5>title</h5>
<pre>{{ctrl.title}}</pre>
...
When I call it like this:
<li ng-repeat="cust in [{id:1}, {id:2}]"
><a ui-sref="orders({customerId: cust.id})">View Orders - cust ID == {{cust.id}}</a>
</li>
Check it in action here
So, whil my previous answer was about make the state working without resolve, now we will observe few adjustments (and one fix) to make even resolve working.
There is a working plunker, extending the previous one.
FIX
The only fix, the most important change come from this definition:
angular
.module('app.services',[])
.factory('customersFactory', customersFactory);
see the plural in the factory name, the 'customersFactory'. While here:
...my main problem is the resolve. This is blocking me from getting into the next controller....
resolve: {
customerId:[ '$stateParams','customerFactory', function( $stateParams, customerFactory) {
return customerFactory.getCustomers($stateParams.id);
}]
}
we ask for 'customerFactory' (singular, no s in the middle)
Few improvements:
So, this would be our adjusted state def:
$stateProvider
.state('orders',{
// INTEGER is here used to later easily use LO_DASH
url:'/customers{customerId:int}', // int is the type
templateUrl: './components/orders/orders.html',
controller: 'OrdersController',
controllerAs: 'ctrl',
resolve: {
// wrong name with 's'
//customerId:[ '$stateParams','customerFactory',
// we use customer, because we also changed the factory
// implementation - to return customer related to
// $statePrams.customerId
customer:[ '$stateParams','customersFactory',
function( $stateParams, customersFactory) {
return customersFactory
//.getCustomers($stateParams.id)
.getCustomer($stateParams.customerId)
;
}]
}
})
Now, this is our adjusted factory, and its new method getCustomer
angular
.module('app.services', [])
.factory('customersFactory', customersFactory);
customersFactory.$inject = ['$http', '$log', '$q', '$stateParams'];
function customersFactory($http, $log, $q, $stateParams) {
return {
getCustomers: getCustomers,
getCustomer: getCustomer
};
function getCustomers() {
// see plunker for this, or above in question
}
// new function
function getCustomer(id) {
var url = "customer.data.json";
return $http
.get(url, {
catch: true
})
.then(function(response){
var data = response.data;
var customer = _.find(data, {"id" : id});
return customer;
})
;
}
}
this is our data.json:
[
{
"id" : 1, "name": "Abc", "Code" : "N1"
},
{
"id" : 2, "name": "Def", "Code" : "N22"
},
{
"id" : 3, "name": "Yyz", "Code" : "N333"
}
]
And here we have controller:
OrdersController.$inject = ['$stateParams', 'customer'];
function OrdersController($stateParams, customer) {
console.log('in');
var vm = this;
vm.title = "Customer Orders " + $stateParams.customerId;
vm.customer = customer;
}
a view to show customer
<h3>customer</h3>
<pre>{{ctrl.customer | json}}</pre>
Check it here in action
I have tried everything to get ui-router's resolve to pass it's value to the given controller–AppCtrl. I am using dependency injection with $inject, and that seems to cause the issues. What am I missing?
Routing
$stateProvider.state('app.index', {
url: '/me',
templateUrl: '/includes/app/me.jade',
controller: 'AppCtrl',
controllerAs: 'vm',
resolve: {
auser: ['User', function(User) {
return User.getUser().then(function(user) {
return user;
});
}],
}
});
Controller
appControllers.controller('AppCtrl', AppCtrl);
AppCtrl.$inject = ['$scope', '$rootScope'];
function AppCtrl($scope, $rootScope, auser) {
var vm = this;
console.log(auser); // undefined
...
}
Edit
Here's a plunk http://plnkr.co/edit/PoCiEnh64hR4XM24aH33?p=preview
When you use route resolve argument as dependency injection in the controller bound to the route, you cannot use that controller with ng-controller directive because the service provider with the name aname does not exist. It is a dynamic dependency that is injected by the router when it instantiates the controller to be bound in its respective partial view.
Also remember to return $timeout in your example, because it returns a promise otherwise your argument will get resolved with no value, same is the case if you are using $http or another service that returns a promise.
i.e
resolve: {
auser: ['$timeout', function($timeout) {
return $timeout(function() {
return {name:'me'}
}, 1000);
}],
In the controller inject the resolve dependency.
appControllers.controller('AppCtrl', AppCtrl);
AppCtrl.$inject = ['$scope', '$rootScope','auser']; //Inject auser here
function AppCtrl($scope, $rootScope, auser) {
var vm = this;
vm.user = auser;
}
in the view instead of ng-controller, use ui-view directive:
<div ui-view></div>
Demo
Here is how I work with resolve. It should receive promise. So I create service accordingly.
app.factory('User', function($http){
var user = {};
return {
resolve: function() {
return $http.get('api/user/1').success(function(data){
user = data;
});
},
get: function() {
return user;
}
}
});
This is main idea. You can also do something like this with $q
app.factory('User', function($q, $http){
var user = {};
var defer = $q.defer();
$http.get('api/user/1').success(function(data){
user = data;
defer.resolve();
}).error(function(){
defer.reject();
});
return {
resolve: function() {
return defer.promise;
},
get: function() {
return user;
}
}
});
These are almost identical in action. The difference is that in first case, service will start fetching date when you call resolve() method of service and in second example it will start fetch when factory object is created.
Now in your state.
$stateProvider.state('app.index', {
url: '/me',
templateUrl: '/includes/app/me.jade',
controller: function ($scope, $rootScope, User) {
$scope.user = User.get();
console.log($scope.user);
},
controllerAs: 'vm',
resolve: {
auser: function(User) {
return User.resolve()
}
}
});