added basic form for Edit Pet

Still need to fix an issue for Joda Time to work together with AngularJS
This commit is contained in:
michaelisvy 2015-07-05 17:52:11 +08:00
parent 3d6685d0ae
commit e40a958a8e
15 changed files with 309 additions and 146 deletions

View file

@ -34,7 +34,6 @@ import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
@ -51,7 +50,6 @@ public class Pet extends NamedEntity {
@Column(name = "birth_date")
@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
@DateTimeFormat(pattern = "yyyy/MM/dd")
private DateTime birthDate;
@ManyToOne

View file

@ -18,7 +18,7 @@ package org.springframework.samples.petclinic.web;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.web.bind.WebDataBinder;
@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
/**
@ -56,26 +57,27 @@ public class OwnerResource {
return this.clinicService.findOwnerById(ownerId);
}
@RequestMapping(value = "/owner/{ownerId}", method = RequestMethod.PUT)
public Owner updateOwner(@PathVariable("ownerId") int ownerId, @RequestBody Owner ownerRequest) {
Owner ownerModel = retrieveOwner(ownerId);
// This is done by hand for simplicity purpose. In a real life use-case we should consider using MapStruct.
ownerModel.setFirstName(ownerRequest.getFirstName());
ownerModel.setLastName(ownerRequest.getLastName());
ownerModel.setCity(ownerRequest.getCity());
ownerModel.setAddress(ownerRequest.getAddress());
ownerModel.setTelephone(ownerRequest.getTelephone());
this.clinicService.saveOwner(ownerModel);
return ownerModel;
// TODO: need to handle failure
/**
* Create Owner
*/
@RequestMapping(value = "/owner", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void createOwner(@RequestBody Owner owner) {
this.clinicService.saveOwner(owner);
// TODO: need to handle failure
}
/**
* Read single Owner
*/
@RequestMapping(value = "/owner/{ownerId}", method = RequestMethod.GET)
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
return retrieveOwner(ownerId);
}
/**
* Read List of Owners
*/
@RequestMapping(value = "/owner/list", method = RequestMethod.GET)
public Collection<Owner> findOwnerCollection(@RequestParam("lastName") String ownerLastName) {
@ -92,4 +94,23 @@ public class OwnerResource {
}
}
/**
* Update Owner
*/
@RequestMapping(value = "/owner/{ownerId}", method = RequestMethod.PUT)
public Owner updateOwner(@PathVariable("ownerId") int ownerId, @RequestBody Owner ownerRequest) {
Owner ownerModel = retrieveOwner(ownerId);
// This is done by hand for simplicity purpose. In a real life use-case we should consider using MapStruct.
ownerModel.setFirstName(ownerRequest.getFirstName());
ownerModel.setLastName(ownerRequest.getLastName());
ownerModel.setCity(ownerRequest.getCity());
ownerModel.setAddress(ownerRequest.getAddress());
ownerModel.setTelephone(ownerRequest.getTelephone());
this.clinicService.saveOwner(ownerModel);
return ownerModel;
// TODO: need to handle failure
}
}

View file

@ -23,7 +23,6 @@ import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
@ -31,7 +30,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.support.SessionStatus;
/**
@ -39,15 +38,14 @@ import org.springframework.web.bind.support.SessionStatus;
* @author Ken Krebs
* @author Arjen Poutsma
*/
@Controller
@SessionAttributes("pet")
public class PetController {
@RestController
public class PetResource {
private final ClinicService clinicService;
@Autowired
public PetController(ClinicService clinicService) {
public PetResource(ClinicService clinicService) {
this.clinicService = clinicService;
}
@ -78,15 +76,14 @@ public class PetController {
} else {
this.clinicService.savePet(pet);
status.setComplete();
return "redirect:/owners/{ownerId}";
return "redirect:/owner/{ownerId}";
}
}
@RequestMapping(value = "/owners/*/pets/{petId}/edit", method = RequestMethod.GET)
public String initUpdateForm(@PathVariable("petId") int petId, Map<String, Object> model) {
@RequestMapping(value = "/owner/*/pet/{petId}", method = RequestMethod.GET)
public Pet findPet(@PathVariable("petId") int petId) {
Pet pet = this.clinicService.findPetById(petId);
model.put("pet", pet);
return "pets/createOrUpdatePetForm";
return pet;
}
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}/edit", method = {RequestMethod.PUT, RequestMethod.POST})

View file

@ -1,64 +0,0 @@
<!DOCTYPE html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="petclinic" tagdir="/WEB-INF/tags" %>
<html lang="en">
<jsp:include page="../fragments/staticFiles.jsp"/>
<body>
<script>
$(function () {
$("#birthDate").datepicker({ dateFormat: 'yy/mm/dd'});
});
</script>
<div class="container">
<jsp:include page="../fragments/bodyHeader.jsp"/>
<c:choose>
<c:when test="${pet['new']}">
<c:set var="method" value="post"/>
</c:when>
<c:otherwise>
<c:set var="method" value="put"/>
</c:otherwise>
</c:choose>
<h2>
<c:if test="${pet['new']}">New </c:if>
Pet
</h2>
<form:form modelAttribute="pet" method="${method}"
class="form-horizontal">
<div class="control-group" id="owner">
<label class="control-label">Owner </label>
<c:out value="${pet.owner.firstName} ${pet.owner.lastName}"/>
</div>
<petclinic:inputField label="Name" name="name"/>
<petclinic:inputField label="Birth Date" name="birthDate"/>
<div class="control-group">
<petclinic:selectField name="type" label="Type " names="${types}" size="5"/>
</div>
<div class="form-actions">
<c:choose>
<c:when test="${pet['new']}">
<button type="submit">Add Pet</button>
</c:when>
<c:otherwise>
<button type="submit">Update Pet</button>
</c:otherwise>
</c:choose>
</div>
</form:form>
<c:if test="${!pet['new']}">
</c:if>
<jsp:include page="../fragments/footer.jsp"/>
</div>
</body>
</html>

View file

@ -15,6 +15,7 @@
<script src="scripts/app/main/MainController.js"></script>
<script src="scripts/app/vet/VetController.js"></script>
<script src="scripts/app/owner/OwnerController.js"></script>
<script src="scripts/app/pet/PetController.js"></script>
</head>
<body class="container">

View file

@ -29,8 +29,8 @@ petClinicApp.config(['$stateProvider', '$urlRouterProvider', function($stateProv
url: 'owner/search',
views: {
'content@': {
templateUrl: 'scripts/app/owner/ownerSearchForm.html',
controller: 'ownerSearchController'
controller: 'ownerSearchController',
templateUrl: 'scripts/app/owner/ownerSearchForm.html'
}
}
@ -39,8 +39,8 @@ petClinicApp.config(['$stateProvider', '$urlRouterProvider', function($stateProv
url: 'owner/list?lastName',
views: {
'content@': {
templateUrl: 'scripts/app/owner/ownerList.html',
controller: 'ownerListController'
controller: 'ownerListController',
templateUrl: 'scripts/app/owner/ownerList.html'
}
}
@ -49,8 +49,18 @@ petClinicApp.config(['$stateProvider', '$urlRouterProvider', function($stateProv
url: 'owner/:id',
views: {
'content@': {
templateUrl: 'scripts/app/owner/ownerDetail.html',
controller: 'ownerDetailController'
controller: 'ownerDetailController',
templateUrl: 'scripts/app/owner/ownerDetail.html'
}
}
}).
state('app.ownercreate', {
url: 'owner',
views: {
'content@': {
controller: 'ownerFormController',
templateUrl: 'scripts/app/owner/ownerForm.html'
}
}
@ -59,8 +69,8 @@ petClinicApp.config(['$stateProvider', '$urlRouterProvider', function($stateProv
url: 'owner/:id/edit',
views: {
'content@': {
templateUrl: 'scripts/app/owner/ownerForm.html',
controller: 'ownerFormController'
controller: 'ownerFormController',
templateUrl: 'scripts/app/owner/ownerForm.html'
}
}
@ -69,12 +79,32 @@ petClinicApp.config(['$stateProvider', '$urlRouterProvider', function($stateProv
url: 'vets',
views: {
'content@': {
templateUrl: 'scripts/app/vet/vetList.html',
controller: 'vetController'
controller: 'vetController',
templateUrl: 'scripts/app/vet/vetList.html'
}
}
});
}).
state('app.petedit', {
url: 'owner/:ownerid/pet/:petid',
views: {
'content@': {
controller: 'petFormController',
templateUrl: 'scripts/app/pet/petForm.html'
}
}
}).
state('app.petcreate', {
url: 'owner/:ownerid/pet',
views: {
'content@': {
controller: 'petFormController',
templateUrl: 'scripts/app/pet/petForm.html'
}
}
});
}]);

View file

@ -1,5 +1,12 @@
'use strict';
function loadOwner($scope, $resource, $stateParams) {
var ownerResource = $resource('/petclinic/owner/' + $stateParams.id);
$scope.owner = ownerResource.get();
}
/*
* Owner Search
*/
@ -36,16 +43,13 @@ angular.module('controllers').controller('ownerDetailController', ['$scope', '$r
loadOwner
]);
function loadOwner($scope, $resource, $stateParams) {
var ownerResource = $resource('/petclinic/owner/' + $stateParams.id);
$scope.owner = ownerResource.get();
}
/*
* Owner Edit Form
* Form used to create and edit owners
*/
angular.module('controllers').controller('ownerFormController', ['$scope', '$resource', '$http', '$stateParams', '$state',
function($scope, $resource, $http, $stateParams, $state) {
angular.module('controllers').controller('ownerFormController', ['$scope', '$http', '$resource', '$stateParams', '$state',
function($scope, $http, $resource, $stateParams, $state) {
$scope.submitOwnerForm = {};
@ -60,12 +64,22 @@ function($scope, $resource, $http, $stateParams, $state) {
city: form.city,
telephone: form.telephone
};
var restUrl = "/petclinic/owner/" + $stateParams.id;
$http.put(restUrl, data);
$state.go('app.ownerlist');
if ($state.current.name == 'app.owneredit') {
var restUrl = "/petclinic/owner/" + $stateParams.id;
$http.put(restUrl, data);
$state.go('app.ownerlist');
}
else { // in case of owner creation
var restUrl = "/petclinic/owner";
$http.post(restUrl, data);
$state.go('app.ownerlist');
}
}
loadOwner($scope, $resource, $stateParams);
if ($state.current.name == 'app.owneredit') {
loadOwner($scope, $resource, $stateParams);
}
}]);

View file

@ -23,10 +23,8 @@
<td>
<a class="btn btn-info" ui-sref="app.owneredit({id: owner.id})">Edit Owner</a></td>
<td>
<spring:url value="{ownerId}/pets/new.html" var="addUrl">
<spring:param name="ownerId" value="${owner.id}"/>
</spring:url>
<a href="${fn:escapeXml(addUrl)}" class="btn btn-success">Add New Pet</a></td>
<a ui-sref="app.petcreate({ownerid: owner.id})" class="btn btn-success">Add New Pet</a>
</td>
</tr>
</table>
@ -58,11 +56,7 @@
</tr>
<tr>
<td>
<spring:url value="/owners/{ownerId}/pets/{petId}/edit" var="petUrl">
<spring:param name="ownerId" value="${owner.id}"/>
<spring:param name="petId" value="${pet.id}"/>
</spring:url>
<a href="${fn:escapeXml(petUrl)}">Edit Pet</a>
<a ui-sref="app.petedit( {ownerid: owner.id, petid: pet.id})">Edit Pet</a>
</td>
<td>
<spring:url value="/owners/{ownerId}/pets/{petId}/visits/new" var="visitUrl">

View file

@ -12,4 +12,4 @@
</form>
<br/>
<a href="/owners/new">Add Owners</a>
<a ui-sref="app.ownercreate">Add Owners</a>

View file

@ -0,0 +1,64 @@
'use strict';
function loadPet($scope, $resource, $stateParams) {
var petResource = $resource('/petclinic/owner/' + $stateParams.ownerid +"/pet/" +$stateParams.petid);
$scope.pet = petResource.get();
}
/*
* Form used to create and edit pets
*/
angular.module('controllers').controller('petFormController', ['$scope', '$http', '$resource', '$stateParams', '$state',
function($scope, $http, $resource, $stateParams, $state) {
$scope.submitPetForm = {};
$scope.submitPetForm = function() {
var form = $scope.pet;
// Creating a Javascript object
var data = {
name: form.name,
birthDate: form.birthDate,
type: form.type
};
if ($state.current.name == 'app.petedit') {
var restUrl = "/petclinic/owner/" + $stateParams.ownerid +"/pet/" +$stateParams.petid;
$http.put(restUrl, data);
$state.go('app.ownerdetail');
}
else { // in case of pet creation
var restUrl = "/petclinic/owner/" + $stateParams.ownerid +"/pet";
$http.post(restUrl, data);
$state.go('app.ownerdetail');
}
}
if ($state.current.name == 'app.petedit') {
loadPet($scope, $resource, $stateParams);
}
}]);

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<script>
$(function () {
$("#birthDate").datepicker({ dateFormat: 'yy/mm/dd'});
});
</script>
<div class="container">
<h2>Pet (work in progress) </h2>
<form class="form-horizontal" name="petForm" data-ng-controller="petFormController">
<fieldset>
<div class="control-group" id="owner">
<label class="control-label"> Owner </label>
{{pet.owner.firstName}} {{pet.owner.lastName}}
</div>
<div class="control-group" id="name">
<label class="control-label">Name </label>
<input ng-model="pet.name" name="name" required/>
<span ng-show="petForm.name.$error.required" class="help-inline"> Name is required.</span>
</div>
<div class="control-group" id="birthDate">
<label class="control-label">Birth date </label>
<input ng-model="pet.birthDate" value="{{pet.birthDate | date:'MM/dd/yyyy'}} name="birthDate" required/>
<span ng-show="petForm.name.$error.required" class="help-inline"> birth date is required.</span>
</div>
<div class="control-group">
<petclinic:selectField name="type" label="Type " names="${types}" size="5"/>
</div>
<div class="form-actions">
<c:choose>
<c:when test="${pet['new']}">
<button type="submit">Add Pet</button>
</c:when>
<c:otherwise>
<button type="submit">Update Pet</button>
</c:otherwise>
</c:choose>
</div>
</fieldset>
</form>
</div>

View file

@ -0,0 +1,28 @@
package org.springframework.samples.petclinic.web;
import org.junit.runner.RunWith;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/business-config.xml", "classpath:spring/tools-config.xml", "classpath:spring/mvc-core-config.xml"})
@WebAppConfiguration
@ActiveProfiles("spring-data-jpa")
public abstract class AbstractWebResourceTests {
protected MockMvc mockMvc;
public void runMockSpringMVC(Object resource) {
this.mockMvc = MockMvcBuilders.standaloneSetup(resource).build();
}
public AbstractWebResourceTests() {
super();
}
}

View file

@ -0,0 +1,53 @@
package org.springframework.samples.petclinic.web;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
/**
* Test class for the UserResource REST controller.
*
* @see UserResource
*/
public class PetResourceTests extends AbstractWebResourceTests {
@Autowired
private PetResource petResource;
@Before
public void setup() {
runMockSpringMVC(petResource);
}
/**
* Expected JSon result:
* {
"id":2,
"name":"Basil",
"birthDate":1344211200000,
"type":{
"id":6,
"name":"hamster",
"new":false
},
"visits":[],
"new":false
}
*/
@Test
public void shouldGetAPetInJSonFormat() throws Exception {
ResultActions actions = mockMvc.perform(get("/owner/2/pet/2.json").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
actions.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(jsonPath("$.id").value(2))
.andExpect(jsonPath("$.name").value("Basil"))
.andExpect(jsonPath("$.type.id").value(6));
}
}

View file

@ -7,44 +7,27 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* Test class for the UserResource REST controller.
*
* @see UserResource
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/business-config.xml", "classpath:spring/tools-config.xml", "classpath:spring/mvc-core-config.xml"})
@WebAppConfiguration
@ActiveProfiles("spring-data-jpa")
public class VetControllerTests {
public class VetResourceTests extends AbstractWebResourceTests {
@Autowired
private VetResource vetResource;
@Autowired
private WebApplicationContext ctx;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(vetResource).build();
runMockSpringMVC(vetResource);
}
@Test
public void testGetExistingUser() throws Exception {
public void shouldGetAListOfVetsInJSonFormat() throws Exception {
ResultActions actions = mockMvc.perform(get("/vets.json").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
actions.andExpect(content().contentType("application/json"))