I have seperated out my controllers & services into modules, and on a form submit, I am trying to get data from a service and then set it in the scope, but I'm having trouble getting the view to update with the new data. I have gone through other questions with similar problems but I can't seem to get it working. The view updates fine from the Search.get() function, but not from the $scope.submitFilterForm function.
I'm trying to update like this:
$scope.searchDetails = results;
This is my code at the moment:
SearchCtrl.js
angular.module('SearchCtrl', []).controller('SearchController', function($scope, $http, Page, Search) {
$scope.pageClass = 'page-search';
$scope.showContent = false;
$scope.searchDetails = [];
Search.get().success(function(searchResults) {
$scope.searchDetails = searchResults;
Page.setTitle('Search');
$scope.showContent = true;
});
$scope.submitFilterForm = function(isValid) {
if (isValid) {
Search.filterByPostcode($scope.postcode, $scope.searchradius).success(function(results) {
console.log('results', results);
$scope.searchDetails = results;
});
}
}
});
SearchService.js
angular.module('SearchService', []).factory('Search', ['$http', function($http) {
return {
get: function() {
return $http.get('/api/places');
},
filterByPostcode: function(postcode, searchradius) {
return $http.get('/api/filter-by-postcode/'+postcode+'/'+searchradius);
}
}
}]);
search.html
<div class="places-list" ng-show="showContent">
<div class="places-list-item" ng-repeat="place in searchDetails">
{{place.place_name}}
</div>
</div>
I also have the template above set up in appRoutes.js like so:
$routeProvider
// home page
.when('/search', {
title: 'Search',
templateUrl: '/partials/search.html',
controller: 'SearchController'
})
$locationProvider.html5Mode(true);
Thanks for any help.
Related
I'm trying to clean up my code such that it corresponds to John Papa's Angular style guide but in the process broke something and my controller no longer appears in ng-inspector. If I can get vm.message to display I can figure out the rest (though any general feedback is appreciated :))
(function () {
"use strict";
angular
.module('roomchoice.manager-dashboard.alerts', [
'ui.router'
])
.config(function config($stateProvider) {
$stateProvider.state('manager.alerts', {
url: '/alerts',
views: {
"main": {
controller: 'AlertsController',
templateUrl: 'manager-dashboard/alerts/alerts.tpl.html'
}
},
data: {
pageTitle: 'Alerts'
}
});
})
.controller('AlertsController', AlertsController);
function AlertsController($scope, Restangular) {
var vm = this;
vm.message = "Hello";
vm.settlements = [];
vm.undepositedPayments = [];
vm.unapprovedFunnels = [];
vm.getSettlements = getSettlements;
vm.getUndepositedPayments = get_UndepositedPayments;
vm.getUnapprovedFunnels = get_unapprovedFunnels;
function getSettlements() {
Restangular.all('alerts/get_settlements').getList().then(function(settlements){
vm.settlements = settlements;
return vm.settlements;
});
}//End of getSettlements
function getUndepositedPayments() {
Restangular.all('alerts/get_undepositedpayments').getList().then(function(undepositedpayments){
vm.undepositedPayments = undepositedpayments;
return vm.undepositedPayments;
});
}//End of getUndepositedPayments
function getUnapprovedFunnels() {
Restangular.all('alerts/get_unapprovedfunnels').getList().then(function(unapprovedfunnels){
vm.unapprovedFunnels = unapprovedfunnels;
return vm.unapprovedFunnels;
});
}//End of getUnapprovedFunnels
}//End of Controller
})();//End of Module
<div id="main" ng-controller="AlertsController as alerts">
<div>
<h1>Alerts (Under Construction) </h1>
<h2>{{alerts.message}}</h2>
</div>
</div>
You are trying to instantiate your controller more than once in your code, and this won't work the way you expect.
You should not use ng-controller in templates that are part of a state. The controller is defined by the state provider, and does not be instantiated inside the template.
Remove ng-controller from your template, and add controllerAs to your state:
$stateProvider.state('manager.alerts', {
url: '/alerts',
views: {
"main": {
controller: 'AlertsController',
controllerAs: 'alerts',
templateUrl: 'manager-dashboard/alerts/alerts.tpl.html'
}
},
data: {
pageTitle: 'Alerts'
}
});
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
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()
}
}
});
I have an interesting issue with the $http.delete() and $window.location.reload() functions. Basically I am calling a delete method that in turn calls the $http.delete() call which - using a REST API interface - removes data from a database (mongoDB). For reasons unknown this is what's happening:
delete is successful in the database
this is verified as the data is no longer in the database
also monitored using Chrome DevTools it shows status: 200
$window.location.reload() gets called and nothing happens
Chrome DevTools shows a GET call to the root of the domain but the status is 'pending'
The page does not time out, it basically keeps on loading and loading and loading. Once I hit refresh / CTRL-F5 all goes back to normal and I can see that my item has been deleted.
Some excerpts from my code:
app.js
angular.module('contacts', ['ngRoute', 'contacts.factory', 'contacts.filters', 'ui.bootstrap']).
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: '/p/list',
controller: ListCtrl,
}).
otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
}]);
controllers.js
function ListCtrl($scope, $modal, contactFactory) {
//code removed
$scope.delete = function(c) {
var id = c._id;
var modalInstance = $modal.open({
templateUrl: 'deleteContactModal',
controller: deleteContactModalCtrl,
resolve: {
contact: function() {
return contactFactory.getContact(id);
}
}
});
}
}
var deleteContactModalCtrl = function($scope, $route, $modalInstance, $window, contact, contactFactory) {
$scope.name = contact.data.contact.name;
$scope.deleteContact = function() {
contactFactory.deleteContact(contact.data.contact._id).success(function() {
//have also tried: $window.location.reload(true);
//as well as window.location.href('/') - didn't work either
$modalInstance.close($window.location.reload());
});
};
}
factory.js
angular.module("contacts.factory", []).
factory('contactFactory', function($http){
return {
//code removed
deleteContact: function(id) {
return $http.delete('/api/contact/' + id);
}
}
});
backend - app.js
//usual express setup
app.delete('/api/contact/:id', api.delete); //delete contact
backend - api.js
//code removed
exports.delete = function (req, res) {
var id = req.params.id;
if (id) {
ContactModel.findById(id, function (err, contact) {
contact.remove(function (err) {
if (!err) {
res.json(true);
} else {
res.json(false)
console.log(err);
}
});
});
}
};
At the moment I'm not even sure if this issue is related to the frontend or to the backend. As mentioned before - the backend portion is working fine - the data is deleted from the DB.
Okay the solution seems to be the following:
$scope.deleteContact = function() {
contactFactory.deleteContact(contact.data.contact._id).success(function() {
$modalInstance.close();
contactFactory.getContacts().success(function(contacts) {
return $scope.contacts = contacts;
});
$window.location.reload();
});
};
I need to get the getContacts() method from my factory again during the delete method.