I'm new to AngularJS and I'm trying to modify the example at http://www.codeproject.com/Articles/576246/A-Shopping-Cart-Application-Built-with-AngularJS
I've implemented a new payment service in the example to call a Json method on my server which works fine, but when I call the method clearItems(); to remove the items from the cart on success it removes the items as expected in the background (as I can see from refreshing the page and the cart it empty) - my problem is that it does not clear the cart in the UI (without refreshing)
If I call this.clearItems(); after the Json call it clears the cart items in the UI as expected, but this is not what I requiquire as I only want to clear the items after success.
Can anyone suggest how I can get this to work?
My Json call is listed below
var me = this;
//this.clearCart = clearCart == null || clearCart;
$.ajax({
cache: false,
type: "POST",
url: '/pos/JsonCashPayment',
contentType: 'application/json',
dataType: "json",
data: JSON.stringify(this.items),
success: function (data) {
me.clearItems();
alert('success function called');
// Do something with the returned data
// This can be any data
}
});
//this.clearItems();
Thanks
Mark
EDIT - Problems running $http
Further to the advice form marck I understand that to do this I need to use $http instead of $json. The problem with doing this is that I need to do this in the shoppingCart.js class (as part of the payments section) which is attached to the controller via app.js (code below). When I try this though I get a JS error that $http doesn't exist.
Is there a way to use $http from the shoppingCart.js class?
app.js
var storeApp = angular.module('AngularStore', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/store', {
templateUrl: 'POS/store',
controller: storeController
}).
when('/products/:productSku', {
templateUrl: 'POS/product',
controller: storeController
}).
when('/cart', {
templateUrl: 'POS/shoppingCart',
controller: storeController
}).
otherwise({
redirectTo: '/store'
});
}]);
// create a data service that provides a store and a shopping cart that
// will be shared by all views (instead of creating fresh ones for each view).
storeApp.factory("DataService", function ($http) {
// create store
var myStore = new store($http);
// create shopping cart
var myCart = new shoppingCart("AngularStore");
controller.js
function storeController($scope, $http, $routeParams, DataService) {
// get store and cart from service
$scope.store = DataService.store;
$scope.cart = DataService.cart;
shoppingCart.js
function shoppingCart(cartName) {
this.cartName = cartName;
this.clearCart = false;
this.checkoutParameters = {};
this.items = [];
// load items from local storage when initializing
this.loadItems();
// save items to local storage when unloading
var self = this;
$(window).unload(function () {
if (self.clearCart) {
self.clearItems();
}
self.saveItems();
self.clearCart = false;
});
}
shoppingCart.prototype.checkoutCash = function (parms, clearCart, $scope, $http) {
// Need to be able to run $http here
$http({
url: '/pos/JsonCashPayment',
method: "POST",
data: this.items,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function (data, status, headers, config) {
}).error(function (data, status, headers, config) {
});
}
To make your approach work, you need to inject $http into your controller, then pass it further to the function you've defined in shoppingcart.js, like so in controller.js:
'use strict';
// the storeController contains two objects:
// - store: contains the product list
// - cart: the shopping cart object
function storeController($scope, $routeParams, DataService, $http) {
// get store and cart from service
$scope.store = DataService.store;
$scope.cart = DataService.cart;
// use routing to pick the selected product
if ($routeParams.productSku != null) {
$scope.product = $scope.store.getProduct($routeParams.productSku);
}
$scope.cart.checkoutCash('parms', 'clearCart', $scope, $http);
}
Obviously, the first two arguments I sent to checkoutCash are filler and need to be replaced with more appropriate values.
Related
In this project, I am executing a query on click list item of ionic. I am getting the data from php json_encode. The data is getting displayed in the networks tab under response. Also, I have added $scope.doctorList = {}; and after that wrote this line $scope.doctorList = response which comes from success function. The data is getting displayed in console.log($scope.doctorList) as well.
Now when I try to display this data in angular, it does not show anything.
I have included it in ng-repeat as : ng-repeat = "doctors in doctorList"
The syntax seems to be correct as the same thing is working for another controller but here, I can't retrieve the data. The page goes blank and there is no error in console / netowrks tab.
I am using one controller for two html files. Please help
Here is the routes file
angular.module('app.routes', []).config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) {
$stateProvider.state('homeselect', {
url: '/home-select',
templateUrl: 'templates/homeselect.html',
controller: 'homeCtrl'
});
$stateProvider.state('home', {
url: '/home',
templateUrl: 'templates/home.html',
controller: 'homeCtrl'
});
});
Here is the controller
angular.module('app.controllers', []).controller('homeCtrl', function ($scope, $http, $ionicSideMenuDelegate, myServices, $window) {
$ionicSideMenuDelegate.toggleLeft();
$scope.loadDoc = function (type) {
$http({
url: "http://localhost/drmedic/retrieve_details_type.php",
method: "POST",
data: {
data: type
}
}).success(function (response) {
$scope.doctorList = {};
$scope.doctorList = response;
$window.location.href = '#/home-select';
});
};
$http({method: 'GET', url: 'http://localhost/drmedic/retrieve_details.php'}).success(function (data) {
$scope.contents = {};
$scope.contents = data;
});
});
Here is the html file code for ng-repeat
<ion-list ng-repeat="doctors in doctorList">
<ion-item>
<center>
{{doctors.name}}<br>
{{doctors.fees}}
</center>
</ion-item>
</ion-list>
You can use service.Open a file called service.js.After that this inject to app.js. I revised the code as follows:
Service.js:
angular.module('app.services', [])
.factory("AppService", function ($http) {
var AppService= {};
AppService.GetDetails = function (data) {
return $http({
url: "http://localhost/drmedic/retrieve_details_type.php",
method: "POST",
data: {
data: data
}
});
return AppService;
}
controller.js:
.controller('homeCtrl',function($scope,$http,$ionicSideMenuDelegate,myServices,$window,AppService) {
$ionicSideMenuDelegate.toggleLeft();
$scope.loadDoc = function(type){
AppService.GetDetails({type:type}).success(function (response) {
$scope.doctorList = {};
$scope.doctorList = response;
$window.location.href = '#/home-select';
})
.error(function (err) {
console.log(err);
});
}
});
ng-repeat iterates through a collection of items, create its own scope and renders it on UI. When I say collections, it should be an array.
https://docs.angularjs.org/api/ng/directive/ngRepeat
Please check the data-type for doctorList, which I believe is an array.
$scope.doctorList = [];
I hope this should solve the problem, if not please share the code I'll take a deep look into it.
I have an html template that displays data from an Angular factory. The problem is that the factory's get method performs call to the backend only when the page is loaded for the first time. When repeatedly opening the page I only see results from the first call to the backend. It looks like the factory somehow caches the results.
With using Chrome debug tools I see that Angular makes GET requests with the same resource id on each page load.
This is the factory definition:
.factory('Company', ['$resource', '$routeParams', function ($resource, $routeParams) {
return $resource('/companies/getCompany/:companyId', {}, {
get: {
method: 'GET',
url: '/companies/getCompany/:companyId',
params: {
'companyId': $routeParams.companyId
}
},
save: {
method: 'POST',
url: '/companies/updateCompany'
},
insert: {
method: 'POST',
url: '/companies/createNewCompany'
}
});
}])
This is the controller code
.controller('MyController', ['$scope', '$location', 'Company',
function ($scope, $location, Company) {
Company.get(function (data) {
$scope.company = data;
});
}]);
I'm using ng-click to open the page
<tr ng-repeat="company in companies"
ng-click="redirectToCompanyForm(company.id)">
$scope.redirectToCompanyForm = function (companyId) {
$location.url('/updateCompany/' + companyId);
}
I set a breakpoint on the factory - app pauses only the first time when I access the page.
Why is my factory called only once and how can I solve this?
From the Angular docs:
Note: All services in Angular are singletons. That means that the injector uses each recipe at most once to create the object. The injector then caches the reference for all future needs.
So you are right, all services are only created once and then cached by Angular.
Edit: better answer for your situation below:
$resource caching
You can disable caching the resource by adding the options to the $resource call:
return $resource('/companies/getCompany/:companyId', {}, {
get: {
method: 'GET',
params: {
'companyId': $routeParams.companyId
},
cache: false // THIS LINE
}
}
Edit 2: according to the docs, the first parameter of $resource is not optional and must be an url.
Maybe you can use low level $http methods in your controller.
$resource is a fantastic utility but in your case you don't want persist the data.
Try the $http.get method..
Or try the query() method.
$scope.myData = DataFactory.query();
Finally fixed the issue. Factory use was incorrect. This is the factory module
.factory('Company', ['$resource', function ($resource) {
return $resource('/companies/getCompany/:id', null, {
save: {
method: 'POST',
url: '/companies/updateCompany'
},
insert: {
method: 'POST',
url: '/companies/createNewCompany'
}
});
}])
And this is how the factory should be called
Company.get({id: $routeParams.companyId}, function (data) {
$scope.company = data;
});
Now the correct data is shown everytime the page is loaded.
I'm using AngularJS 1.3 and UI-Router. I have an state in which i have a resolve and a templateProvider.
What i'm trying to accomplish is that the information retrieved from database in the resolve can be used by the templateProvider. Right now, I have to get the information twice, once from resolve and another from templateProvider, and that's annoying.
The code:
.state('articleurl', {
url: '/:articleUrl',
resolve: {
article: function ($http, $stateParams, $location) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
})
.then(function (article) {
return article;
}, function (error) {
$location.path('/404');
});
},
loggedin: checkLoggedin
},
templateProvider: ['$templateFactory', '$stateParams', '$http', function ($templateFactory, $stateParams, $http) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
}).then(function(article) {
if ( article.data.template )
return $templateFactory.fromUrl('articles/views/templates/' + article.data.template + '.html');
else
return $templateFactory.fromUrl('articles/views/templates/news.html');
});
}],
controller: 'ArticlesViewController'
})
As you can see, according to article's kind i load a different template in the templateProvider. Besides, i use the article's information in the controller which has been previously got in the state's resolve.
Is there any way to use in the templateProvider the information previously fetched in the resolve avoiding this way another call to database?
Right now, it is doing 2 calls to database per connection...
Thanks!
app.factory('article', function ($cacheFactory){
var articleCache = $cacheFactory('article');
return function (url) {
return articleCache.get(url) || articleCache.put(url, $http({
method: 'GET',
url: '/articles/' + url
})
);
};
});
Use it as article($stateParams.articleUrl).then(...) in both places, that will keep the things DRY. You may get better control over the cache (e.g. expiration) by replacing $cacheFactory with angular-cache.
$http own caching may be successfully used as well instead of explicit caching:
If there are multiple GET requests for the same URL that should be
cached using the same cache, but the cache is not populated yet, only
one request to the server will be made and the remaining requests will
be fulfilled using the response from the first request.
I think you can inject directly the resolved variables, so you could inject article in templateProvider:
.state('articleurl', {
url: '/:articleUrl',
resolve: {
article: function ($http, $stateParams, $location) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
})
.then(function (article) {
return article;
}, function (error) {
$location.path('/404');
});
},
loggedin: checkLoggedin
},
templateProvider: ['$templateFactory', '$stateParams', '$http', 'article', function ($templateFactory, $stateParams, $http, article) {
// Now here you can use article without the need to re-call it
}],
controller: 'ArticlesViewController'
})
I am trying to use httml get to get the json
I have something like
.controller('testController', function($scope, $state, $http) {
$http({
url: "/json/test.json",
method: "GET",
}).success(function(data) {
$scope.testData = data;
console.log(data)
});
//outside of the $http scope but under the same controller, I have
//It shows undefined.
console.log($scope.testData)
})
I know we have to keep data our of the login codes so I don't want to modify the data inside the $http. Is there anyway to accomplish this? Thanks!
First, don't put this in a controller. Move it to a Service. That's what Services are designed for.
Second, the $http service is asynchronous. Your result only exists in the success callback, and your console.log will be called before the http request is finished. You need to assign it to a scope variable to use it outside of the callback, and expect it to be empty until the call back is complete.
http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app provides an example of an AngularJS authentication service, if that's what you're doing.
To go with your example:
.controller('testController', function($scope, $state, $http) {
$scope.testData = {};
$http({
url: "/json/test.json",
method: "GET",
}).success(function(data) {
$scope.testData = data;
$scope.logData();
});
$scope.logData = function() {
console.log($scope.testData);
}
});
This plnkr code is created in order to give your answer, yes you can have the $scope.testData outside the http request.
Have a look!!
var app = angular.module('plunker', []);
app.controller('testController', function($scope, $http) {
$http({
url: "test.json",
method: "GET",
}).success(function(data) {
$scope.testData = data;
alert(data);
callA();
});
function callA(){
alert($scope.testData);
}
});
I want to call a particular function: GetSession() at the beginning of my application load. This function makes a $http call and get a session token: GlobalSessionToken from the server. This session token is then used in other controllers logic and fetch data from the server. I have call this GetSession()in main controller: MasterController in $routeChangeStart event but as its an asynchronous call, my code moves ahead to CustomerController before the $http response.
Here is my code:
var GlobalSessionToken = ''; //will get from server later
//Define an angular module for our app
var myApp = angular.module('myApp', ['ngRoute']);
//Define Routing for app
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/customer', {
templateUrl: 'partials/customer.html',
controller: 'CustomerController',
resolve: {
loadData: function($q){
return LoadData2($q,'home');
}
}
}).
otherwise({
redirectTo: '/home'
});
}]);
//controllers start here and are defined in their each JS file
var controllers = {};
//only master controller is defined in app.js, rest are in separate js files
controllers.MasterController = function($rootScope, $http){
$rootScope.$on('$routeChangeStart', function(){
if(GlobalSessionToken == ''){
GetSession();
}
console.log('START');
$rootScope.loadingView = true;
});
$rootScope.$on('$routeChangeError', function(){
console.log('ERROR');
$rootScope.loadingView = false;
});
};
controllers.CustomerController = function ($scope) {
if(GlobalSessionToken != ''){
//do something
}
}
//adding the controllers to myApp angularjs app
myApp.controller(controllers);
//controllers end here
function GetSession(){
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
}).error(function (data, status, headers, config) {
console.log(data);
});
}
And my HTML has following sections:
<body ng-app="myApp" ng-controller="MasterController">
<!--Placeholder for views-->
<div ng-view="">
</div>
</body>
How can I make sure this GetSession() is always called at the very beginning of my application start and before any other controller calls and also called only once.
EDIT: This is how I added run method as per Maxim's answer. Still need to figure out a way to wait till $http call returns before going ahead with controllers.
//Some initializing code before Angular invokes controllers
myApp.run(['$rootScope','$http', '$q', function($rootScope, $http, $q) {
return GetSession($http, $q);
}]);
function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
}
Even though some of the solutions here are perfectly valid, resolve property of the routes definition is the way to go, in my opinion. Writing your app logic inside session.then in every controller is a bit too much , we're used such approach too in one of the projects and I didn't work so well.
The most effective way is to delay controller's instantiation with resolve, as it's a built-in solution. The only problem is that you have to add resolve property with similar code for every route definition, which leads to code duplication.
To solve this problem, you can modify your route definition objects in a helper function like this:
function withSession(routeConfig) {
routeConfig.resolve = routeConfig.resolve || {};
routeConfig.resolve.session = ['getSessionPromise', function(getSessionPromise) {
return getSessionPromise();
}]
return routeConfig;
}
And then, where define your routes like this:
$routeProvider.when('/example', withSession({
templateUrl: 'views/example.html',
controller: 'ExampleCtrl'
}));
This is one of the many solutions I've tried and liked the most since it's clean and DRY.
You can't postpone the initialisation of controllers.
You may put your controller code inside a Session promise callback:
myApp.factory( 'session', function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
} );
myApp.controller( 'ctrl', function($scope,session) {
session.then( function() {
//$scope.whatever ...
} );
} );
Alternative: If you don't want to use such callbacks, you could have your session request synchronous, but that would be a terrible thing to do.
You have not provided any details related to GetSession. For scenarios like this you should use the resolve property while defining your routes in $routeProvider. I see you are using resolve already.
What you can do now is to wrap the GlobalSessionToken into a Angular service like GlobalSessionTokenServiceand call it in the resolve to get the token before the route loads. Like
resolve: {
loadData: function($q){
return LoadData2($q,'home');
},
GlobalSessionToken: function(GlobalSessionTokenService) {
return GlobalSessionTokenService.getToken() //This should return promise
}
}
This can then be injected in your controller with
controllers.MasterController = function($rootScope, $http,GlobalSessionToken){