c# - DropdownListFor on Change updates and posts parent object -


man..and thought project nice , easy....

here's problem:

i'm making checklist project manager using mvc 4, , have editor template each task item. within each task item, have dropdownlistfor user can use select task state ('complete', 'inprogress', etc.)

when user changes task state, script goes , adds completion date (if changed completed state). want update , save task changes in database using httppost method, "updatetaskstate".

as side question, correct , proper way acheive goal? i'd love have don't need refresh tasks view every time change made too..

my task editor template:

@model models.task  @html.hiddenfor(model => model.task_id, new { @id = "taskid" }) @html.hiddenfor(model => model.task_name) @html.hiddenfor(model => model.task_desc) @html.hiddenfor(model => model.user_completed,new {@id = "usercompleted" }) @html.hiddenfor(model => model.completion_date, new { @id = "completiondate" })  <table style="width:80%">     <tr style="width:60%">         <th colspan="3">@html.displayfor(model => model.task_name)</th>         <th align="left">         @html.dropdownlistfor(model => model.task_state_id,     new selectlist((system.collections.ienumerable)viewdata["taskstates"], "task_state_id", "state"),              new { @id = "ddlstate" })     </th>         <td>             @html.editorfor(model => model.notes, new { htmlattributes = new { @class = "form-control" } })             @html.validationmessagefor(model => model.notes, "", new { @class = "text-danger" })         </td>         </tr>     <tr>         <td colspan="3">@html.displayfor(model => model.task_desc)</td>         <td>@html.label("completed ")@html.displayfor(model => model.user_completed)@html.label(", ")@html.displayfor(model => model.completion_date)</td>     </tr> </table> 

my main view script:

@model nscengineering.models.nriandcategoriesviewmodel @{ viewbag.title = "details";}  <h2>details</h2> @using (html.beginform()) { @html.antiforgerytoken()     <div class="form-group">     <div class="col-md-offset-2 col-md-10">         <input type="submit" value="save" class="btn btn-default" />     </div> </div>  <div class="form-horizontal">     <hr />     @html.hiddenfor(item => model.taskstates)     @html.hiddenfor(item => model.id, new{ @id ="nriid" })     <div class="nrisummary">     @html.labelfor(item => model.summary )     @html.displayfor(item => model.summary, "_nri")         </div>     <hr />     <div class="tasks">       @html.labelfor(item => model.tasks )         @for (int = 0; < model.tasks.count(); i++ )         {             @html.editorfor(item => model.tasks[i], "_task", new{@id = "taskitem"})         }         </div>     </div> }  <p>     @html.actionlink("back list", "index") </p>  @section scripts{  <script type="text/javascript">     $(this.document).ready(function () {     $('#ddlstate').change(function () //wire on change event of 'country' dropdownlist     {         var selection = $('#ddlstate').val(); //get selection made in dropdownlist         if (selection == '4') {             $('#completiondate').val('@datetime.now.date');         }         var completion = $('#completiondate').val();         alert(completion);         alert($('#taskid').val());         var url = '@url.action("updatetaskstate", "nris")';         $.ajax({             url: url,             type: 'post',             data: $('#taskitem').serializearray(),             contenttype: "application/json; charset=utf-8",             success: function (e) {                 $("#message").html("success");             },             error: function (xhr, status, error) {                 // show error                 $('#message').html(xhr.responsetext);             }         })      }) }); 

}

update1

@model nscengineering.models.nriandcategoriesviewmodel  @using (html.beginform()) {     @html.antiforgerytoken()     <div class="form-group">         <div class="col-md-offset-2 col-md-10">             <input type="submit" value="save" class="btn btn-default" />             <p>                 @html.actionlink("back list", "index")             </p>         </div>     </div>     @html.labelfor(item => model.nrisummary)     @html.displayfor(item => model.nrisummary, "_nri")  //just displays project summary details     <div class="form-group">                 @for (int = 0; < model.categories.count; i++)         {             <div style="border:solid ; border-width:1px">                <table class="category" style="width:100%">                    <thead style="font-size:large ; background-color:black">                       @html.displayfor(c => c.categories[i].category_name)                    </thead>                    <tbody>                        <tr>                            @rendertaskscontrol(model.categories[i].tasks, model.statelist)                        </tr>                                                   @for (int k = 0; k < model.categories[i].subcategories.count; k++)                        {                            <tr>                                <td>                                    <table>                                        <thead style="font-size:larger">                                            @html.displayfor(c => c.categories[i].subcategories[k].category_name)                                            @rendercategorypercentage(model.categories[i].subcategories[k].tasks)                                        </thead>                                        <tbody>                                            @rendertaskscontrol(model.categories[i].subcategories[k].tasks, model.statelist)                                        </tbody>                                    </table>                                </td>                             </tr>                                                   }                    </tbody>                </table>                 </div>         }         </div> }   @helper rendertaskscontrol(ilist<nscengineering.models.task> tasklist, selectlist states) {     (int = 0; < tasklist.count; i++) {      <div class="task">                 @html.displayfor(model => tasklist[i].task_name)                    @html.dropdownlistfor(model => tasklist[i].task_state_id, states, new { @class = "ddlstate" })                 @html.hiddenfor(model => tasklist[i].task_id)                 @html.hiddenfor(model => tasklist[i].nri_id)                 @html.displayfor(model => tasklist[i].completion_date, new { @class = "date" })                 @html.hiddenfor(model => tasklist[i].category_id)                  @*@html.editorfor(model => task.notes)                 @html.validationmessagefor(model => task.notes, "", new { @class = "text-danger" })*@             @html.displayfor(model => tasklist[i].task_desc)         @html.displayfor(model => tasklist[i].user_completed)  </div> } }  @helper rendercategorypercentage(ilist<nscengineering.models.task> tasklist) {     int sum = 0;    int total = 0;    var percentage = "";      foreach (nscengineering.models.task task in tasklist)     {         if (task.task_state_id != -1) {               sum += task.task_state_id;         }          total += 3;              }      if (total != 0){          var ratio = ((double)sum / total);          percentage = string.format("{0:0.0%}", ratio);          }      else {           percentage = "invalid value";      }     <text> @sum + @total </text>      <br />     @percentage };    @section scripts{  <script type="text/javascript">  //this me trying completiondate programatically update     $(this.document).ready(function () {         $('.ddlstate').change(function () {             if ($('.ddlstate').val() == 3) {                 date = '@datetime.now.date';                 var task = $(this).closest('.task');                 var completiondate = task.children('.date');                 task.children($('.date')).text(date);                 alert(date);                 alert(task.children($('.date')).text());             }             else {                 $('.completion_date').val(null);             }         //    location.reload(true);         })     }); </script> }  public partial class category {     public category()     {         tasks = new list<task>();         subcategories = new list<category>();     }      [key]     [databasegenerated(databasegeneratedoption.identity)]     public byte category_id { get; set; }      [required]     public string category_name { get; set; }     public byte? parent_category_id { get; set; }      [foreignkey("parent_category_id")]     public category parentcategory { get; set; }      [inverseproperty("parentcategory")]     public virtual ilist<category> subcategories { get; set; }      public virtual ilist<task> tasks { get; set; } } 

start creating view model represents want display/edit , avoid sending , receiving unnecessary data across wire

view models

public class taskviewmodel {   public int id { get; set; }   public string name { get; set; }   public int state { get; set; }   public string description { get; set; }   public string notes { get; set; } }  public class nriandcategoriesviewmodel {   public list<taskviewmodel> tasks { get; set; }   public selectlist statelist { get; set; }   // other properties of model display in main view } 

in method, initialize instance of nriandcategoriesviewmodel, map properties of tasks collection of taskviewmodel , assign statelist (create selectlist once rather passing collection via viewdata , constructing selectlist each task).

view

@model nscengineering.models.nriandcategoriesviewmodel @using (html.beginform()) {   ...   (int = 0; < model.tasks.count; i++)   {     <div class="task">       @html.hiddenfor(m => m.tasks[i].id, new { @class = "id" })       @html.displayfor(m => m.tasks[i].name)       @html.dropdownlistfor(m => m.tasks[i].state, model.statelist, new { @class = "state" })       @html.textareafor(m => m.tasks[i].notes, new { @class = "notes" })       @html.displayfor(m => m.tasks[i].description)     </div>   }   <input type="submit" value="save" /> } 

note use editortemplate taskviewmodel correct use is

@html.editorfor(m => m.tasks) 

do not use inside loop or specify templates name (doing have overriding default behavior , end duplicate name , id attributes)

this submit form controller

public actionresult updatetaskstate(nriandcategoriesviewmodel viewmodel) 

so can (1) check validation errors , return view if necessary, (2) data model database, (3) loop each taskviewmodel in viewmodel.tasks , update corresponding properties in data model including current date , userid, (4) save data model, , (5) redirect index view.

you seem overly concerned making many database calls. if using ef, tasks have been modified saved (not of them, net result same - in fact, worse if user selects wrong state , corrects since calls being made). , ironic since sending , posting whole lot of useless data doing having far worse affect database call. if want post 1 task @ time (and consequence lose benefits such client side unobtrusive validation) use following script

var url = '@url.action("updatetaskstate", "nris")'; $('.state').change(function() {   var state = $(this).val();   var task = $(this).closest('.task');   var id = task.children('.id').val();        var notes = task.children('.notes').val();   $.post(url, {id: id, state: state, notes: notes }, function(response) {     // response   }); }); 

and change controller

public actionresult updatetaskstate(taskviewmodel viewmodel) 

note if rendering controls in table row, need modify selectors suit table layout

but consider user perspective. user not expect data saved selecting item drop down list. if users selects item drop down list , modifies notes, notes never saved , user none wiser. poor ui design , confusing! stick standard submit allows user modify data, check it, , make conscious decision save it.


Comments

Popular posts from this blog

c++ - QTextObjectInterface with Qml TextEdit (QQuickTextEdit) -

javascript - angular ng-required radio button not toggling required off in firefox 33, OK in chrome -

xcode - Swift Playground - Files are not readable -