Category: MVC

The model item passed into the dictionary is of type ‘[]’ , but this dictionary requires a model item of type ‘[]’

 

I am tired of how many times I found this question on forums. So , if  you encounter this error

The model item passed into the dictionary is of type ‘xxx’, but this dictionary requires a model item of type ‘yyy’

with the variation

The model item passed into the dictionary is of type ‘xxx’, but this dictionary requires a model item of type ‘System.Collections.Generic.IEnumerable`1[xxx]’

then read on.

ASP.NET MVC is plain and simple. The user enters an url in the browser. A Controller class is made from this request and an Action answer to the url request . The Action   gathers a Model from business logic and gives the Model to the View. The View has a Model also and generates HTML from the Model  .

 

Now let’s get to the code .

The action looks like

public ActionResult MyAction(parameters){

MyModelClassNameFromTheAction modelAction =   // gathers the Model –

return View(modelAction)

}

The View looks like:

@model MyModelClassNameFromTheView

….( html from the MyModelFromTheView )

 

If the 2 class names ( including namespaces) are not the same( and   MyModelClassNameFromTheAction  does not inherit from  , then the error occurs. Practically , MVC is saying : How can I match the 2 classes ?

 

Solving

Usually, you change the Action.

1. You may have inadvertently return the wrong view from the Action. Solution: return View(“~/<folders>/another razor.cshtml”) .

2.  You may have inadvertently return the wrong model . Solution :Solution: return View(another model.) . E.g. the View has as Model  IeNumerable<Model> and in the action you

return Model. The solution is changing

return View(Model)

to

return View( new[](Model));

MVC 5 encrypt parameters–SEO

 

I have added SEO ( ok, a bullshit example of SEO ) to http://msprogrammer.serviciipeweb.ro/2017/03/20/mvc-5-encrypt-parameters/ .

This is pretty simple :

 public class EncDecSEOBullShit : IEncryptDecrypt
    {
        //public static string FullName = typeof(EncDecSEOBullShit).AssemblyQualifiedName;
        const string seo = "this-is-seo-";
        public string DecryptString(string value)
        {
            return value.Replace(seo, "");
        }

        public string EncryptString(string value)
        {
            return seo + value;
        }
    }

 

the action is

<a href=’@Url.ActionEnc(new EncDecSEOBullShit(), “TestEncryptSEO”, new {id=7, a = 1, b = “asd” })’>Test</a>

It generates url such as:

http://mvc5encrypt.apphb.com/Home/TestEncryptSEO/7?a=this-is-seo-1&b=this-is-seo-asd

The Action Filter to decrypt takes the full type name ( with assembly name) of the class that does SEO ( or encrypt / decrypt)

[MVCDecryptFilter(EncDecFullClassName = "MVCEncryptDemo.EncDecSEOBullShit, MVCEncryptDemo")]

Example at

http://mvc5encrypt.apphb.com/

MVC 5 encrypt parameters

 

This is an old idea that I was having. The problem was that , for many people, showing the parameters in MVC is not something that they want . For example. suppose that we have this action

 

public ActionResult TestEncrypt(int id, int a, string b)

 

The this action can be activated by putting in the Razor cshtml file this

 

<a href=’@Url.Action("TestEncrypt", new { id=7, a = 1, b = "asd" })’>Test</a>

 

that will generate this URL :

http://mvc5encrypt.apphb.com/Home/TestEncrypt/7?a=1&b=asd .

The parameters are passed in clear text ( a is 1 and b is asd). What if I want to encrypt those into the URL ,

http://mvc5encrypt.apphb.com/Home/TestEncrypt/7?a=EAAAAHT3XGpsOow%2f2Wto%2fho1C3Bmy1kTFnBosorsrt9X3Eqj&b=EAAAADbjm%2bS8NDAKqznGI%2bzF02oOAY9wf24SFyFxPxbCu0ea

 

but receive in the Action the default values ( 1 and asd)?

Enter MVC5Encrypt

What you have to do is modify slightly the code in the .cshtml and add an attribute to the Action

 

<a href=’@Url.ActionEnc("mySecret", "TestEncrypt", new { id=7, a = 1, b = "asd" })’>Test</a>

 

and the action will be

[MVCDecryptFilter(secret = &quot;mySecret&quot;)]   
public ActionResult TestEncrypt(int id, int a, string b)

 

You can see into action at

http://mvc5encrypt.apphb.com/Home/TestEncrypt/7?a=EAAAAHT3XGpsOow%2f2Wto%2fho1C3Bmy1kTFnBosorsrt9X3Eqj&b=EAAAADbjm%2bS8NDAKqznGI%2bzF02oOAY9wf24SFyFxPxbCu0ea

 

FAQ:

1.  What is “mysecret”?

See code on http://stackoverflow.com/questions/202011/encrypt-and-decrypt-a-string that I shameless copied.

 

2. What about backward compatibility ( i.e., old links will function ) ?

Yes if you do not already encode in base64 ( default class encrypter knows if the parameter value is in base64 ) . See

http://mvc5encrypt.apphb.com/Home/TestEncrypt/7?a=1&b=asd 

 

3. What about extending this with a custom encrypt class ?

You can – see this function

 

public static string ActionEnc(this UrlHelper helper, IEncryptDecrypt encDec, string actionName, object routeValues)

 

4. What about extending this to route parameters ( e.g. http://localhost/Person/Edit/5  – the 5 parameter is in the route and not encrypted ) ?

Glad you ask. Please fill an feature request on github

5. More details ? 

Sources on GitHub : https://github.com/ignatandrei/MVC5Encrypt

Demo at http://mvc5encrypt.apphb.com/ 

NuGet at https://www.nuget.org/packages/MVC5Encrypt/

( Other solution is to use http://madskristensen.net/post/httpmodule-for-query-string-encryption)

Five common mistakes for ASP.NET MVC accesing resources : css, js, images, ajax

This is a Razor /MVC5 variant of older post
http://msprogrammer.serviciipeweb.ro/2010/10/09/five-common-mistakes-for-asp-net-mvc-accesing-resources-css-js-images-ajax/

To have once for all the link to show to people, because too much makes the same error again and again. (From here – you can use ResolveUrl or Url.Content – it’s the same for me. I use ResolveUrl because I used first …)

Case 1 The image does not display

Please check you have the following :

 <img src="@Url.Content("~/Content/images/YOURIMAGE.jpg" )" alt="image" style="border:0" /> or 
 
<img src="~/Content/images/YOURIMAGE.jpg" alt="image" style="border:0" /> 

Case 2 The css does not show

Please check you have the following :

 @string.Format("<link href="{0}" type="text/css" rel="stylesheet" />", Url.Content("~/Content/your.css"))  

or

 <style type="text/css">
@import '@Url.Content("~/Content/your.css")';

</style>  
  <style type="text/css">
<link href='~/Content/your.css' type='text/css' rel='stylesheet' />

</style>  

Case 3 The js does not execute (undefined error)

3.1 Please check you have the following :

 <script type="text/javascript" src="@Url.Content("~/Scripts/yourjs.js")"></script> 

or

 <script type="text/javascript" src="~/Scripts/yourjs.js"></script> 

This should be either in _Layout.cshtml, or in the scripts section

 @section scripts {   } 

in your .cshtml file.
3.2 please check in browser console ( press F12 ) for any javascript error that code may have

Case 4 The js does execute in .cshtml page, but did not execute under js file

Please check you DO NOT have the following @something in the js file. The js file is not interpreted by the same engine as cshtml, so can not interpret asp.net tags. Instead , add a parameter to your function for each @ variable that you have. Simple example : Let’s say in cshtml you have :

 <script type=”text/javascript”>

function Generate(){

window.open('@Url.Action(“About”)’);
}

</script> 

and you call Generate() from .cshtml file. Now you want the same in a .js file. The function Generate will have a parameter instead of Razor variable

 function Generate(url){  window.open(url); } 

and call like this from .cshtml file:

 Generate('@Url.Action("About")'); 

Case 5 The ajax request gives you a 404 error.

Please ensure that you have

 @Url.Content("~/path”) 

and not

 /path 

when you call the Ajax URL

Bonus 1: T4MVC , http://mvccontrib.codeplex.com/releases

Bonus 2: Edit the project file and put <MvcBuildViews>true</MvcBuildViews>

( short url: http://bit.ly/asp5Mistakes)

Swashbuckle.MVC

 

The Swashbuckle package for documenting WebAPI is really awesome. I have used for documenting Exporter API for Word/Excel/PDF/ODS/ODT.

One problem though: It does not respect the layout of your site.

For example, if the original header is this

 

when  I click on API test(swagger)

 

. This is not what I want – since I want the same layout.

Enter NuGet package https://www.nuget.org/packages/Swashbuckle.MVC/ . What it does is

1. add a controller(SwashbuckleMVCController), and Index action inside the controller.

2. add a View(SwashbuckleMVC/Index.cshtml) to your application

3. add a module to your application . The module intercept /swagger/ui/index call and modify with a Filter the send data ( practically, it adds to the beginning and the end of the stream the bytes from the Index action that I mentioned at 1)

Cons: It adds another <html> inside the main <html> . I made an issue on Swashbuckle to mention that https://github.com/domaindrivendev/Swashbuckle/issues/658

 

The code is free on https://github.com/ignatandrei/Swashbuckle.MVC .

You can test it on http://exporter.azurewebsites.net/swagger/ui/index

ASP.NET MVC–find error

I am moderator and a frequent answerer at forums.asp.net . Mostly of time the error is in thinking that database connection errors belong to MVC – or other things that are part of the .NET framework, not of MVC . However , I must write here the most confusing problem  that I have encountered:

<start quoting>

“I have an XML file, whose contents I want to display on the browser window using ASP drop down list and LINQ to XML. I am using Visual Studio MVC 4 for this purpose. In the model folder I have created a file to access the XML file, whose contents are :

[… not important code]

In the view folder, I have a asp file as shown

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="MvcApplication7.Controllers.QueryController" CodeBehind="~/Controllers/QueryController.cs" %>


<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <div id="updatePanel">
        <asp:DropDownList runat="server" ID="fragment">
        </asp:DropDownList>

    </div>

</asp:Content>
 

And finally in the Controller folder, I have

 namespace MvcApplication7.Controllers
    {
        public class QueryController : System.Web.UI.Page
        {
            //
            // GET: /Query/

        protected void Page_Load(object sender, EventArgs e)
        {
                fragment.DataSource = MvcApplication7.Models.fragmentNameModels.GetFragmentName();
                fragment.DataTextField = "id";
                fragment.DataValueField = "id";
                fragment.DataBind();
        }
 }
}
 

The ID given to the drop down list is "fragment", but when i use it my controller file I get the following error message :

Error 1 The name 'fragment' does not exist in the current context “

 

<end quoting>

Could you spot the error?

Then re-read the post and found the confusing things that will make assumption that is MVC ( including original poster words, comments and code)

 

( And , if not, the answer is here http://forums.asp.net/t/1974367.aspx?The+name+________+does+not+exist+error+Advice+needed+ASAP+ )

 

 

 

Minimize Json

TL;DR:

Minimize Json Answer by minimizing the names of the properties transmitted. As a practical example, see http://jsonminimize.apphb.com/ ( although is gzipped , the answers are 3862 versus 3089 bytes – an 20% gain . Your results will vary – because of how I obtain the random obtaining of data )

 

Prerequisites:

Ajax / Json   – http://msprogrammer.serviciipeweb.ro/2011/12/05/jquery-ajax-request-and-mvcdetailed/

AutoMapper – for automating transformation on server code of an object to another : http://automapper.org/

Mandatory for source code: Knockout – just because I needed a templating engine for javascript

 

Introduction

 

Let’s say you have an application that shows a report of something( for my example, departments and employees). Let’s say that you have optimized database for speed , the network connection, the data you load, everything.

However, because the end user wants to retrieve entire data, the page loads in 3 seconds. And this is not acceptable.

Looking to the source, you see that thepage makes one Json request that retrieves all data. You can not minimize this data. You tell the user that you can make several JSon request – but he will experience some delays. The user does not want to do this.

You look deeper into JSon answer and see that response from JSon:

{"ok":true,"departments":[{"IdDepartment":0,"NameDepartment":"Department 0","Employees":[{"FirstName":"Andrei","LastName":"Ignat","Login":"Andrei.Ignat","DateOfBirth":"\/Date(-62135596800000)\/","ManagerName":"Knowledge"},{"FirstName":"0 JkerzEZgv","LastName":"0 DjYFekyLF","Login":"0 JkerzEZgv.0 DjYFekyLF","DateOfBirth":"\/Date(24019200000)\/","ManagerName":"manager 0"},{"FirstName":"0 SlBKASBb","LastName":"0 rYGQXWQ","Login":"0 SlBKASBb.0 rYGQXWQ","DateOfBirth":"\/Date(21686400000)\/","ManagerName":"manager 1"}]},{"IdDepartment":1,"NameDepartment":"Department 1","Employees":[{"FirstName":"1 JkerzEZgv","LastName":"1 DjYFekyLF","Login":"1 JkerzEZgv.1 DjYFekyLF","DateOfBirth":"\/Date(24019200000)\/","ManagerName":"manager 0"},{"FirstName":"1 SlBKASBb","LastName":"1 rYGQXWQ","Login":"1 SlBKASBb.1 rYGQXWQ","DateOfBirth":"\/Date(21686400000)\/","ManagerName":"manager 1"}]},{"

What if , instead of “IDDepartment” the server transmits just “I” ? And if , instead of “NameDepartment” just “N” ? The response will be minimized! However, you do not want to affect the page source, so this article is about.

 

Original Application

The original application is displaying the departments and theirs employees. It is made with Knockout for templating and have this code ( oversimplified for convenience) :

$.ajax({
//code
            success: function (returndata) {
                    if (returndata.ok) {
                        window.alert("Departments transferred: " + returndata.departments.length);
                        ko.applyBindings(returndata);
//code

and the knockout templating engine will interpret the templating

<div data-bind=”foreach: departments”>
<h3 data-bind=”text: NameDepartment”><h3>

The steps:

1. Create 2 new classes , DSmall  and ESmall with properties names smaller.

2.Map the properties with a Dictionary from Department to DSmall  and from Employee to ESmall

3. Map the list of Department to list of DSmall and list of Employees to ESmall (using AutoMapper and the dictionaries from 2)

4. Verify with an console example

5. Transmit over JSon the DSmall and ESmall. 

6. In javascript recreate the Department and Employees from DSmall and ESmall.

 

7.Results

 

8.Homework

 

Create 2 new classes , DSmall  and ESmall with properties names smaller.

The Departmen have a property named IDDepartment. DSmall will have a property named I. And so on – see below:


public class Department{
        public Department()
        {
            Employees = new List<Employee>();
        }
        public int IdDepartment { get; set; }
        public string NameDepartment { get; set; }
        public List<Employee> Employees { get; set; }



    }
public class DSmall
    {
        public int I { get; set; }
        public string D { get; set; }

        public List<ESmall> E{ get; set; }
    }

public class Employee
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Login
        {
            get
            {
                return FirstName + "." + LastName;
            }
        }
        public DateTime DateOfBirth { get; set; }

        public string ManagerName { get; set; }


    }
public class ESmall
    {
        public string F { get; set; }
        public string L { get; set; }
        public string Lo { get; set; }
        public DateTime D{ get; set; }

        public string M{ get; set; }
    }

 

 

 

Map the properties with a Dictionary from Department to DSmall  and from Employee to ESmall

As I said,the Departmen have a property named IDDepartment. DSmall will have a property named I. And so on. So I came up with this for department:

  dDep = new Dictionary<string, string>();
            dDep.Add("I", "IdDepartment");
            dDep.Add("D", "NameDepartment");
            dDep.Add("E", "Employees");

and for converting Employees to ESmall

dEmp = new Dictionary<string, string>(); 
            dEmp.Add("F", "FirstName");
            dEmp.Add("L", "LastName");
            dEmp.Add("Lo", "Login");
            dEmp.Add("D", "DateOfBirth");
            dEmp.Add("M", "ManagerName");

The dictionaries will be good also for transmitting to javascript the names in order to not change the names of the properties in templating.

 

 

 

Map the list of Department to list of DSmall and list of Employees to ESmall (using AutoMapper and the dictionaries from 2)

This is the easiest line:

Utils<Employee, ESmall>.CreateMap(dEmp);
Utils<Department, DSmall>.CreateMap(dDep);

I invite you to read the code on utils class. It involves some lambda programming. I will not detail here - just grab the source from https://github.com/ignatandrei/MVCMinimizeJson/ 



<p>&#160;</p>  <p>&#160;</p>  <h3> Verify with an console example </h3>  <p>&#160;</p> 
I have made an console example to show the difference of serializing DSmall and Department. The code does not involve database - just loads some random data.
I have serialized with XMLSerializer and Javascript serializer 99 Department with random employees and then transforming to DSmall and ESmall


           #region xml
            var ser = new XmlSerializer(typeof(List<Department>));            
            var sb = new StringBuilder();
            using (var sw = new StringWriter(sb))
            {
                ser.Serialize(sw, d);
            }
 //           Console.WriteLine(sb.ToString());
            var nrXML = sb.Length;
            #endregion

            #region javascript
            var jss = new JavaScriptSerializer();
            int nrJss = jss.Serialize(d).Length;
            #endregion

For javascript the result is impressive: 30%. ( as I say, because of the random, your result may vary a little . Do your research)

image

Transmit over JSon the DSmall and ESmall. 

 

 

This was the easiest part. I just put the random data into appliaction( I know, I know... but it was easier). And I have created 2 actions

public JsonResult RequestEmployees()
        {
            //usually this will be loading from database
            //but to compare same data when minimizing, 
            //I have to put somewhere            
            List<Department> d = this.HttpContext.Application["data"] as List<Department>;
            return Json(new { ok = true, departments = d });
        }

        public JsonResult RequestEmployeesMinimizing()
        {
            //usually this will be loading from database
            //but to compare same data when minimizing, 
            //I have to put somewhere                        
            List<Department> d = this.HttpContext.Application["data"] as List<Department>;
            var min = LoadData.Minimize(d);
            return Json(new { ok = true, departmentsMin = min, LoadData.dDep, LoadData.dEmp }); 

        }

As you see, when transmitting the minimizing version of data, we will send also the dDep ( dictionary department) and dEmp( dictionary employees) of mapped properties to the View - in order to re-instantiate the original Employee and Department from javascript

 

In javascript recreate the Department and Employees from DSmall and ESmall.

 

 

 

We must do this in order to not modify the template that we already have.So I have created a simple mapping function in Javascript:

 function createObjectMapped(minObject, dictProp) {
            var obj = new Object();
            
            for (var prop in dictProp) {

                obj[dictProp[prop]] = minObject[prop];

            }
            return obj;
        }

and use like this:

returndata.departments = [];
                        $.each(returndata.departmentsMin,function(index, departmentMin)
                        {
                            var dep = createObjectMapped(departmentMin, returndata.dDep);
                            //mapping employees of the department
                            dep.Employees = [];
                            $.each(departmentMin.E, function (index, employeeMin) {
                                dep.Employees.push(createObjectMapped(employeeMin, returndata.dEmp));
                            });
                            returndata.departments.push(dep);

                        });

7.Results

In Fiddler, for 99 departments with random employees, the original data is 77.765 bytes . The minimized data is 55.796 bytes. My gaining was 28%

8.Homework

Do the same when sending json bulk data - see http://msprogrammer.serviciipeweb.ro/2014/01/05/javascript-mvvm-and-asp-net-mvc/

Source code at https://github.com/ignatandrei/MVCMinimizeJson/
View online at http://jsonminimize.apphb.com/

The original problem and idea was coming from Adrian Petcu Thank you !

Javascript MVVM and ASP.NET MVC

TL;DR;

The purpose of this article is to show is how to transmit data to edit( create, update, delete) from a MVVM array to an ASP.NET MVC action in order for the action to bind to an IEnumerable/Array/List of objects. We will make also a javascript function that can be re-use across multiple MVVM frameworks to transmit data for multiple objects at once.

As always, you can find source code at https://github.com/ignatandrei/JavaScriptAndMVVMandMVC/ and you can view online at http://mvvmjavascriptmvc.apphb.com/

 

If you know already that, the last item on this rather long post is a homework. Download code and do it Winking smile

 

 

Prerequisites:

  1. If you want to know about ajax, please see the same example of how to save employee one by one http://msprogrammer.serviciipeweb.ro/2011/12/05/jquery-ajax-request-and-mvcdetailed/.
  2. The reference of sending an array of objects to MVC is http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/ . Please read it first since we will compose the Http request in ASP.NET MVC manner.
  3. If you do not know about MVVM and data binding in javascript, please follow most comprehensive tutorial that I know, http://learn.knockoutjs.com/

Objects

We start with the Employee ( Id, Name and IdDepartment) and Department(Id, Name). We will make an interface to display and edit multiple employees. The user can choose to change the Name and pick a Department from a list of Departments (presented as a select / dropdown / combox) .Also, he can create new Employee or delete an existing Employee. Then the user can submit all changes at once. We will use for this MVVM from Knockout, but you can use any other MVVM framework.

We will have 2 modes: edit and display. For edit, we have as actions add, delete ,modify and send all modifications(along with validation) . I would list what it is mandatory for every mode and action.

For fast learners:
Display
Edit Mode –Modify existing data
Edit Mode –add new employee
Edit Mode –delete existing employee
Edit Mode –send modifications and client validation
Summary
Homework


Display:

clip_image002

As you see we need to display the Employee list with name and the Department name.

If I do not want to create a NameDepartment property on Employee, but just use DepartmentId then the Model of the View should return the list of names of Departments.

public class ListEmployeesViewModel

{

public employeeList AllEmployees { get; set; }

public departmentList DepartmentList{ get; private set; }

This will be serialized as javascript array in the Home view:

@{

var jss = new JavaScriptSerializer();

var arrDepartments = jss.Serialize(Model.DepartmentList);

var arrEmployees = jss.Serialize(Model.AllEmployees);

}
//This is in the script tag in javascript:
//existing departments

var arrDeps = @Html.Raw(arrDepartments) ;

//existing employees

var arrEmps = @Html.Raw(arrEmployees) ;


So we have in arrDeps the departments and in arrEmps the existing employees.

To display the list of employees we create a MVVM javascript model( we have here knockout style, but is similar with other javascript MVVM framework)

First we create an employee in javascript ( explanations will follow) :

//this is in javascript
var emp = function(empId,name,deptId,active){

var self = this;

self.nr = i++; 

self.IdEmployee = ko.observable(empId);

self.NameEmployee = ko.observable(name);

self.Active = ko.observable(active);

self.iddepartament = ko.observable(deptId);

self.editMode = ko.observable(false);

self.displayMode= ko.observable(true);

self.deptName= function(){

var id=self.iddepartament();

var name ="";

$.each(arrDeps,function(index,value){

if(value.IdDepartment == id){

name=value.NameDepartment;

return false;

}

});

return name;

}

}

Explanation 1: the nr is the number of the employee . The user does not like ID, but wants to know how much employees are displayed.

Explanation 2: I have add an editMode and displayMode to the employee – to know the mode in which the employee is. I can have one property instead of 2(because of complementarity) , but was easier for me.

Explanation 3: In order to display DepartmentName it is enough to create a function on the employee to return the department name , iterating through the arrDeps – the code is

self.deptName= function(){

var id=self.iddepartament();

var name ="";

$.each(arrDeps,function(index,value){

if(value.IdDepartment == id){

name=value.NameDepartment;

return false;

}

});

return name;

}

Now we create the javascript model that holds all employees on the view:

var jsModel = function() {

var self = this;

self.employees = ko.observableArray([]);

And we make a function to add the existing employees to the array of employees:

self.addEmp = function(empId,name,deptId,active) { 

self.employees.push(new emp(empId,name,deptId,active)) 

};

And we add to the javascript MVVM model the existing employees

var model= new jsModel();

$.each(arrEmps,function(index,value){

model.addEmp(value.IdEmployee ,value.NameEmployee, value.iddepartament,value.Active);

});

And display it on the template by just binding :

ko.applyBindings(model);

that will repeat in

<tbody data-bind=’foreach: employees’>

<tr>

<td>

<span data-bind=’text: nr’ > </span>

</td>

<td>

<span data-bind=’text: NameEmployee,visible:displayMode’> </span>

<!—code removed for clarity–>

</td>

<td>

<span data-bind=’text: deptName(),visible:displayMode’> </span>

<!—code removed for clarity–>

</td>

<td>

<span data-bind=’text: Active,visible:displayMode’> </span>

<!—code removed for clarity–>

</td>

<td><!—code removed for clarity–>

</td>

</tr>

So this was the display mode. Pretty simple, huh ?

Edit Mode –Modify existing data

The Edit button calls the javascript model edit(true):

self.edit = function(val){

var arr =self.employees();

$.each(arr ,function(index,value){

value.editMode(val);

value.displayMode(!val);

});

So I put editMode to true and displayMode to false

Now, back to the template:

<td>

<span data-bind=’text: NameEmployee,visible:displayMode’> </span>

<input data-bind=’value: NameEmployee,visible:editMode’ />

</td>

You can see here visible attribute binded on displayModel and editMode –and how the span or the input are visible either way.

clip_image004

Same for the checkbox and the select. Because of the MVVM (in this case, knockout) any changes on the data ( name, active, changing department) will be binded back to the array of employees in the javascript MVVM model

Edit Mode –add new employee

When you press add a new employee is generated – the number 3:

clip_image006

It is easy – the Add button calls the same function addEmp that we use to push existing employees:

function BeginAdd(){

model.addEmp(0,'',0,true);

model.edit(true);

}

So a new employee is added to the array of employees – and the employees table is displaying the added employee . Do you like MVVM now ? 😉

Edit Mode –delete existing employee

The delete button have this code:

<td><button data-bind="click: $root.removeEmp,visible:editMode">Delete</button></td>

It is clear visible just when editMode is true. And the removeEmp removes the current employee from array, but having the id ( if not 0 – means new) put into a string that contains the ids of deletedEmployeesa:

 self.removeEmp = function(emp) { 

var id=emp.IdEmployee(); 

if(id != 0)

self.deletedItemsId += id;

self.employees.remove(emp); 

};

And the employees table is removing the row for the employee . Do you like MVVM now ? 😉

Edit Mode –send modifications and client validation

On the server side, the parameters of the action that receives the data is simple:

[HttpPost]

[HttpPost]

public JsonResult SaveEmployees(ListEmployeesViewModel e,string deletedItems)

I will explain the 2 arguments. For the first, remember ListEmployeesViewModel from the beginning ? It contains the list of employees and we will post that:

public class ListEmployeesViewModel

{

public employeeList AllEmployees { get; set; }

The second argument is the string that contains the id’s of deletedEmployees.

Now to transmit those from javascript array of employees.

Being a programmer, I like code-reuse. So why not create a function that iterates through an array( of employees), get all properties ( eliminating non-relevant, such as nr, editMode, displayModel and others) and compose the data in the MVC style(http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/ )

First, we need reflection in javascript:


//this is generic and can be put in a different .js file

var refProps= function (obj, exclude , recognizeFunction) {

var properties = [];

for (var prop in obj) {

//you can define an exclude function to exclude added properties

if(exclude){

if(exclude(prop))

continue;

}

var excludeProp = true;

var t = (typeof obj[prop]).toLowerCase();

if (t == 'number' || t == 'string' || t == 'boolean') {

excludeProp=false;

}

if(excludeProp){

//special case :maybe it is a observable function 

if(recognizeFunction){

if(t == 'function' ){

if(recognizeFunction(t))

excludeProp=false;

}

};

}

if(!excludeProp)

properties.push(prop);

};

return properties;

}

Fast explanation of parameters:

  1. obj is the object in javascript that I need all properties that are number, string, or boolean.
  2. I need to remove some properties(nr,editMode, displayMode) that are relevant in javascript – but not on the server side – so I have put an exclude function( you can put null)
    function exclude(prop){
    
    switch(prop){
    
    case "nr":
    
    return true;
    
    case "deptName":
    
    return true;
    
    case "editMode":
    
    return true;
    
    case "displayMode":
    
    return true;
    
    default:
    
    return false;
    
    }
    
    
  3. Also, knockout make a special ko.observable function
    self.NameEmployee = ko.observable(name);
    

    – so I need to recognize those functions – and the function is(surprise!) ko.observable

Now saving the array is again re-usable:

function saveArray(itemsArray,/*you can not pass those*/prefix, excludeProp,recognizeFunction,validateProp){

var l = itemsArray.length ;

if(l == 0){

return "";

}

var propNames = refProps(itemsArray[0],excludeProp,recognizeFunction); // you can pass null on exclude to add all properties

var nr = 0 ;

var strData="";

for (var i = 0; i < l; i++) {

var objToSave= itemsArray[i]; 

//you can pass here another function to recognize which object can be saved

//if(!canBeSaved(objSave) continue;

for (var j = 0; j < propNames.length; j++) {

var nameProp = propNames[j];

var val =objToSave[nameProp] ;

var t = (typeof val).toLowerCase();

if(t == 'function'){

val = objToSave[nameProp]();

}

if (validateProp) {

if (!validateProp(nameProp,val, objToSave, i)) {

return "";

}

}

strData += "&"+ prefix+"[" + nr + "]." + nameProp;

strData += "=" + val;

}

nr++;

}

return strData;

}


The itemsArray parameter is the items array that you want to save ( in my case, the employees).

The new function is validateProp – you can pass null – but this is an implementation that take into consideration that the employee should not have the name empty and the user must select something from the department list:


function validateProperty(propName, value, item, number){

switch(propName){

case "NameEmployee":

if(value == ""){

window.alert("please enter employee name for row number " + (number+1) );

return false;

}

return true;

case "iddepartament":

if(value === undefined || value == 0){

window.alert("please select a department for row number " + (number+1) );

return false;

}

return true;

default:

return true;

}

}



After those 2 re-usable functions( saveArray and refProps ) the code for save is pretty simple:

We obtain the values for employees using saveArray

self.save = function() { 

var itemsArray = self.employees();

var strData = saveArray(itemsArray,"AllEmployees",exclude, ko.observable,validateProperty);

if(strData == "")

{

//window.alert("no save");

return;

}

strData="deletedItems=" +self.deletedItemsId + strData;

window.alert("saving:" + strData);

and post to the server.


$.ajax({

type:"POST",

url:'@Url.Content("~/Home/SaveEmployees")' ,

data: strData ,

datatype:"JSON",

//Just we must be attentive: if success , then the id of the new employees( those with id’s 0) must be replaced by the id’s of the id’s generated on server.
//How I identify if multiple new employees? Well, it is easier to delete all employees with id’s 0 and add the id’s that are not already in the array:

var dels=[];

//delete the new items and add the new ones

var idExisting=';';

$.each(self.employees(), function(index,emp) {

if(emp.IdEmployee() == 0 )

dels.push(emp);

else

idExisting += emp.IdEmployee()+";";

});

$.each(dels,function(index,emp) {

self.removeEmp(emp);

});

//add new one

window.alert('add new ones');

$.each(returndata.emps,function(index,emp) {

var id=emp.IdEmployee;

if(idExisting.indexOf(";" + id+";") == -1){// not found - means it is a new one

self.addEmp(emp.IdEmployee ,emp.NameEmployee, emp.iddepartament,emp.Active);

}

});


Also, for Antiforgery token I have used this code

 //added antiforgery token
                var aft= $('input[name="__RequestVerificationToken"]');
                if(aft.length){
                    strData +="&__RequestVerificationToken=" + aft.val();
                }

Well, that was it !

Summary:


We have had an javascript array of employees to edit and send data at once . We have make the POST as for ASP.NET MVC rules. You can re-use the refProps javascript function( that gives you the name of the properties of an object – in our case, employee) and saveArray javascript function – that serialize an javascript array to a recognizable ASP.NET MVC idiom

As always, you can find source code at https://github.com/ignatandrei/JavaScriptAndMVVMandMVC/ and you can view online at http://mvvmjavascriptmvc.apphb.com/

 

Homework for you:

( fork on github  and send me the solution via github)


1. Modify the nr ( the employee order number) such as , when deleting or adding a new employee, the numbers are in good order – not 1 and 3 like in the picture

clip_image008

2. Add a hiredate to the employee . Ensure you transmit the date(Hint: Modify the refProps )

LaterEdit:
Hintea Dan Alexandru made a simple application to show me that a simple json.stringify it is enough for MVC to do this magic.
More , it shows directly in the MVVM model a toDTO that is simplier to use( however, the validation part remains to do)
Source code at https://github.com/hinteadan/MvcAjaxSample/#!

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.