PDA

View Full Version : [CLOSED] mvc form with has many grid



registrator
Feb 13, 2015, 10:18 AM
How can I bid a grid on a model edit form where a model for the form has a List<Customer> property?

Thank you

Dimitris
Feb 13, 2015, 10:33 AM
Hello,

Are you looking for something like this: http://mvc2.ext.net/#/GridPanel_Data_with_Details/Binding/ ?

I think you can make use of the above example if you switch components:

The details Panel becomes the master edit Form.
The master Grid becomes the details (customers) Grid.



Hope it helps.

registrator
Feb 13, 2015, 10:55 AM
I'l; try to use this. Thnx.
But I believe you understand the requirement, it's form for editing a model where a model has a List of objects property that should be displayed in a grid.

Dimitris
Feb 13, 2015, 11:08 AM
Well, yes, based on your requirements I believe the provided example will suit you just fine. Please, do give it a try and let us know of the results.

registrator
Feb 14, 2015, 1:57 PM
This was not easy to transform to the opposite. The functionality is pretty much off.
What I ended up doing is this:


X.GridPanelFor(m => m.Companies, false, true, false).ColumnModel(
X.Column().DataIndex("CompId").Hidden(true),
X.Column().Text("Name").DataIndex("Name").Flex(1)
).ID("GridPanel2")

But I seem to have a problem with the grid selModel property.
I have the script for moving the record from this grid to another from a sample Daniil sent me in another thread:


add : function (source, destination) {
source = source || App.GridPanel1;
destination = destination || App.GridPanel2;
if (source.selModel.hasSelection()) {
var records = source.selModel.getSelection();
source.store.remove(records);
destination.store.add(records);
}
},

In this function I get: TypeError: source.selModel is undefined

Is there something wrong with my GridPanelFor definition?

Dimitris
Feb 15, 2015, 2:58 PM
Hello registrator,

Let me give you a simple example that covers both your questions. The example's Model models a one to many relationship between a Branch (a company branch, a bank branch etc) and its Customers. Sample data are also included.

Example Model:



public class Branch
{
public int ID { get; set; }
public string Name { get; set; }
public List<Customer> Customers { get; set; }

public static List<Branch> GetAll()
{
return new List<Branch>()
{
new Branch
{
ID = 1,
Name = "Town 1 branch",
Customers = new List<Customer>
{
new Customer
{
ID = 100,
Name = "Customer 100",
Phone = "100 123456"
},
new Customer
{
ID = 101,
Name = "Customer 101",
Phone = "101 123456"
},
new Customer
{
ID = 102,
Name = "Customer 102",
Phone = "102 123456"
}
}
},
new Branch
{
ID = 2,
Name = "Town 2 branch",
Customers = new List<Customer>
{
new Customer
{
ID = 200,
Name = "Customer 200",
Phone = "200 123456"
},
new Customer
{
ID = 201,
Name = "Customer 201",
Phone = "201 123456"
}
}
}
};
}
}

public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}


Example Controller:



public class OneToManyController : Controller
{
public ActionResult Index()
{
return View(Branch.GetAll()[0]);
}
}


Example View:



@model Branch

@{
Layout = null;
var X = Html.X();
}

<!DOCTYPE html>

<html>
<head>
<title>MVC GridPanels</title>
<script>
var CustomerSelector = {
add: function (source, destination) {
source = source || App.GridPanel1;
destination = destination || App.GridPanel2;
if (source.getSelectionModel().hasSelection()) {
var records = source.getSelectionModel().getSelection();
source.store.remove(records);
destination.store.add(records);
}
}
};
</script>
</head>
<body>
@X.ResourceManager()

@(X.FormPanelFor(m => m)
.BodyPadding(5)
.DefaultAnchor("100%")
.Width(350)
)

@(X.GridPanelFor(m => m.Customers,false, true, false)
.ID("GridPanel1")
.Width(350)
.Height(250)
.ColumnModel(
X.Column().DataIndex("ID").Hidden(true),
X.Column().Text("Name").DataIndex("Name").Flex(1),
X.Column().Text("Phone").DataIndex("Phone").Width(150)
)
.SelectionModel(X.RowSelectionModel().Mode(Selecti onMode.Multi))
.Buttons(
X.Button()
.Icon(Icon.ResultsetNext)
.Listeners(l =>
l.Click.Handler = "CustomerSelector.add();"
)
)
)

@(X.GridPanel()
.ID("GridPanel2")
.Width(350)
.Height(250)
.Store(X.Store()
.Model(X.Model()
.Fields("ID","Name","Phone")
)
)
.ColumnModel(
X.Column().DataIndex("ID").Hidden(true),
X.Column().Text("Name").DataIndex("Name").Flex(1),
X.Column().Text("Phone").DataIndex("Phone").Width(150)
)
)
</body>
</html>


View lines 30 to 44 answer your first question about how to bind the model to the view. There is nothing special here apart from line 36 where the GridPanel is bound to the list of Customers. I am pretty sure you have figured it out yourself, already.

View lines 13 to 25, 45 to 52 and 55 to 70 answer your second question about how to move records from one GridPanel to another. It is important to note that the first grid defines its SelectionModel. It is also important to see how the source and destination javascript variables are assigned to references of the two gridpanels. I have intentionally highlighted the most important lines to help you correct these specific parts of your code.

Hope it helps.

registrator
Feb 16, 2015, 1:00 PM
Hi Dimitris

Amazing effort and I really do appreciate it. It helped a lot clearing out the understanding.
I must add that at the end I still had a problem and I am posting it here so someone could benefit from the answer.

I had a call on a button like this:


X.Button().Icon(Icon.ResultsetPrevious).Listeners( l => l.Click.Handler = "CountrySelector.remove(GridPanel1, GridPanel2);").ToolTip("Remove Selected Rows"),

And the problem was that I wasn't adding "App." in front of the GridPanel1 and 2 references. It's an EXT.NET thing I guess, right?
So the solution was to call the javascript function for transporting from grid to grid like this:


X.Button().Icon(Icon.ResultsetPrevious).Listeners( l => l.Click.Handler = "CountrySelector.remove(App.GridPanel1, App.GridPanel2);").ToolTip("Remove Selected Rows"),

registrator
Feb 16, 2015, 1:50 PM
Hi Dimistris

I need to move forward now with the functionality.
I am trying to save the edited model and the List property is coming in as null.
This is roughly what I have on the view:


X.FormPanel()
.ID("ClientForm")
.Items(
X.AntiForgeryField(),
X.RadioGroupForEnum(m => m.UserType).FieldLabel("User type").Width(400),
X.TextFieldFor(m => m.Email).CheckChangeEvents(new string[] { "blur" }),
X.Container().Layout(LayoutType.Column)
.Items(
X.Container().Layout(LayoutType.Form).ColumnWidth( 0.6).Items(
X.TextFieldFor(m => m.FirstName)
),
X.Container().Layout(LayoutType.Form).ColumnWidth( 0.4)
.Items(
X.TextFieldFor(m => m.LastName).LabelWidth(70)
)
),
X.TextFieldFor(m => m.CompanyName),
X.Container().Layout(LayoutType.Column)
.Items(
X.Container().Layout(LayoutType.Form)
.Items(
X.GridPanelFor(m => m.Companies, false, true, false)
.ID("GridPanel2")
.SelectionModel(X.RowSelectionModel().Mode(Selecti onMode.Single))
.ColumnModel(
X.Column().DataIndex("CompId").Hidden(true),
X.Column().Text("Has access to").DataIndex("Name").Flex(1)
)
)
)
)

and the button to post:

X.Button().Text("Save")
.DirectEvents(de =>
{
de.Click.Url = Url.Action("SaveClient");
de.Click.Before = "var valid= #{ClientForm}.getForm().isValid();";
de.Click.EventMask.ShowMask = true;
de.Click.FormID = "ClientForm";
}),

Dimitris
Feb 17, 2015, 5:54 PM
A GridPanel and its Columns don't submit anything automatically even if put it into a FormPanel. It has been answered in this thread: http://forums.ext.net/showthread.php?27589-CLOSED-Form-post-issue-for-gridpanel-inside-form-panel

You could try to send the Departments to the controller action via the DirectEvent's ExtraParams. Please, see this example: http://examples2.ext.net/#/GridPanel/Miscellaneous/Submit_Two_Grids/.

You may also find this thread helpful: http://forums.ext.net/showthread.php?24854-CLOSED-MVC-Gridpanel-Form-Post

registrator
Feb 18, 2015, 8:54 PM
Hi Dimitris

Sending the grid data would be ok for me as a solution but my project is MVC so I can't refer to any of the solution threads fully and when I implement it like this:

My grid is
X.GridPanelFor(m => m.Companies, false, true, false)

button click has
de.Click.ExtraParams.Add(new Parameter("Companies", "Ext.encode(#{GridPanel2}.getRowsValues({selectedOn ly : false}))"));

controller method is
public async Task<ActionResult> SaveClient(ClientViewModel model, List<Company> Companies)

I get
Companies to be of zero length.

I imagine the problem is with picking up the data from the grid. How do you pick up the whole List<Company> list of the grid?

Dimitris
Feb 19, 2015, 3:00 PM
I hope you do not mind if I continue with the example I have already presented you with:

View:



@(X.Button()
.Text("Save")
.DirectEvents(de =>
{
de.Click.Url = Url.Action("SaveClient");
de.Click.EventMask.ShowMask = true;
de.Click.FormID = "ClientForm";
de.Click.ExtraParams.Add(
new Parameter("gridData", "Ext.encode(#{GridPanel2}.getRowsValues())",ParameterMode.Raw)
);
})
)



Controller:



public ActionResult SaveClient(string gridData)
{
List<Customer> customers = JSON.Deserialize<List<Customer>>(gridData);
// ...
return this.Direct();
}

registrator
Feb 19, 2015, 4:32 PM
I don't mind, it's easy to follow.
Thank you. This works great!

Dimitris
Feb 19, 2015, 4:41 PM
Glad it helped. Thank you for your cooperation.