I'm used to see in MVC Pattern view, controller and model separated and now in many How To I can see model implemented in controller and for me this practice doesn't respect MVC Pattern.
In my case, what I want is for example :
a POJO Car :
public class Car
{
private int price;
private int doors;
public Car (int px, int dr)
{
this.price = px;
this.doors = dr;
}
}
And after instanciation in my java programme
Car car = new Car(1000, 4);
Now, how can I to put this object into Angular's model please ?
Thanks in advance.
I usually use the following pattern
angular.module('yourServiceName.car.model', [])
.factory('Car', function() {
function Car(car) {
/* extend with lodash.
* Since this is a POJO, don't transform anything you dont need.
*/
_.assign(this, car, this);
}
Car.Enums = {SOME:1, ENUM:2, YOU:3, MIGHT:4, NEED:5};
Car.prototype.method = function() {
/* some model specific method.
* don't throw REST or UI things here tho.
*/
}
Car.create = {
fromJSON: function(json) {
/* only use this for JSON missing transformations, like
* 'Y'|'N' to true|false
* dateString to Date instance
*/
json.startDate = Date.parse(json.startDate); or something.
// I want to see 'Car' in my debug window, instead of 'Object'
return new Car(json);
}
}
return Car;
});
Then on your API service
angular.module('yourService.car.rest', [
'yourService.car.model'
])
.factory('carApi', function(baseURL, Car) {
var path = baseURL+'/car'
var Routes = {
CREATE : path,
LIST : path+'/%s',
DETAILS: path+'/%d'
};
function getCar(id, params) {
// this should blowup if id is not a number
var url = sprintf(Routes.DETAILS, id);
return $http.get(url, params).then(function(response.data) {
// if now JSON transformations are done
// return new Car(response.data);
return Car.create.fromJSON(response.data);
});
}
function listCars(ids, params) {
ids = ids || [];
var _ids = ids.join(',');
var url = sprintf(Routes.LIST, _ids);
return $http.get(url, params).then(function(response) {
return _.map(response.data, Car.create.fromJSON);
});
}
function createCar(oCar) {
/* Hungarian notation indicates I expect an instance of 'Car'
* And not just any object
*/
$http.post(Routes.CREATE, {data: oCar});
}
return {list:listCars, get:getCar, create:createCar};
});
So finally in your controller, you would have something like
angular.module('yourProject.ui.car.list', [
'yourServiceName.car.model',
'yourServiceName.car.rest'
])
.controller('ListCarsController', function ListCarsCtrlr(carsApi, Car) {
$scope.ids = [1, 2, 3];
$scope.load = function() {
var params = {}; // anything else you need to pass
carsApi.list(ids, params).then(function(cars) {
$scope.cars = cars; // all of these are now instanceof Car
})
}
});
Your controller ends up as simple as this:
You can even take it up a nodge if you need Viewmodel
angular.module('yourProject.ui.car.list.viewmodel', [
'yourService.car.model'
])
.factory('CarItemViewmodel', function CarItemVM() {
function CarItemViewmodel(oCar) {
// do some flattening or something you can unit test
this.price = oCar.additionalAttributes.somethingDeep.price;
this.ammount = oCar.someOtherStuff[0].quantity;
};
return CarItemViewmodel;
});
MVC patter of angularjs means that all layer(M,V,C) are in client side.
In this pattern, Server side normally return simple json file which are requested by ajax wheter you implement your server side code with MVC pattern or not.
Therefore If you prefer to use POJO on your server side code, I suggest to conver POJO to json format and serve it as json file.
In the client angularjs code, you can convert json to javascript simple object and use it as model.
You don't. And you can't.
You can make a web service in Java. You can make Angular to call that web service. But you can't push data from Java into "Angular's model", because that's not how the web works.
override the tostring method of your Car class
public String toString(){
return new JSONObject(this).toString();
}
And you can put that object in json and return it as a json
Car car = new Car(1000, 4);
JSONObject jsonReturn = new JSONObject();
jsonReturn.put("car ", car );
Related
(repository with an example of what I'm trying to do is here)
It's my first time using ByteBuddy and I've found myself struggling to merge the contents of two #RequestMapping annotations.
Something like this:
#Validated
#RequestMapping(
path = "/somePrefix",
produces = {"application/text", "application/xml"},
consumes = {"application/text", "application/xml"},
params = {"exampleParam1", "exampleParam2"},
headers = {"key=val", "nokey=val"}
)
public interface ExampleInterface {
#RequestMapping(value = {"/someEndpoint"}, method = {RequestMethod.POST}, produces = {"application/json"}, consumes = {"application/json"})
ResponseEntity<String> someEndpointMethod(String value);
}
And pretending to having this:
public interface ExampleInterface {
#RequestMapping(
value = {"/somePrefix/someEndpoint"},
method = {RequestMethod.POST},
produces = {"application/text", ,application/xml","application/json"},
consumes = {"application/text", "application/xml","application/json"},
params = {"exampleParam1", "exampleParam2"},
headers = {"key=val", "nokey=val"}
)
ResponseEntity<String> someEndpointMethod(String value);
}
I've seen that editing the values can be done replacing the value when the method annotation object is visited, for example:
[...]
#Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (Type.getDescriptor(RequestMapping.class).equals(descriptor)) {
return new AnnotationVisitor(Opcodes.ASM9, super.visitAnnotation(descriptor, visible)) {
#Override
public AnnotationVisitor visitArray(String name) {
if ("produces".equals(name)) {
return new AnnotationVisitor(Opcodes.ASM9, super.visitArray(name)) {
#Override
public void visit(String name, Object value) {
// I'd like to receive an array as value, so I can provide one with all values merged
boolean tryToMerge = false;
if (tryToMerge) {
//I cannot return array with everything
Object[] newValue = new Object[]{value};
value = Arrays.copyOf(newValue, newValue.length + originalAnnotation.produces().length);
System.arraycopy(originalAnnotation.produces(), 0, value, newValue.length, originalAnnotation.produces().length);
} else {
//I can only replace a single value
value = originalAnnotation.produces()[0];
}
// How to set an array in produces?
super.visit(name, value);
}
};
} else {
return super.visitArray(name);
}
}
};
} else {
return super.visitAnnotation(descriptor, visible);
}
}
[...]
However, I'm receiving the array values one per one via visit() and I cannot just return an array with the two values I want to merge (["application/text", "application/xml"]) because it's expecting an String object. I can substitute the value that I'm receiving but I cannot add more.
Besides that, the headers and params arrays are not being visited, which seems logical because no values are on those arrays. However, I'm not sure how I should visit those fields in the #RequestMapping on the method so I can insert the values picked from the class one.
What I am missing here?
Thanks in advance.
In such a case, you can override the onEnd() method of AnnotationVisitor. When invoked, you know that all values are processed and you can invoke super.onValue() from there before delegating to super.onEnd(). If you know the replacements beforehand, you can also simply leave the onValue method empty to drop all existing values and then repopulate the array in the end.
I have the following Sample object being created and adding it to a map as follows.
Is there a way I could have added these set values to Sample object without having to create
an object and directly add it to the map?
Note that I can't change Sample class thus can't be adding constructors nor builder pattern in there.
I am using Java 8
The following works where I create the Sample object and assign it to a variable first and then
add it to a map.
Sample SampleHeader = new Sample();
SampleHeader.setOrder(null);
SampleHeader.setHeader(HEADER_DATA);
data.getAllSamples().put("info", SampleHeader);
Looking for something like this (syntatically wrong)
data.getAllSamples().put(
"info", new Sample().setOrder(null).setHeader(HEADER_DATA)
);
You can use a builder, like this, to allow you to create the object via method chaining:
public class SampleBuilder {
private Sample sample = new Sample();
public SampleBuilder setHeader(Object header) {
sample.setHeader(header);
return this;
}
public SampleBuilder setOrder(Object order) {
sample.setOrder(order);
return this;
}
public Sample build() {
Sample built = sample;
sample = new Sample();
return built;
}
// OR this
public static Sample build(Object header, Object order) {
Sample built = new Sample();
built.setHeader(header);
built.setOrder(order);
return built;
}
}
Then you can call:
data.getAllSamples().put(
"info", new SampleBuilder().setOrder(null).setHeader(HEADER_DATA).build()
);
// OR this
data.getAllSamples().put(
"info", new SampleBuilder(null, HEADER_DATA).build()
);
I am using Swagger version 2 with Java Spring. I have declared a property and it works fine and it generates a drop down list of value I assigned.
#ApiParam(value = "Pass any one Shuttle provider ID from the list", allowableValues = "1,2,3,4,10")
private Long hotelId;
Now, I need a way to populate this list which is passed in allowableValues from my database as it could be random list as well as huge data. How can I assign list of values dynamically from database in this allowableValues?
This question is bit old, I too faced the same problem so thought of adding here which may help some one.
//For ApiModelProperty
#ApiModelProperty(required = true, allowableValues = "dynamicEnum(AddressType)")
#JsonProperty("type")
private String type;
Created a component which implements ModelPropertyBuilderPlugin
#Component
#Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public class ApiModelPropertyPropertyBuilderCustom implements ModelPropertyBuilderPlugin {
private final DescriptionResolver descriptions;
#Autowired
public ApiModelPropertyPropertyBuilderCustom(DescriptionResolver descriptions) {
this.descriptions = descriptions;
}
public void apply(ModelPropertyContext context) {
try {
AllowableListValues allowableListValues = (AllowableListValues) FieldUtils.readField(context.getBuilder(),
"allowableValues", true);
if(allowableListValues!=null) {
String allowableValuesString = allowableListValues.getValues().get(0);
if (allowableValuesString.contains("dynamicEnum")) {
String yourOwnStringOrDatabaseTable = allowableValuesString.substring(allowableValuesString.indexOf("(")+1, allowableValuesString.indexOf(")"));
//Logic to Generate dynamic values and create a list out of it and then create AllowableListValues object
context.getBuilder().allowableValues(allowableValues);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
Similary for ApiParam we can create component which will implement ParameterBuilderPlugin
#Override
public void apply(ParameterContext context) {
#SuppressWarnings("Guava") final Optional<ApiParam> apiParam =
context.resolvedMethodParameter().findAnnotation(ApiParam.class);
if (apiParam.isPresent()) {
final String allowableValuesString = apiParam.get().allowableValues();
//Your logic here
context.parameterBuilder().allowableValues(allowableValues);
}
}
You need to create constructor in SwaggerConfiguration class.
#Autowire service and withdraw data you need from database
assign this to final variable
assign this final variable to allowableValues in annotation
enjoy not efficient api
private final String allowableValues;
public SwaggerConfiguration() {
List<YourEntitiy> list = someService.findAll();
//code to get every value you need and add create comma separated String
StringJoiner stringJoiner = new StringJoiner(",");
stringJoiner.add(list.get(0).getValue());
this.allowableValues = stringJoiner.toString();
}
#ApiParam(allowableValues = allowableValues)
But I think it's bad idea getting all ids from database just to create allowable values. Just validate in api method if that id exist and/or Create new api to get ids from database, use pagination from Spring Data project, like PageImpl<> javadocs
I have to move from an old Java code to a new one using Play! Framework.
In the old code, I called a Java servlet using Ext-Js (Javascript Framework) using this way :
function getTree()
{
var store = Ext.create('Ext.data.TreeStore',
{
root:
{
text: 'System',
id: 'root',
expanded: true
},
proxy:
{
type: 'ajax',
url: 'TreeServlet',
extraParams:
{
mode:'getChildren'
},
reader:
{
type:'json',
root:'result'
}
}
});
Now, I would like to use Play! to do the same, but I do not know how to use it.
. In routes.conf:
GET /tree controllers.Application.makeTree()
. In controller.Application:
public static Result makeTree(){
// What shoul I put here to call the "Servlet"
}
I do not want to use Servlet, but I don't know how to do it.
Thank you for you help!
EDIT 1:
Thank you to all of you!
Here is how I eventually manage to achieve my goal:
public class Tree extends Controller
{
private MenuManager menuManager;
String node;
String mode;
String hash;
ObjectNode response;
public void createTree() throws IOException{
this.menuManager = MenuManager.getMenuManager();
getParams();
createJson(mode, node, hash);
}
public static Result returnJson() throws IOException{
Tree t = new Tree();
t.createTree();
return ok(t.response);
}
}
And in routes:
GET /tree controllers.Tree.returnJson()
What do you guys think? Good practice?
In earlier play frameworks you have to create only static methods for each and every request handler in controller.
But in the newer version (after play 2.0) you don't need to have static methods you can use normal public methods and configure it in routes prefixed with '#' symbol.
And don't maintain or declare attributes within controller class.
Because play is an event driven framework not like oridinary servlet based framework.
It provides REST and it doesn't maintain any httpsession like in servlets.
Session is available in the form of cookies only.
Below is the remodified version of your code,
public class TreeController extends Controller
{
public void createTree() throws IOException{
MenuManager menuManager = MenuManager.getMenuManager();
String mode = request().getQueryString("mode");
String node = request().getQueryString("node");
String hash = request().getQueryString("hash");
TreeNodeDto treeObject = menuManager.buildTree();
ok(treeObject.toJson());
}
}
public class BaseDto<T extends BaseDto<T>> implements Serializable{
public JsonNode toJson() {
return Json.toJson(this);
}
public T fromJson(JsonNode jsonObject) {
return (T) Json.fromJson(jsonObject, this.getClass());
}
}
public static class TreeNodeDto extends BaseDto {
public String hash;
public String name;
public Set<TreeNodeDto> children;
// Override equals and hashcode, because we are using "set" to maintain the child nodes.
}
routes
GET /tree #controllers.TreeController.createTree()
Hope this will give some ideas.
Cheers..!!!
Check the WS object: https://www.playframework.com/documentation/2.3.x/JavaWS
It seems that the return from the Servlet is a Json so check how to process json in play here: https://www.playframework.com/documentation/2.3.x/JavaJsonActions
I believe something like that should do it
public static Promise<Result> makeTree() {
final Promise<Result> resultPromise = WS.url("TreeServlet").setHeader("Content-Type", "application/json").get().map(
new Function<WSResponse, Result>() {
public Result apply(WSResponse response) {
return ok("Feed title:" + response.asJson().findPath("result"));
}
}
);
return resultPromise;
}
Below is the structure for your http request in play,
public static Result makeTree() {
TreeDto treeDto=new TreeDto();
JsonNode jsonResponse = Json.newObject();
try {
treeDto = <<Logic to get the tree objects from db>>;
if(treeDto != null) {
jsonResponse = Json.toJson(treeDto);
}
} catch (XODAOException e) {
Logger.error("Error while building the tree.", e);
jsonResponse = generateErrorResponse("Error while building tree.", e);
}
return ok(jsonResponse);
}
Controller
#RequestMapping(value = "/chartofaccount", method = RequestMethod.GET)
public String getChartofAccount(Model model) {
try {
List CoaCategoryList = new ArrayList();
COACategoriesModel obj;
List<COACategoriesModel> getCoaCategoryList = coaCategoriesService
.getAllCOACategories();
if (getCoaCategoryList.size() > 0) {
for (int i = 0; i < getCoaCategoryList.size(); i++) {
obj = getCoaCategoryList.get(i);
CoaCategoryList.add(obj.getId());
CoaCategoryList.add(obj.getName());
model.addAttribute("list", CoaCategoryList);
}
}
} catch (Exception ex) {
log.error("Exception.." + ex);
}
return "fin/coa";
}
fin/coa.jsp
<ct:Select setIdentity="coaCategoryId" list="${list}" selected="0" />
the above list successfully populate, In my custom tag.
but
Another controller
#RequestMapping(value = "/addCoaMaintenance", method = RequestMethod.POST)
public String addCoaCategory(
#RequestParam("mainAccount") long mainAccount,
#RequestParam("subAccount") long subAccount,
#RequestParam("accountName") String accountName,
#RequestParam("coaCategoryId") long coaCategoryId,
#RequestParam("postingType") int postingType,
#RequestParam("typicalBalance") int typicalBalance,
#RequestParam("isActive") int isActive,
Model model) {
Date sysdate = null;
String Message="";
try{
sysdate = new Date();
Message="Operation Fail!";
COAMaintenanceModel coaMaintenanceModel= new COAMaintenanceModel(mainAccount, subAccount, accountName, coaCategoryId, postingType, typicalBalance, isActive, GetSessionValue.getSysUserId(), GetSessionValue.getSysUserIp(), sysdate, 0);
coaMaintenanceService.AddCOAMaintenance(coaMaintenanceModel);
Message="Account Save Successfully";
model.addAttribute("result_success", Message);
}catch(Exception ex){
log.error("Exception.."+ex);
model.addAttribute("result_fail", Message);
}
return "fin/coa";
}
as the this second one controller return fin/coa.jsp, list is null. how to control this? I am using spring Mvc + hibernate. How to make the first controller list make available, until I required.
I'm not sure I understood the problem correctly, however, the way I see it, you didn't include the list in the model in the second handler method / controller.
Are both handler methods (#RequestMapping annotated methods) inside the same controller class? If yes, one solution is to add #ModelAttribute on a metod that populates the list.
E.g. you would add the following:
#ModelAttribute("list")
public List getCategoryList() {
List CoaCategoryList = new ArrayList();
COACategoriesModel obj;
List<COACategoriesModel> getCoaCategoryList = coaCategoriesService
.getAllCOACategories();
// this if is unncessary
// if (getCoaCategoryList.size() > 0) {
for (int i = 0; i < getCoaCategoryList.size(); i++) {
obj = getCoaCategoryList.get(i);
CoaCategoryList.add(obj.getId());
CoaCategoryList.add(obj.getName());
}
return CoaCategoryList;
}
This will make attribute "list" available in models of all requests in this controller and you will not need to add it via model.addAttribute(...).
Other solution would be to populate the list manually in each handler and add it manually to the model (you already do that for the first handler method, but not in the second one).
If the handler methods are not inside same controller class than you again need to make sure list is in the model before rendering the view (either by making both controller classes extending a common abstract controller were you add functionality to populate the list, or by manually populating the model, or some other way).
PS.
Also, consider taking a look at "Code Conventions for the Java Programming Language" (e.g. variable names in Java usually start with lowercase letter to distinguish them form class names) http://www.oracle.com/technetwork/java/codeconv-138413.html
One solution : test if your list is null or empty
<c:if test="${not empty list}">
<ct:Select setIdentity="coaCategoryId" list="${list}" selected="0" />
</c:if>