Grid delete not working in Partial View

15 posts, 2 answers
  1. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 30 Sep 2013 Link to this post

    I'm not sure if this is an issue my grid configuration or just a general MVC issue I am having but I've got a grid in a partial view and when I try to delete an item it is posting to the parent view controller method instead of the method I have configured in the grid.  I believe I have the views configured correctly but I must be missing something.

    View:
    @model PASS.ViewModels.Proposals.RequiredViewModel
     
    @using (Ajax.BeginForm("Required", "Proposals", new AjaxOptions { UpdateTargetId = "requiredReturnMsg", HttpMethod = "Post" }))
    {
      
    @Html.HiddenFor(model => model.Proposal_ID, Model.Proposal_ID)
     
    <div class="editor-container">
     
        <div class="editor-label">
            @Html.Label("Funding Source")
        </div>
        <div class="editor-field">
            @Html.DropDownListFor(model => model.Funding_Source_ID,  new SelectList(Model.FundingSources, "Value",  "Text"), "(Select One)")
            @Html.ValidationMessageFor(model => model.Funding_Source_ID)
        </div>   
        <br class="clear" />
        <div class="editor-label">
            @Html.Label("Specify (only if requested)")
        </div>
        <div class="editor-field">
            @Html.TextBoxFor(model => model.Funding_Specify, new { style = "width: 350px;" })
            @Html.ValidationMessageFor(model => model.Funding_Specify)
        </div>
        <br class="clear" />
        <br />
        <br />
        <p><input type="submit" value="Add Funding Source" /></p>
        <br />
        <br />
        @Html.Action("FundingSources", "Proposals", new { proposalID = Model.Proposal_ID })
        <br />
        <br />
     
        <div id="requiredReturnMsg"></div>
     
    </div>
         
    }
    The partial view is called by the @Html.Action

    Partial View:
    @model IEnumerable<PASS.ViewModels.Proposals.FundingSourcesViewModel>
        
    @using (Ajax.BeginForm("FundingSources", "Proposals", new AjaxOptions { }))
    {
     
    <div style="width:95%;">
    @(Html.Kendo().Grid(Model)
        .Name("gvFundingSources") 
        .Columns(columns =>
        {
            columns.Bound(o => o.FundingSourceDescription).Title("Funding Source");
            columns.Bound(o => o.Funding_Specify).Title("Specifics");
            columns.Command(command => { command.Destroy(); }).Width(50);
        })
        .Sortable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Model(model => model.Id(o => o.ID))
            .Read(read => read.Action("FundingSources", "Proposals"))
            .Destroy(destroy => destroy.Action("DeleteFundingSource", "Proposals"))
        )
    )
    </div>
         
    }

    Controller Methods:
    public ActionResult Required(int proposalID)
    {
        var context = new PASSEntities();
     
        RequiredViewModel model = new RequiredViewModel
        {
            Proposal_ID = proposalID,
            FundingSources = context.Funding_Sources.ToList().Select(m => new SelectListItem { Value = m.ID.ToString(), Text = m.Description }).ToList()
        };
     
        return PartialView(model);
    }
     
    [HttpPost]
    public ActionResult Required(RequiredViewModel model)
    {
        try
        {
            var context = new PASSEntities();
            var fundingsource = context.Proposal_Funding_Sources.Find(model.ID);
            bool bAdd = false;
     
            if (fundingsource == null) {
                fundingsource = new Proposal_Funding_Sources();
                bAdd = true;
            }
     
            fundingsource.Funding_Source_ID = model.Funding_Source_ID;
            fundingsource.Proposal_ID = model.Proposal_ID;
            fundingsource.Funding_Specify = model.Funding_Specify;
     
            if (bAdd) context.Proposal_Funding_Sources.Add(fundingsource);
            else context.Entry(fundingsource).State = System.Data.EntityState.Modified;
            context.SaveChanges();
     
            var message = new SystemMessage(Models.eMessageType.SUCCESS);
            message.Message = "Your data has been saved!";
            return PartialView("_SystemMessage", message);
        }
        catch
        {
            var message = new SystemMessage(Models.eMessageType.ERROR);
            message.Message = "Save Failed!";
            return PartialView("_SystemMessage", message);
        }
     
    }
     
    [ChildActionOnly]
    public ActionResult FundingSources(int proposalID)
    {
        var context = new PASSEntities();
     
        var model = (from a in context.Proposal_Funding_Sources
                     join b in context.Funding_Sources on a.Funding_Source_ID equals b.ID
                     where a.Proposal_ID == proposalID
                     select new FundingSourcesViewModel()
                     {
                         ID = a.ID,
                         Proposal_ID = a.Proposal_ID,
                         Funding_Source_ID = a.Funding_Source_ID,
                         Funding_Specify = a.Funding_Specify,
                         FundingSourceDescription = b.Description
                     });
     
        return PartialView(model);
    }
     
    [HttpPost]
    [ChildActionOnly]
    public ActionResult DeleteFundingSource(int id)
    {
        try
        {
            using (PASSEntities context = new PASSEntities())
            {
                var fundingsource = context.Proposal_Funding_Sources.Find(id);
                context.Entry(fundingsource).State = System.Data.EntityState.Deleted;
                context.SaveChanges();
            }
     
            var message = new SystemMessage(Models.eMessageType.SUCCESS);
            message.Message = "Your data has been saved!";
            return PartialView("_SystemMessage", message);
        }
        catch
        {
            var message = new SystemMessage(Models.eMessageType.ERROR);
            message.Message = "Save Failed!";
            return PartialView("_SystemMessage", message);
        }
    }

    When I try to debug, the DeleteFindingSource Post method is not reached but instead the Required Post method (which is the parent view) is called.

    Thanks.
  2. Daniel
    Admin
    Daniel avatar
    2214 posts

    Posted 02 Oct 2013 Link to this post

    Hello Stephen,

    You are using nested forms which is not supported by the browsers. You should remove the form from the partial view. Also, a form will be rendered for each delete button when the data is bound on the server so you should bind the data entirely via Ajax.
    Since the Grid is using Ajax editing, this however should not be causing the problem with the destroy action. Could you check if there are any JavaScript errors? 

    Regards,
    Daniel
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  3. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 02 Oct 2013 Link to this post

    Hi Daniel,

    Ok that makes sense about the nested forms.  I have removed the form from the partial view.  The grid tells the read and the delete which controller methods to use so that makes sense as well.  I have the grid set to bind via Ajax but is there something I need to do further to "bind the data entirely via ajax"?  Do I need to have jquery on the page to hook the grid to the controller method instead of on the .Datasource?

    There are no javascript errors on my page.

    Thanks for the help.

    Steve
  4. Daniel
    Admin
    Daniel avatar
    2214 posts

    Posted 04 Oct 2013 Link to this post

    Hello Steve,

    You should remove the data that is passed to the GridBuilder constructor and load the data only with the Read action in order to avoid rendering a form for each destroy button on the server e.g.

    @(Html.Kendo().Grid<PASS.ViewModels.Proposals.FundingSourcesViewModel>()
        .Name("gvFundingSources")
        .Columns(columns =>
        {
            columns.Bound(o => o.FundingSourceDescription).Title("Funding Source");
            columns.Bound(o => o.Funding_Specify).Title("Specifics");
            columns.Command(command => { command.Destroy(); }).Width(50);
        })
        .Sortable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Model(model => model.Id(o => o.ID))
            .Read(read => read.Action("FundingSources", "Proposals"))
            .Destroy(destroy => destroy.Action("DeleteFundingSource", "Proposals"))
        )
    )
    Regards,
    Daniel
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  5. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 04 Oct 2013 Link to this post

    I see the difference between my grid partial view and the code you posted is that I don't strongly type the partial view to my view model but instead just reference that view model directly in the grid.  I have done that and now when the partial view loads it loads an empty grid.  Does the grid not fire the read action automatically when it loads?  Do I need to do something else to make it fire?
  6. Daniel
    Admin
    Daniel avatar
    2214 posts

    Posted 08 Oct 2013 Link to this post

    Hi Steve,

    A request should be made immediately to the server unless the autoBind option is set to false but it seems that the action methods used for the Read and Destroy operations are not configured correctly for Ajax binding. The "FundingSources" action is also configured as ChildAction so it will not be called at all. Please check this documentation topic which demonstrates how to configure the action methods when Ajax binding is used.

    Regards,
    Daniel
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  7. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 08 Oct 2013 Link to this post

    Ok I've got it almost working now but there are a few issues:

    1.  It is actually displaying the JSON in my partial view instead of the grid itself.

    2. I removed the ChildActionOnly but I thought that was necessary since it is a partial view and I don't want that being browsed to other than through its parent view?

    3. When I am doing the get and converting it to JSON, I am using LINQ to fill the viewmodel but I need to actually select from both tables in the join and I'm not sure how to do that with the conversion to JSON.  It doesn't seem to work like it does when I normally fill my viewmodels from LINQ.

    My updated code is below.

    Controller:

    public ActionResult FundingSources(int proposalID, [DataSourceRequest]DataSourceRequest request)
    {
        using (var context = new PASSEntities())
        {
            IQueryable<Proposal_Funding_Sources> model =
                    (from a in context.Proposal_Funding_Sources
                     join b in context.Funding_Sources on a.Funding_Source_ID equals b.ID
                     where a.Proposal_ID == proposalID
                     select a);
            DataSourceResult result = model.ToDataSourceResult(request, f => new FundingSourcesViewModel
            {
                ID = f.ID,
                Proposal_ID = f.Proposal_ID,
                Funding_Source_ID = f.Funding_Source_ID,
                Funding_Specify = f.Funding_Specify
            });
     
            return Json(result, JsonRequestBehavior.AllowGet);
        }
    }
     
     
    [HttpPost]
    public ActionResult DeleteFundingSource([DataSourceRequest]DataSourceRequest request, FundingSourcesViewModel model)
    {
        using (var context = new PASSEntities())
        {
            var fundingsource = new Proposal_Funding_Sources()
            {
                ID = model.ID,
                Proposal_ID = model.Proposal_ID,
                Funding_Source_ID = model.Funding_Source_ID,
                Funding_Specify = model.Funding_Specify
            };
            context.Proposal_Funding_Sources.Attach(fundingsource);
            context.Proposal_Funding_Sources.Remove(fundingsource);
            context.SaveChanges();
        }
        return Json(new[] { model }.ToDataSourceResult(request));
    }

    Partial View with Grid:

    @(Html.Kendo().Grid<PASS.ViewModels.Proposals.FundingSourcesViewModel>()
        .Name("gridFundingSources")
        .Columns(columns =>
        {
            columns.Bound(o => o.FundingSourceDescription).Title("Funding Source");
            columns.Bound(o => o.Funding_Specify).Title("Specifics");
            columns.Command(command => { command.Destroy(); }).Width(50);
        })
        .Sortable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Model(model => model.Id(o => o.ID))
            .Read(read => read.Action("FundingSources", "Proposals"))
            .Destroy(destroy => destroy.Action("DeleteFundingSource", "Proposals"))
        )
    )





  8. Answer
    Daniel
    Admin
    Daniel avatar
    2214 posts

    Posted 08 Oct 2013 Link to this post

    Hello Steve,

    The action that returns the Grid View should not be replaced. You should include a separate action method from which the data to be read via Ajax. It should be possible to perform the join and the projection the same way:

    [ChildActionOnly]
    public ActionResult FundingSources(int proposalID)
    {
        ViewBag.proposalID = proposalID;
      
        return PartialView();
    }
     
    public ActionResult ReadFundingSources(int proposalID, [DataSourceRequest]DataSourceRequest request)
    {
        using (var context = new PASSEntities())
        {
            var model = (from a in context.Proposal_Funding_Sources
                         join b in context.Funding_Sources on a.Funding_Source_ID equals b.ID
                         where a.Proposal_ID == proposalID
                         select new FundingSourcesViewModel()
                         {
                             ID = a.ID,
                             Proposal_ID = a.Proposal_ID,
                             Funding_Source_ID = a.Funding_Source_ID,
                             Funding_Specify = a.Funding_Specify,
                             FundingSourceDescription = b.Description
                         });
            DataSourceResult result = model.ToDataSourceResult(request);
      
            return Json(result, JsonRequestBehavior.AllowGet);
        }
    }
    @(Html.Kendo().Grid<PASS.ViewModels.Proposals.FundingSourcesViewModel>()
        .Name("gvFundingSources")
        .Columns(columns =>
        {
            columns.Bound(o => o.FundingSourceDescription).Title("Funding Source");
            columns.Bound(o => o.Funding_Specify).Title("Specifics");
            columns.Command(command => { command.Destroy(); }).Width(50);
        })
        .Sortable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Model(model => model.Id(o => o.ID))
            .Read(read => read.Action("ReadFundingSources", "Proposals", new {proposalID = ViewBag.proposalID}))
            .Destroy(destroy => destroy.Action("DeleteFundingSource", "Proposals"))
        )
    )


    Regards,
    Daniel
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  9. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 08 Oct 2013 Link to this post

    Thank you!  Its working now.  I just have two more questions:

    1. Is there a way to pass the proposalID other than using ViewBag/ViewData in this case?

    2. When the grid is set to use Ajax binding instead of Server binding, the styling changes.  The rows of the grid become taller as if there is more padding.  Same with the buttons on the grid.  I have experienced this in other places in my project with the grid as well when switching from server to ajax.

    Thanks again,

    Steve
  10. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 08 Oct 2013 Link to this post

    I just realized that I didn't really need the grid in a partial view so I moved it directly to the parent view.  This eliminated the need for the ViewBag since I have access to the ID passed into the parent view through the parent model.

    I still need to fix the styling issues of the grid but I also now just noticed when I add a record using input fields in the parent view that the grid does not update as it does with the delete.  Is there some jquery I need to call in order to refresh the grid?
  11. Daniel
    Admin
    Daniel avatar
    2214 posts

    Posted 10 Oct 2013 Link to this post

    Hi,

    You can refresh the Grid data by calling its dataSource read method.
    I am not sure what could cause the rows in Ajax binding to be taller. The Grid uses the same structure and the same classes in both modes. If anything, the rows in server binding should be taller since a form and a submit button will be rendered for the delete command. Could you provide a runnable sample that demonstrates the difference in the two modes?

    Regards,
    Daniel
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  12. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 23 Oct 2013 Link to this post

    Attached is a sample project that has grids with both server and ajax binding.  When you run the project and go to the Index page you will see two links: one for server binding and one for ajax binding.

    As for the dataSource read method, can you give me an example of how I would use that.  For this view I've got textboxes in the view and then the grid is the partial view.  Once they fill the textboxes and hit submit it should add the rows to the grid.  It adds them to the database but they don't show up in the grid unless I refresh the page.  So I'm not sure where/when I would call the dataSource read method.
  13. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 23 Oct 2013 Link to this post

    Forgot to attach the project, sorry.
  14. Answer
    Daniel
    Admin
    Daniel avatar
    2214 posts

    Posted 25 Oct 2013 Link to this post

    Hello,

    The height is different because the width of the command column is insufficient to display the button. Increasing the width should resolve the problem:

    columns.Command(command => { command.Destroy(); }).Width(100);
    As for reloading the data - you should get the Grid object and call its dataSource.read method after the changes have been made to the database:
    function onDataUpdated() {
        var grid = $("#gridProposalTypes").data("kendoGrid");
        grid.dataSource.read();
    }
    Regards,
    Daniel
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  15. Stephen
    Stephen avatar
    158 posts
    Member since:
    Jan 2009

    Posted 25 Oct 2013 Link to this post

    Thanks!  Very strange that on the server binding grid I have the width set to 50 and that is wide enough but in ajax binding it needs to be wider...

    I got the grid updating as well.  Thank you!
Back to Top