ProAngularJS Ch.07 쇼핑 – 장바구니

※ 실행결과: http://study.jeju.onl/sportsstore/app.html

※ Deployd 대시보드 http://study.jeju.onl:5500/dashboard
deployd 실행을 위해서는 다음과 같이 console에서 명령어를 실행하여야 한다.
(* deployd는 mongodb를 자식 프로세서로 생성시켜 데이터를 관리한다)

$ dpd -p 5500 /home/ec2-user/study-html/sportsstore/app.dpd dashboard

이번 단원은 더 많은 controller와 factory, route 기능 등을 사용하였다.
쇼핑몰의 중요 요소인 장바구니 기능을 통해 아이템을 넣고 빼고,
결재를 위한 서머리(총액 등) 기능을 구현했다.

완성된 화면은 다음과 같다.

1. 상품리스트에서 장바구니 담기 버튼을 만들었고
상단 타이틀 부분에 현재 선택한 상품 갯수와 결재 총액을 표시하도록 했다.
‘Checkout’ 버튼을 누르면 장바구니 화면으로 이동한다.
ProAngularJS-ch07-productList

2. 장바구니 화면
테이블 방식으로 출력하고, 각 항목에 대해 삭제 버튼을 두었다
하단에 ‘계속 쇼핑하기’와 ‘지금 결재’ 버튼이 있다. (결재 기능은 다음장에서 계속)
ProAngularJS-ch07-cartSummary

3. 항목 삭제하기를 누른 결과
데이터가 변경되면서 cartSummaryCtrl의 total() 함수가 총액을 다시 계산해 출력한다.
ProAngularJS-ch07-cartSummary_remove

이에 대한 소스코드이다. (주석 작성)

* 파일명: controllers/cartSummaryCtrl.js

angular.module("sportsStore")
.controller("cartSummaryCtrl", function($scope, cart) {

  $scope.cartData = cart.getProducts();
  //console.log("cart : "+($scope.cartData != undefined)+" (length="+$scope.cartData.length+")");

  $scope.total = function() {
    var total = 0;
    for (var i=0; i<$scope.cartData.length; i++) {
      total += ($scope.cartData[i].price * $scope.cartData[i].count);
    }
    return total;
  };

  $scope.remove = function(id) {
    cart.removeProduct(id);
  };

});

* 파일명: components/cart/cart.js

// 모듈명 cart 내에 독립된 서비스 cart를 factory 함수로 정의한다
// factory 함수는 필요한 때에 한번만 호출되어 싱글턴 객체로 서비스를 생성한다
angular.module("cart", [])
.factory("cart", function() {
	
	// 장바구니 배열
	var cartData = [];

	return {
		addProduct: function(id, name, price) {
			// 장바구니 내에 동일 아이템이 있으면 중복 처리
			var addedToExistingItem = false;
			for (var i=0; i<cartData.length; i++) {
				if (cartData[i].id == id) {
					cartData[i].count++;
					addedToExistingItem = true;
					break;
				}
			}
			// 새로운 아이템이면 장바구니 배열에 추가
			if(!addedToExistingItem) {
				cartData.push({
					count: 1, id: id, price: price, name: name
				});
			}
		},
		removeProduct: function(id) {
			// 장바구니 내에 해당 id의 item을 잘라내기 (splice)
			for (var i=0; i<cartData.length; i++) {
				if (cartData[i].id == id) {
					cartData.splice(i, 1);
					break;
				}
			}
		},
		getProducts: function() {
			// 장바구니 데이터를 반환하기
			return cartData;
		}
	};
})
// directove 메서드에 디렉티브 명과 팩토리 함수를 인자로 넘겨준다.
// ** 팩토리 함수는 디렉티브 객체를 정의한다. ex) function(cart) {}
.directive("cartSummary", function(cart) {
	return {
		restrict: "E",
		templateUrl: "components/cart/cartSummary.html",
		controller: function($scope) {

			var cartData = cart.getProducts();

			// 부분뷰 cartSummary.html 에서 사용할 total() 함수 정의
			$scope.total = function() {
				var total = 0;
				for (var i=0; i<cartData.length; i++) {
					total += (cartData[i].price * cartData[i].count);
				}
				return total;
			};

			// 부분뷰 cartSummary.html 에서 사용할 itemCount() 함수 정의
			$scope.itemCount = function() {
				var total = 0;
				for (var i=0; i<cartData.length; i++) {
					total += cartData[i].count;
				}
				return total;
			};

		}
	}
})
;

* 파일명: controllers/sportsStore.js

angular.module("sportsStore")
.constant("dataUrl", "http://localhost:5500/products")
.controller("sportsStoreCtrl", function($scope, $http, dataUrl) {

	/*
	$scope.data = {
		products: [
			{ name: "Product #1", description: "A product", category: "Category #1", price: 100 },
			{ name: "Product #2", description: "B product", category: "Category #1", price: 110 },
			{ name: "Product #3", description: "C product", category: "Category #2", price: 210 },
			{ name: "Product #4", description: "D product", category: "Category #3", price: 202 }
		]
	};
	*/
	$scope.data = {};

	$http.get(dataUrl)
		.success(function(data) {
			$scope.data.products = data;
		})
		.error(function(error) {
			$scope.data.error = error;
		});
	
});

* 파일명: controllers/productListCtrl.js

angular.module("sportsStore")
.constant("productListActiveClass", "btn-primary")
.constant("productListPageCount", 3)
// 콘트롤러 함수의 인자에 외부 모듈 cart의 cart 서비스를 추가했다
// ** cart 서비스에 대한 의존성을 선언한 것임 (injection)
.controller("productListCtrl", function($scope, $filter
		, productListActiveClass, productListPageCount, cart) {
	
	var selectedCategory = null;

	$scope.selectedPage = 1;
	$scope.pageSize = productListPageCount;

	// newCategory 인자가 없으면 null(전체), 있으면 카테고리 지정
	$scope.selectCategory = function(newCategory) {
		selectedCategory = newCategory;
		$scope.selectedPage = 1;
	};

	$scope.selectPage = function(newPage) {
		$scope.selectedPage = newPage;
	}

	// 카테고리 지정이 없거나 또는 해당 카테고리일 경우에만 true
	$scope.categoryFilterFn = function(product) {
		return selectedCategory == null || product.category == selectedCategory;
	};

	// 선택 카테고리와 같으면 강조표시 style 클래스를 반환 (constant로 선언)
	$scope.getCategoryClass = function(category) {
		// Note: .constant 지시자가 먹히려면 controller 함수 인자로 받아야 한다!!
		//console.log("productListActiveClass = " + productListActiveClass);
		return (selectedCategory == category) ? productListActiveClass : "";
	};

	$scope.getPageClass = function(page) {
		return ($scope.selectedPage == page) ? productListActiveClass : "";
	};

	$scope.addProductToCart = function(product) {
		// cart 서비스의 addProduct 함수를 호출
		cart.addProduct(product.id, product.name, product.price);
	};
});

* 파일명 : app.html

<!DOCTYPE html>
<html ng-app="sportsStore">
<head>
  <title>Sports Store</title>
  <link href="/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
  <link href="/node_modules/bootstrap/dist/css/bootstrap-theme.css" rel="stylesheet" />

  /node_modules/angular/angular.js
  /node_modules/angular/angular-route.js
  
    // 최상위 컨트롤러 선언
    // 의존성 선언을 위해 인자로 customFilters, cart, ngRoute 등을 추가
    angular.module("sportsStore", ["customFilters", "cart", "ngRoute"])
    // 라우트를 설정 (config 호출): 의존성으로 routeProvider를 인자로 선언
    .config(function($routeProvider) {
      $routeProvider.when("/complete", {
        templateUrl: "./views/thankYou.html"
      });
      $routeProvider.when("/placeorder", {
        templateUrl: "./views/placeOrder.html"
      });
      $routeProvider.when("/checkout", {
        templateUrl: "./views/checkoutSummary.html"
      });
      $routeProvider.when("/products", {
        templateUrl: "./views/productList.html"
      });
      $routeProvider.otherwise({
        templateUrl: "./views/productList.html"
      });
    });
  
  http://controllers/sportsStore.js
  http://filters/customFilters.js
  http://controllers/productListCtrl.js
  http://components/cart/cart.js
  http://controllers/cartSummaryCtrl.js
</head>
<body ng-controller="sportsStoreCtrl">
  

  
Error ({{ data.error.status }}). The product data was not loaded.
Click here to try again.
<!-- <ng-include src="'views/productList.html'"></ng-include> --> <ng-view /> </body> </html>

* 파일명: components/cart/cartSummary.html

 <style>
 .navbar-right { float: right !important; margin-right: 10px; }
 .navbar-text { margin-right: 15px; }
</style>

* 파일명: views/productList.html

  

{{ item.name }} {{ item.price | currency }}

Add to cart {{ item.description }}
</div> </div>

* 파일명: views/checkoutSummary.html

 <h2>Your cart</h2>

There are no products in your shopping cart. Click here to return to the catalogue
Quantity Item Price Subtotal
{{ item.count }} {{ item.name }} {{ item.price | currency }} {{ (item.price * item.count) | currency }} Remove
Total: {{ total() | currency }}
</div> </div>
  1. 혹시 server.js 에러가 나지 않던가요?
    connect@2.x.x버전 쓰셨나요?

    ㅜㅜㅜㅜ
    Cannot GET /test.html 이런 에러 나지 않던가요~?

    좋아요

    응답

    1. 개발의 편의를 위해 걍 Apache 위에서 돌리고 있습니다.
      connect 관련 문제까지 끼어들까봐서요 ^^

      좋아요

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중

%d 블로거가 이것을 좋아합니다: