MVC how do i populate dropdownlist without using a model - forms

I am using mvc 4, i have a form in a view (not binded to a model), its using standard html elements.
I want to populate a dropdownlist from a list value (i.e return from controller action)
also based on the selection of a value from the first dropdownlist i want to populate a second dropdownlist
can someone please guide

for the first dropdownlist you loop thru all the available options and add in <option> tags inside <select> for the second dropdownlist you need to either make bunch drop downs and hide/show them, or you create one big list and remove invalid entries base on the first list's selection. You would definitely need to do javascript for 2nd list.

If you don't want to use a Model (you should though), you will have to add the items to ViewData
I'll stub something out for you and you can complete the rest.
Inside your controller, create a list object of what you need. If you are using EntityFrameWork this will look familiar.
var list = context.Table.ToList();
List<System.Web.Mvc.SelectListItem> ddlItems = new List<System.Web.Mvc.SelectListItem>();
foreach (var item in list)
{
ddlItems.Add(new SelectListItem(){ Text = item.Text, Value = item.Value.ToString()});
}
ViewData["DDLItems"] = ddlItems;
#Html.DropDownListFor(x => x.LeagueId,
new SelectList((System.Collections.IEnumerable)ViewData["DDLItems"], "Value", "Text")
, "--Select League--", new { id = "league" })
You can define your second dropdownlist with just a placeholder until the cascade effect happens.
#Html.DropDownListFor(x => x.DivisionId, Enumerable.Empty<SelectListItem>(),
"--Select Division--", new { id = "ddlDivision" })
Your going to need to use JQuery and fire and event when the dropdown changes, and then use Ajax to make a call back to the controller. Theres 2348239 examples online about making Ajax calls, know how to do that because it's done all the time in MVC.
I'll let you figure that part out. One hint, inside the Ajax call you can pass data to the controller. something like this data: { leagueId: value } where value is the value of the dropdownlist you want to cascade off of. leagueId must match type and name of the parameter your controller will expect.
Return a Json object from your controller like so...
public JsonResult GetDivisions(int leagueId)
{
var division = //similar to before, fill a list.
return Json(divisions, JsonRequestBehavior.AllowGet);
}
And then in the success function of your Ajax call, you will populate the Second dropdownlist.
success: function (data) {
$.each(data, function (index, item)
$('#ddlDivision')
.append($('<option></option>')
.val(item.Value)
.html(item.Text))
item.Value and item.Text can be anything, just as long as the Json you return as the properties of Text and Value
IE...
var divisions = (from x in context.Division
select new
{
Text = league + " " + x.Region,
Value = x.DivisionId
}).ToList();

Related

AG-Grid with TreeData using Enterprise Row Model

I am trying to implement a grid with tree data using ag-grid. I am using the Enterprise Row Model. The problem is that when hard coding the data and setting it through setRowData the grid displays perfectly. However, when data is loaded through the enterprise row model, the grid does not render as a tree. In fact, the getDataPath callback is not even being called.
Did anyone manage to use the tree data feature with an enterprise data source as this does not seem to be documented?
Thanks
I am assuming that by Enterprise row model, you mean Serverside row model so you expect tree structured data from server. In that case, I have been able to combine following features in Ag-grid : Infinite scrolling + Tree data + server side row model.
I have even implemented custom filtering and it's working as expected.
Data flow:
We have to enable the server side row model on ag-grid using its configuration.
Implement a fake server and proxy data source objects in JavaScript. The object of data source must contain a method named "getRows" that ag-grid can call. Ag-grid will call this method every time user performs actions such as: scroll, filter, sorting, expanding a parent row to see child rows etc.
Implement a method called onGridReady() which will be called by ag-grid every time it's trying to render the grid first time, and then pass the server and dataSource objects to ag-grid's internal API inside onGridReady().
Implementation (using combination of ReactJS and plain JavaScript):
Enable serverSide row model in ag-grid.
<AgGridReact
columnDefs={this.columnDefs}
rowModelType={this.rowModelType}
treeData={true}
isServerSideGroup={this.isServerSideGroup}
getServerSideGroupKey={this.getServerSideGroupKey}
onGridReady={this.onGridReady}
cacheBlockSize={50}
/>
Implement a fake server and proxy data source objects in JavaScript.
To work with server side row model, you need to supply the data in an instance of ServerSideDataSource in JavaScript. Instance of ServerSideDataSource must have a method called getRows() which will be called by ag-grid every time user scrolls down to get next set of data or a row is expanded for retrieving its children records in Tree structure.
The constructor for ServerSideDataSource accepts a proxy data container, typcailly used in ag-grid example: a fakeServer instance. A singleton instance of fakeServer holds the data that was received from the real server and keeps it for following usage:
a) when ag-grid wants to display child records, it calls getRows. Because we supply a custom implementation of this ServerSideDataSource, we can write logic inside getRows to extract data from this fakeServer instance.
b) When ag-grid tries to display next set of data in infinite scrolling or paging, it checks how much data was last retrieved to ask for next block in infinite scrolling (using startRow and endRow variable).
Defining fake server and server side data source:
function createFakeServer(fakeServerData) {
function FakeServer(allData) {
this.data = allData;
}
FakeServer.prototype.getData = function(request) {
function extractRowsFromData(groupKeys, data) {
if (groupKeys.length === 0) {
return data; //child records are returned from here.
}
var key = groupKeys[0];
for (var i = 0; i < data.length; i++) {
if (data[i].employeeId === key) {
return extractRowsFromData(groupKeys.slice(1), data[i].children.slice());
}
}
}
return extractRowsFromData(request.groupKeys, this.data);
};
return new FakeServer(fakeServerData);
}
function createServerSideDatasource(fakeServer) {
function ServerSideDatasource(fakeServer) {
this.fakeServer = fakeServer;
}
ServerSideDatasource.prototype.getRows = function(params) {
console.log("ServerSideDatasource.getRows: params = ", params);
var rows = this.fakeServer.getData(params.request);
setTimeout(function() {
params.successCallback(rows, rows.length);
}, 200);
};
return new ServerSideDatasource(fakeServer);
}
Implement onGridReady()
Once this dataSource is ready, you have to supply this to ag-grid by calling its API method: params.api.setServerSideDataSource(). This API is available inside onGridReady() method that must be passed to Ag-grid as well. This method is mandatorily required if you're using serverSide row model.
onGridReady = params => {
...
var fakeServer = createFakeServer(jsonDataFromServer);
var dataSource = createServerSideDatasource(fakeServer);
params.api.setServerSideDatasource(dataSource);
}
Providing a key property that help ag-grid identify parent-child relationship. You have to supply these parameters to grid. Check the point (1) in Implementation that has HTML syntax and shows how to supply these methods to ag-grid.
var rowModelType = "serverSide";
var isServerSideGroup = function (dataItem) {
return !!dataItem.children;
};
var getServerSideGroupKey = function (dataItem) {
return dataItem.employeeId;
};
Notice that in getServerSideGroup(), we are returning a boolean value which checks whether children property of current row (i.e. dataItem) has any children or not.
I would request you to separately look through documents for server side row model for each feature and that means Tree data (client model) and Tree data (server side model) have two different approaches. We can't setup one model and expect it to work with data of other model.
Documentation for server side row model : https://www.ag-grid.com/javascript-grid-server-side-model/
Please try this and let me know. I had these requirements a month ago, so I contacted them for their help on Trial Support and they have prepared a plunker for this problem statement:
Working example for Tree data from server side with infinite scroll. https://next.plnkr.co/edit/XON5qvh93CpURbOJ?preview
Note:
Since the tree data is being retrieved from server, you can't use getDataPath.
Tree data would be in nested hierarchy per row unlike the client-side tree model. So unique column names are not encapsulated in an array.
Wrong :
var rowData = [
{orgHierarchy: ['Erica'], jobTitle: "CEO", employmentType: "Permanent"},
{orgHierarchy: ['Erica', 'Malcolm'], jobTitle: "VP", employmentType: "Permanent"}
...
]
Right :
[{
"employeeId": 101,
...
"children": [
{
"employeeId": 102,
...
"children": [
{
"employeeId": 103,
...
},
{
"employeeId": 104,
...
}
]},
]}
}]
There was one point when the grid was not being rendered at all in initial phase when I was just setting up the grid with Enterprise features and so it's their recommendation to use px instead of % for height and width of the wrapper DIV element that contains your Ag-grid element.
Edit:
If you prefer to fetch children record in separate API calls to save initial load time then you can make these API calls inside getRows() method. API call will have success and error callbacks. Inside the success callback method, once you receive the children data, you can pass them to ag-grid using:
params.successCallback(childrenData, childrenData.length);
Example: When a parent row is expanded, it sends a unique key of that parent row (which you must have configured already) through params.request.groupKeys. In this example, I am using JavaScript's fetch() to represent API calling. This method accepts ApiUrl, and optional request parameters' object in case of POST/PUT requests.
ServerSideDatasource.prototype.getRows = function(params) {
//get children data based on unique value of parent row from groupKeys
if(params.request.groupKeys.length > 0) {
fetch(API_URL, {...<required parameters>...})
.then(response => response.json(), error => console.log(error))
.then((childrenData) => {
params.successCallback(childrenData, childrenData.length);
});
}
else {
//this blocks means - get the parent data as usual.
params.successCallback(this.fakeServer.data, this.fakeServer.data.length);
}
};
Adding import 'ag-grid-enterprise' followed by initializing the enterprise key resolved the issue of getDataPath not working correctly.
Using example provided by Akshay Raut above, I was inspired to combine server side pagination, and client side grouping. Following his steps, with a slight change. On the getServerSideDatasource function, you can check if the parent node have the children, and rather than calling the server, just return the children directly. That way, you will be able to display the already loaded children as client side. Here is a sample code:
getServerSideDatasource(): IServerSideDatasource {
return {
getRows: (params) => {
if (params.request.groupKeys.length > 0) {
params.success({
rowData: params.parentNode.data.sales,
rowCount: params.parentNode.data.sales.length,
});
} else {
// Your regular server code to get next page
}
Here is a stackblitz:
https://stackblitz.com/edit/ag-grid-angular-hello-world-1gs4jx?file=src%2Fapp%2Fapp.component.ts
Make sure you are using gridOptions.treeData = true An example is here.
Hierarchy of data should be properly set when you implement the gridOptions.getDataPath(data)
Make sure you have implemented Enterperise.getRows Read more
If the above things don't work, share your code here to understand the overall picture better.
Infinite Scrolling or Enterprise/Serverside datasources are not compatible with Tree Data
https://www.ag-grid.com/javascript-grid-row-models/
So you have to either change your code to use Client Side Row Model or use Row Grouping (only available in enterprise)

Can choices in a list be changed after it has been been rendered?

I have a w2ui form that contains a w2ui Drop List of choices. The choices will be different depending on what the user selected to bring up the form. My question is: can the contents of a Drop List be changed after it has been rendered?
With standard HTML controls, I would do something like this:
$("#mySelect option[value='xyz']").remove();
or
$("#mySelect").append('<option value="abc">abc</option>');
Can these kinds of operations be done with a w2ui Drop List? Any example code?
In w2ui 1.5 you can use $jQueryElement.w2field() to access the w2fild object - and then manipulate it.
Example:
var field = $("#my_input").w2field();
field.options.items = ["my", "new", "items"];
// optionally: pre-select first item
field.setIndex(0);
// if you do NOT use "setIndex" you need to call "refresh" yourself!
// field.refresh();
Note: setIndex() internally calls refresh() - so as stated above, you do not need to call refresh yourself in that case.
If you want to completely clear/empty your field, you can call field.reset().
Edit: after clarification that it's about a form field:
// Note: ``this`` refers to the w2form
// ``field[8]`` refers to a field of type "select"
this.fields[8].options.items = ["my", "new", "items"];
this.record = {
field_select: 'new'
};
this.refresh();

sencha touch 2.0 : How to create a form from a model

Is there an easy way to automatically generate a form panel (I mean the fields and the values), given a model and a store?
Create an instance of your model, then iterate through the empty data object adding input fields to the form panel, this code won't work because the Form.Panel isn't added to anything but you should be able to get the idea.
var objModel = Ext.create('app.model.objModel'),
fp = Ext.create('Ext.form.Panel');
Ext.iterate(objModel.data, function (item) {
fp.add({xtype: 'textfield', name: item, label: item});
}

MVC2 Add Collection of Object to Another Object

Not sure if this has been asked before, but I have an object (Restaurant) and I have a details view of that restaurant. On that details view, I want to have a list of cuisines that the restaurant offers. I want to also have a dropdownlist of the available cuisines and have the ability to click an "Add" button and it adds that cuisine to the Restaurant. I have a RestaurantCuisine table (using Entity Framework) that has a foreign key of the ID of the cuisine from a Cuisine table that has a primary key of ID.
So, now, my question, how do I do this? I sortof understand the concept behind the Create view and then the Create view post, but in this case, I'm not posting back the Restaurant object. So, how do I get the restaurant ID and the Cuisine ID so that I can add that to the restaurant cuisine collection?
Ok, so, now after investigating more, I believe I have asked the wrong question. My actual issue, is that I have a View that displays a Restaurant's details and I have a Details function in my controller for it. This works fine. The next step I want to do is have a dropdownlist with available Cuisines that this restaurant offers and have an 'Add' button next to it. And if you click on the 'Add' button, it adds the value of the item in the dropdownlist to the collection of cuisines setup in the Restaurant object.
Is this "easily" possible? I'm beginning to lose my faith in MVC2 :(
Ok, last try here. Let me ask this, does anyone know how to have a dropdownlist (I have now got this created) and have an "Add" button next to it, and get the selected value from that dropdownlist?
For those of you attempting to use VB.Net with MVC, I'm saying a prayer for you. It's rough. There are rarely any examples, and some syntax is not available that IS available in C#. So, as far as the answer to my original question. It seems as though it was my inexperience that caused the instant flare of the question.
After analying my problem, I figured out a way around it. First of all, let me clarify what I was attempting to do then explain what I did. The goal was to have an object (Restaurant in my case). And I wanted to have a list of properties (cuisines in my case) displayed on the Details view that I could assign to that object (Restaurant). The properties were cuisines that I had setup in a Cuisines table (CuisineId, Name, Description) and when you add a cuisine to a restaurant, it writes a record in another table RestaurantCuisine (RestaurantCuisineId, RestaurantId, CuisineId). So, getting the list of cuisines was the first task and have them display in a dropdownlist. That was done by creating a SelectList of cuisines in the Details view function by creating a CuisineRepository and calling a function that gets a list of all cuisines:
Dim cuiss As New CuisineRepository()
ViewData("Cuisines") = New SelectList(cuiss.FindAllCuisines().ToList(), "CuisineId", "Name")
Once you have this setup as a SelectList, it's easy to display that on the view by doing the following:
<h3>Cuisines:</h3>
<br />
<%= Html.DropDownList("Cuisines")%>
So, the problem after this was the real problem. I wanted to have an ActionLink that would be bound to the value of the DropDownList. I was unsuccessful in this attempt. But luckily, my jQuery is not dusty so I performed a little magic by doing the following:
<script type="text/Javascript">
$(document).ready(function () {
$('#Cuisines').change(function (e) {
$('#SelectedCuisine').val($('#Cuisines').val());
setHref($(this).val());
});
setHref($('#Cuisines').val());
});
function setHref(val) {
if (val) {
$("#addCuisine").attr('href', '/Restaurant/AddCuisine/' + $('#RestaurantId').val() + '?cuisineId=' + val);
}
}
</script>
<h3>Cuisines:</h3>
<br />
<%= Html.DropDownList("Cuisines")%>
<a id="addCuisine" href="">Add</a>
So, as you can see, I simply modify the href of the anchor as the user changes the selected Cuisine. Then, in my controller, I have a function setup to add a cuisine and to remove a cuisine from a restaurant:
Function AddCuisine(ByVal id As Guid, ByVal cuisineId As String) As ActionResult
Try
If ModelState.IsValid Then
'Dim selcuisInp As HtmlInputHidden = ViewData("SelectedCuisine")
Dim selectedCuisineId As Guid = New Guid(cuisineId)
Dim rc As New RestaurantCuisine
rc.RestaurantCuisineId = Guid.NewGuid
rc.RestaurantId = id
rc.CuisineId = selectedCuisineId
'rc.CuisineId = New Guid(selList.SelectedValue.ToString)
rc.CreatedDate = DateTime.Now()
'rc.CreatedBy =
db.AddToRestaurantCuisines(rc)
db.SaveChanges()
End If
Return RedirectToAction("Details", New With {.id = id})
Catch e As Exception
Dim innerE As String = e.InnerException.ToString
Return RedirectToAction("Details", New With {.id = id})
End Try
End Function
Function DeleteRestaurantCuisine(ByVal id As Guid) As ActionResult
Dim rc = (From rcs In db.RestaurantCuisines
Where rcs.RestaurantCuisineId = id
Select rcs).Single()
Dim rid As Guid = rc.RestaurantId
Try
db.RestaurantCuisines.DeleteObject(rc)
db.SaveChanges()
Return RedirectToAction("Details", New With {.id = rid})
Catch ex As Exception
Return RedirectToAction("Details", New With {.id = rid})
End Try
End Function
Notice the RedirectToAction. I had to pass the id of the object (Restaurant) that the Details view required and couldn't find the syntax anywhere. Finally, after a while of searching, found this site that the guy has a few examples of controllers and luckily he gives examples of both C# and VB. To add routeValues to the RedirectToAction, you declare a new list, and use an inline with and add your values with a period (.) preceding them:
Return RedirectToAction("Details", New With {.id = rid})
I hope this helps someone. It sure made the difference in my decision to use MVC. I am 100% sold even though I know I'm going to run into many roadblocks along the way.

Display empty textbox using Html.TextBoxFor on a not-null property in an EF entity

I am using Entity Framework (v4) entities. I have an entity called Car with a Year property of type integer. The Year property does not allow NULL. I have the following in my Create view:
<%= Html.TextBoxFor(model => model.Year) %>
I am required to return a new Car object (due to other requirements) in my HttpGet Create action in the CarController.
Currently, a zero is displayed in the Year textbox because the Year property does not allow NULL. I would like to display an empty textbox in the Create view. How do I do this?
Use Html Attributes Overload. In razor it would be:
#Html.TextBoxFor(model => model.Year, new { Value = "" })
Try this instead:
Html.TextBox("Year", "")
If you are using the strongly typed TextAreaFor helper, then there is no direct way to set a default value. The point of the strongly typed helper is that it binds the text area to a model property and gets the value from there. If you want a default value, then putting in the model would achieve that. You can also just switch to the non-strongly typed TextArea helper. It gives you more a bit more flexibility for cases like this:
#{
var defaultValue= "";
}
#Html.TextArea("Model.Description", defaultValue, new { Value = "added text", #class = "form-control", #placeholder = "Write a description", #rows = 5 })
Try this is you are trying to append to your field or want to modify an existing field with an empty TextBoxFor.
Html.TextBoxFor(model => model.Year, Model.Year="")