How to populate ExtJS Tree with data from Spring MVC Controller? - spring-mvc

As a newbie in ExtJS I'm trying to use it with Spring MVC Controller. So my structure is following: tree.jsp gets JSON data from SomeController.java. I'm using Tiles 2 to connect them.
tree.jsp
<script type="text/javascript">
if (Ext.BLANK_IMAGE_URL.substr(0,5) != 'data'){
Ext.BLANK_IMAGE_URL = '/ext-3.3.1/resources/images/default/s.gif';
}
Ext.onReady(function(){
var proxy = new Ext.data.HttpProxy({
api: {
read: 'jsonTree'
}
});
var treeLoader = new Ext.tree.TreeLoader({
proxy: proxy
});
// shorthand
var Tree = Ext.tree;
var tree = new Tree.TreePanel({
el:'tree-div', //we need to have a div in our html named this
title: "Data Sources",
useArrows:true,
autoScroll:true,
animate:true,
enableDD:true,
containerScroll: true,
rootVisible: false,
loader: treeLoader,
root: {
nodeType: 'async',
text: 'Ext JS',
draggable:false,
id:'source'
}
});
// render the tree
tree.render();
});
SomeController.java
#Controller
#RequestMapping("/some")
public class SomeController {
private Logger log = LoggerFactory.getLogger(CountryReferenceBookController.class);
private static final String LIST_VIEW_KEY = "redirect:list.html";
#Autowired
private RestTemplate restTemplate;
#RequestMapping(value = "/jsonTree/{languageId:\\d+}")
public #ResponseBody Map<String, ? extends Object> getJsonTreeInfo(#PathVariable("languageId") Long languageId) {
List treeList = new ArrayList<Map<String, Object>>();
List<ReferenceBookTitleImpl> list = new ArrayList<ReferenceBookTitleImpl>();
list = restTemplate.getForObject("http://localhost:8084/xcollector/restService/reference/list", list.getClass());
for (ReferenceBookTitleImpl title : list) {
String description = restTemplate.getForObject("http://localhost:8084/xcollector/restService/reference/title/" + title.getId() + "/content/" + languageId, String.class);
//create a child node that is a leaf
Map child = new HashMap();
child.put("id", title.getId());
child.put("text", description);
child.put("checked", Boolean.FALSE);
child.put("leaf", Boolean.TRUE);
treeList.add(child);
}
//the spring mvc framework takes a hashmap as the model..... :| so in order to get the json array to the View, we need to put it in a HashMap
Map modelMap = new HashMap();
modelMap.put("JSON_OBJECT", treeList);
log.info("jsonArray: " + treeList.toString());
return modelMap;
}
}
When I send a GET response to the controller I get a file with json array instead of the populated tree. What is wrong with my code?
Thanks a lot in advance,
L.

Since nobody had asked I decided to answer my question myself. The main problem was wrong understanding of AJAX call.
If you want to populate a tree panel from Spring MVC Controller you have to implement two methods: the first returns a view where the tree panel is located, the second returns a data which the tree panel is populated with.
Let's take a look at the example:
First of all, I use Tiles 2 in the project. So in a file with tiles (templates.xml) I added a following tile:
<definition name="/tree" extends="defaultTemplate">
<put-attribute name="title" value="Tree" />
<put-attribute name="content" value="/WEB-INF/jsp/tree.jsp"/>
</definition>
The Controller which on charge of processing requests on an url http://<ip:port>/<web-app>/tree has two methods:
This method returns jsp view:
#RequestMapping(value = "/tree")
public ModelAndView getTreeView() {
return new ModelAndView("/tree");
}
Next method returns json data:
#RequestMapping(value = "/tree/data", method=RequestMethod.POST)
public #ResponseBody List<Map<String, Object>> getJsonTreeInfo() {
List treeList = new ArrayList<Map<String, Object>>();
List<FooImpl> list = new ArrayList<FooImpl>();
list = restTemplate.getForObject("http://localhost:8084/<web-app>/restService/list", list.getClass());
for (FooImpl foo : list) {
String description = restTemplate.getForObject("http://localhost:8084/<web-app>/restService/title/" + foo.getId() + "/content/11", String.class);
//create a child node that is a leaf
Map child = new HashMap();
child.put("id", foo.getId());
child.put("text", description);
child.put("leaf", Boolean.TRUE);
treeList.add(child);
}
return treeList;
}
Could be unclear how this list becomes json array. I have a message converter which is doing all the job of converting.
JSP side:
<script type="text/javascript">
if (Ext.BLANK_IMAGE_URL.substr(0,5) != 'data'){
Ext.BLANK_IMAGE_URL = '/ext-3.3.1/resources/images/default/s.gif';
}
// application main entry point
Ext.onReady(function() {
// shorthand
var Tree = Ext.tree;
var tree = new Tree.TreePanel({
useArrows: true,
autoScroll: true,
animate: true,
enableDD: true,
containerScroll: true,
border: false,
// auto create TreeLoader
dataUrl: 'tree/data',
root: {
nodeType: 'async',
text: 'List',
draggable: false,
id: 'src'
}
});
// render the tree
tree.render('tree-div');
tree.getRootNode().expand();
});
</script>
<div id="tree-div"></div>
This line dataUrl: 'tree/data' sends POST request to the second contoller's method. It returns the json array. Finally, the tree is popullated with data :) Everyone is happy :)
I hope it helps someone because I spent a lot of time to come to this solution.
Enjoy your job :)

Related

map<string,string> implementation in grpc for .netCore

Using a map in the proto file of grpc for .net core to send a dictionary as a request parameter makes it private field(read-only) in the auto-generated code. So I am unable to assign the dictionary to map and pass it in the API request. How do I make it read-write.?
Sample proto request:
service xyz{
rpc testTrans(TestRequest) returns (TestResponse);
}
message TestRequest {
map<string,string> props = 1;
}
so the auto-generated code looks like this :
public const int PropsFieldNumber = 1;
private static readonly pbc::MapField<string, string>.Codec _map_Props_codec
= new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForString(18), 10);
private readonly pbc::MapField<string, string> Props_ = new pbc::MapField<string, string>();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public pbc::MapField<string, string> Props {
get { return Props_; }
}
So now when i try to assign property in request as below, it throws error :
Property or Indexer TestRequest.Props could not be assigned to -- it is read only.
public static void testTrans(Dictionary<string, string> test)
{
var res = client.InitTrans(new TestRequest
{
Props = test
});
}
It seems like there is being prevented when you want to directly declare and initialize the value with:
var res = client.InitTrans(new TestRequest
{
//Property could not be assigned to -- it is read only...error
Props = new Map<string,string>.Add("somekey", "somevalue");
// Alternatively the same problem will also occur when you do
// Props = new Map<string,string>.Add(SomeDict);
}
Instead there should be work around by initializing your variable and then add the value(s) to the dictionary later (after the initialization of the entire message object).
var res = new TestRequest{};
//test is some dictionary
res.TestRequest.Props.Add(test);
//alternatively you can also add with (key, value)
res.TestRequest.Props.Add("someKey", "someValue);

Response on created context keeps giving me NullStream

I'm trying to write a middleware for batch requests i .net core 2.0.
So far the I have splitted the request, pipe each request on to the controllers.
The controllers return value, but for some reason the response on the created context that I parse to the controllers keeps giving me a NullStream in the body, so I think that there is something that I miss in my setup.
The code looks like this:
var json = await streamHelper.StreamToJson(context.Request.Body);
var requests = JsonConvert.DeserializeObject<IEnumerable<RequestModel>>(json);
var responseBody = new List<ResponseModel>();
foreach (var request in requests)
{
var newRequest = new HttpRequestFeature
{
Body = request.Body != null ? new MemoryStream(Encoding.ASCII.GetBytes(request.Body)) : null,
Headers = context.Request.Headers,
Method = request.Method,
Path = request.RelativeUrl,
PathBase = string.Empty,
Protocol = context.Request.Protocol,
Scheme = context.Request.Scheme,
QueryString = context.Request.QueryString.Value
};
var newRespone = new HttpResponseFeature();
var requestLifetimeFeature = new HttpRequestLifetimeFeature();
var features = CreateDefaultFeatures(context.Features);
features.Set<IHttpRequestFeature>(newRequest);
features.Set<IHttpResponseFeature>(newRespone);
features.Set<IHttpRequestLifetimeFeature>(requestLifetimeFeature);
var innerContext = _factory.Create(features);
await _next(innerContext);
var responseJson = await streamHelper.StreamToJson(innerContext.Response.Body);
I'm not sure what it is I'm missing in the setup, since innerContext.Response.Body isn't set.
One of the endpoints that I use for testing and that gets hit looks like this
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
I found the error, or two errors for it to work.
First I had to change my newResponse to
var newRespone = new HttpResponseFeature{ Body = new MemoryStream() };
Since HttpResponseFeature sets Body to Stream.Null in the constructor.
When that was done, then Body kept giving an empty string when trying to read it. That was fixed by setting the Position to Zero like
innerContext.Response.Body.Position = 0;

Post data with free-jqgrid, what are the code in client and Web API side?

I am using ASP.NET MVC 6 Web API. In the View I use free-jqgrid.
Let's borrow Oleg's free jqgrid data to demonstrate my purpose. We already have the table shown.
Next I am going to add new Vendor. Please notify that there is primary key id(identity column) in the database. We don't want it displaying in the screen.
In VendorRespository.cs, I add the new Vendor as
public void AddVendor(Vendor item)
{
using (VendorDataContext dataContext = new VendorDataContext())
{
dataContext.Database.Connection.ConnectionString = DBUtility.GetSharedConnectionString(
"http://centralized.admin.test.com");
var newVendor = dataContext.Vendors.Create();
newVendor.Company = item.Company;
newVendor.ContactName = item.ContactName;
newVendor.ContactPhone = item.ContactName;
newVendor.UserName = item.UserName;
newVendor.UserKey = item.UserKey;
newVendor.Active = item.Active;
newVendor.FacilityId =item.FacilityId;
newVendor.ClientID = item.ClientID;
dataContext.SaveChanges();
}
}
My questions:
Not sure the script like?
<script>
API_URL = "/VendorManagement/";
function updateDialog(action) {
return {
url: API_URL
, closeAfterAdd: true
, closeAfterEdit: true
, afterShowForm: function (formId) { }
, modal: true
, onclickSubmit: function (params) {
var list = $("#jqgrid");
var selectedRow = list.getGridParam("selrow");
rowData = list.getRowData(selectedRow);
params.url += rowData.Id;
params.mtype = action;
}
, width: "300"
};
}
jQuery("#jqgrid").jqGrid('navGrid',
{ add: true, edit: true, del: true },
updateDialog('PUT'),
updateDialog('POST'),
updateDialog('DELETE')
);
In the controller, not sure what is the code?
// POST
public HttpResponseMessage PostVendor(Vendor item)
{
_vendorRespository.AddVendor(item);
var response = Request.CreateResponse<Vendor>(HttpStatusCode.Created, item);
string uri = Url.Link("DefaultApi", new { id = item.Id });
response.Headers.Location = new Uri(uri);
return response;
}
My code has many compiling errors such as
'HttpRequest' does not contain a definition for 'CreateResponse' and the best extension method overload 'HttpRequestMessageExtensions.CreateResponse(HttpRequestMessage, HttpStatusCode, Vendor)' requires a receiver of type 'HttpRequestMessage'
Please help me to get rid of the error and inappropriate code.
EDIT:
I borrowed the code snippet from here.
I need add the code such as
[Microsoft.AspNet.Mvc.HttpGet]
public dynamic GetVendorById(int pkey)
{
return null;
}
And
// POST
[System.Web.Http.HttpPost]
public HttpResponseMessage PostVendor(Vendor item)
{
_vendorRespository.AddVendor(item);
var response = Request.CreateResponse<Vendor>(HttpStatusCode.Created, item);
string uri = Url.Link("/VendorManagement/GetVendorById", new { id = item.pkey });
response.Headers.Location = new Uri(uri);
return response;
}

ASP.Net Passing array from Controller to View to javascript

I have ASP.Net MVC3 application. And My task is to read points from file and display them. For reading points from file I use DLL. I draw them in javascript.
Here is my code:
// Controller
{
IntPtr lib = LoadLibrary("lib.dll");
// getting points from DLL. I get then as array of strings to serialise later
string[] points = new string[0];
GetArrayPointsAsStrings(points); // I get coordinates - [x1, y1, z1, x2, y2, z2, ..]
FreeLibrary(lib);
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
string serialized_points = serializer.Serialize(points);
return View(new MyModel(serialized_points));
}
// Model
public class MyModel
{
public string Points { get; private set; }
}
// View
#{
var m = Model;
var array_of_strings_points = m.Points;
}
// object from javascript
var js_obj = new js_obj();
var points_string = '#Html.Raw(#array_of_strings_points)';
js_obj.DrawPoints(points_string);
//js
//.. and here in DrawPoints() I parse the string of points and draw point
My question is: Is it ok to pass all serialized points to string from controller to javascript this way? May be there is better way to pass all points to javascript?
Thanks,
Zhenya
My question is: Is it ok to pass all serialized points to string from
controller to javascript this way? May be there is better way to pass
all points to javascript?
On your model keep the real CLR type (string[]). You don't need to be JSON serializing in your controller:
public class MyModel
{
public string[] Points { get; set; }
}
then have your controller populate and pass the view model to the view:
string[] points = ...
MyModel model = new MyModel();
mode.Points = points;
return View(model);
and finally in your view:
#model MyModel
...
<script type="text/javascript">
var js_obj = new js_obj();
var points = #Html.Raw(Json.Encode(Model.Points));
js_obj.DrawPoints(points);
</script>
Now if your js_obj.DrawPoints expects a JSON string as parameter instead of a javascript array of
strings you could pass it like this:
#model MyModel
...
<script type="text/javascript">
var js_obj = new js_obj();
var points = #Html.Raw(Json.Encode(Model.Points));
js_obj.DrawPoints(JSON.stringify(points));
</script>

How to unit test code that uses HostingEnvironment.MapPath

I have some code that uses HostingEnvironment.MapPath which I would like to unit test.
How can I setup HostingEnvironment so that it returns a path and not null in my unit test (mstest) project?
Why would you have a code that depends on HostingEnvironment.MapPath in an ASP.NET MVC application where you have access to objects like HttpServerUtilityBase which allow you to achieve this and which can be easily mocked and unit tested?
Let's take an example: a controller action which uses the abstract Server class that we want to unit test:
public class HomeController : Controller
{
public ActionResult Index()
{
var file = Server.MapPath("~/App_Data/foo.txt");
return View((object)file);
}
}
Now, there are many ways to unit test this controller action. Personally I like using the MVcContrib.TestHelper.
But let's see how we can do this using a mocking framework out-of-the-box. I use Rhino Mocks for this example:
[TestMethod]
public void Index_Action_Should_Calculate_And_Pass_The_Physical_Path_Of_Foo_As_View_Model()
{
// arrange
var sut = new HomeController();
var server = MockRepository.GeneratePartialMock<HttpServerUtilityBase>();
var context = MockRepository.GeneratePartialMock<HttpContextBase>();
context.Expect(x => x.Server).Return(server);
var expected = #"c:\work\App_Data\foo.txt";
server.Expect(x => x.MapPath("~/App_Data/foo.txt")).Return(expected);
var requestContext = new RequestContext(context, new RouteData());
sut.ControllerContext = new ControllerContext(requestContext, sut);
// act
var actual = sut.Index();
// assert
var viewResult = actual as ViewResult;
Assert.AreEqual(viewResult.Model, expected);
}
Well I was writing a test today for code that I don't control and they used
private static String GetApplicationPath()
{
return HostingEnvironment.ApplicationVirtualPath.TrimEnd('/');
}
so here is a C# reflection hack to set that value
var path = "/aaaa/bb";
HostingEnvironment hostingEnvironment;
if (HostingEnvironment.IsHosted.isFalse())
new HostingEnvironment();
hostingEnvironment = (HostingEnvironment)typeof(HostingEnvironment).fieldValue("_theHostingEnvironment");
var virtualPath = "System.Web".assembly()
.type("VirtualPath").ctor();
virtualPath.field("_virtualPath", path);
//return virtualPath.prop("VirtualPathString");
//return virtualPath.prop("VirtualPathStringNoTrailingSlash");
hostingEnvironment.field("_appVirtualPath", virtualPath);
//hostingEnvironment.field("_appVirtualPath") == virtualPath;
return HostingEnvironment.ApplicationVirtualPath == path;
//using System.Web.Hosting
It will depend on what mocking or isolation framework you are using. You might want to look into either a) creating a wrapper type around the static property that can be mocked, or b) using a framework which can mock static properties - e.g. Moles or Typemock Isolator
As i faced same issue i changed my code bit.
From
strhtmlTemplate = File.ReadAllText(System.Web.Hosting.HostingEnvironment.MapPath(Lgetfilepath.CVal));
To
strhtmlTemplate = File.ReadAllText(HttpContextFactory.Current.Server.MapPath(Lgetfilepath.CVal));
For Unit test
public HttpContextBase mockHttpContextBase()
{
var moqContext = new Mock<HttpContextBase>();
var moqRequest = new Mock<HttpRequestBase>();
var moqServer = new Mock<HttpServerUtilityBase>();
var moqPath = new Mock<ConfigurationBase>();
moqContext.Setup(x => x.Request).Returns(moqRequest.Object);
moqContext.Setup(x => x.Server.MapPath(#"~\Data\xxxxxxx")).Returns(Environment.CurrentDirectory+#"\xxxxxx");
setupApplication(moqContext);
return moqContext.Object;
}
Now we while Writing TestClass you need to refer above method to mock. Hope it will helpful for your TestCases.
MockDataUT mockData = new MockDataUT();
var mockRequestContext = new HttpRequestContext();
HttpContextFactory.SetCurrentContext(mockData.mockHttpContextBase());
Just use this code..
Make a new folder name Reference in root directory and added your file inside this folder.
Use this
public static XElement GetFile()
{
HttpContext.Current = new HttpContext(new HttpRequest("", "http://www.google.com", ""), new HttpResponse(new StringWriter()));
var doc = new XmlDocument();
var file = HttpContext.Current.Server.MapPath("\\") + "abc.xml";
doc.Load(file);
var e = XElement.Load(new XmlNodeReader(doc));
return e;
}

Resources