loads of useful information, examples and tutorials pertaining to web development utilizing asp.net, c#, vb, css, xhtml, javascript, sql, xml, ajax and everything else...

 



Advertise Here
C-Sharpener.com - Programming is Easy!  Learn Asp.Net & C# in just days, Guaranteed!

Inline AJAX DropDown and Text Editing with Asp.Net MVC and jQuery

by naspinski 7/12/2010 5:31:00 AM

including how to use a database to populate the dropdown

First thing is first, you will need to download jQuery and the Jeditable plugin (I prefer to refer to it as the Jedi-Table!). Be sure to put these references in your View (or Masterpage). Next, you have to set up a view on which to use an inline edit. I find that I often want to use this approach on tables of information. For this View, I will set it to use an IEnumerable of an Item I have called 'ItemOwner' (this is arbitrary and does not really matter). It will be a simple table that lists the Name and the Country of the owner, both of which will be editable inline. Here is the Index in my ExampleController.cs:
myDataContext db = new myDataContext();
public ActionResult Index()
{
    // get the info for the 'Countries' dropdown:
    ViewData["countries"] = db.Countries
        .Select(x => new SelectListItem() 
        { 
            Text = x.Name, 
            Value = x.Id.ToString() 
        }).ToJson();

    // get the 'ItemOwners' I am interested in:
    var owners = db.ItemOwners.Take(3);

    return View(owners);
}

As you can see there, I am also pulling the countries from the database and throwing them into the ViewState - we will get to this later. Since the Country is actually a foreign key relation, the value is set to an integer which is the identity field in the database. It is also using a .ToJson() extension which takes a IEnumerable<SelectListItem> and puts it into a simple JSON string that I use which is here:
public static string 
    ToJson(this IEnumerable<SelectListItem> slis)
{
    string output = "{";
    if (slis != null)
    {
        for (int i = 0; i < slis.Count(); i++)
        {    
            output += " '" + slis.Skip(i)
            .First().Value + "': '" + 
            slis.Skip(i).First().Text + "'" + 
            (i == slis.Count() - 1 ? " " : ",");
        }
    }
    return output += "}";
}

There is probably a better way to do that... but I don't know it?!

I am also pulling 3 ItemOwners from the database, I know this is silly, but it just an example. Here is how I am displaying them in the view:
<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Country</th>
        </tr>
    </thead>
    <tbody>
        <% foreach(var owner in Model) { %>
        <tr>
            <td><%= owner.Name %></td>
            <td><%= owner.Country.Abbreviation %></td>
        </tr>
        <% } %>
    </tbody>
</table>

Now that there is a simple table we want to make it a bit more interactive. Since we aregoing to make all of these fields editable, we need to add in a way to distinguish exactly what they are. To do that, we will need two things: the id of the item they are editing, and the type of inline editing we will be doing (i.e. dropdown or text input). So to do that, let's add in a few css classes and an identifieng ID:
<td id="name<%= owner.Id %>" class="editable text">
    <%= owner.Name %></td>
<td id="ctry<%= owner.Id %>" class="editable dropdown">
    <%= owner.Country.Abbreviation %></td>

And now add a little css to make them appear to be clickable:
td.editable:hover 
{ cursor:pointer; background-color:Orange; }

Now they all look like you can click on them, so we can move on to making the click actually do something.

This is where the jQuery comes in, and it is very simple. I have made these 'helper' methods in Javascript to make all of my inline calls centrally controllable, I keep this in my sites script folder so if I change one inline edit, I change them all; it also makes for more readable Javascript on each page.
function InlineDropdown(collectionToDropDown, ajaxAddress, dropDownDataSet) {
    collectionToDropDown.editable(ajaxAddress,
    {
        data: dropDownDataSet,
        type: 'select',
        indicator: 'saving...',
        tooltip: 'click to edit...',
        submit: 'Save',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextbox(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress, 
    {
        indicator: 'saving...',
        tooltip: 'click to edit...',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextarea(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress, 
    {
        type        : 'textarea',
        rows        : 4,
        indicator   : 'saving...',
        tooltip     : 'click to edit...',
        style       : 'inherit',
        submit      : 'Save',
        onblur      : 'ignore',
        placeholder : 'click to edit'
    });
}

Obviously you can read all about the options on the Jeditable page, but this is how I set them. Also notice I have a InineTextarea included as well for a textarea which is not covered here but works the exact same.

Now the jQuery calls are almost trivial:
InlineTextbox(
    $('td.editable.text'), 
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>"
);

InlineDropdown(
    $('td.editable.dropdown'), 
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>", 
    <%= ViewData["countries"].ToString() %>
);

What that is doing is sending the POST requests to the specified address. The POST contains a few things:
  • id - the id of the element that sent the request
  • value - the new value passed by the element
We are also passing more information there - remember that we passed both the type of field to edit and the id of the ItemOwner to edit, ie [name837] which emans we want to edit the Name field of ItemOwner 837. So we simply set up an ashx handler (which we specified above) to do the dirty work:
public void ProcessRequest(HttpContext context)
{
    string newValue;
    try
    {
        myDataContext db = new myDataContext();
        string elementId = context.Request.Form["id"];

        // since we made the first 4 of the id the 'field' whic to edit
        // we can just pull the first 4 letters for use in our switch:
        string fieldToEdit = elementId.Substring(0, 4);

        //now take anything after those 4 and it is the Id:
        int idToEdit = Convert.ToInt32(elementId.Remove(0, 4));

        // the value is simply a string:
        newValue = context.Request.Form["value"].Trim();

        // now that we have the id, get the ItemOwner from the db
        ItemOwner owner = db.ItemOwners.FirstOrDefault(x => x.Id == idToEdit);

        // after all is said and done, we will return newValue to the user so the field
        // looks as if the change has taken place (which it has)

        // using the field we pulled above, decide what to do:
        switch (fieldToEdit)
        {
            // name is easy
            case "name": owner.Name = newValue; break;

            // since the country is an integer foreign key, we need to Convert.ToInt32:
            case "ctry":
                owner.CountryId = Convert.ToInt32(newValue);
                // now that we have recorded the value, we want to return the text to
                // the user and not the id value which would make no sense
                newValue = db.Countries.FirstOrDefault(x => x.Id == owner.CountryId).Abbreviation;
                break;
            // if it wasn't caught, something is wrong:
            default: throw new Exception("invalid fieldToEdit passed");
        }

        db.SubmitChanges(); // save it
    }
    // now if an exceptions were reported, the user can see what happened
    // this also inform the user nothing was saved
    // you could easily make this not reported to the user and logged elsewhere
    catch (Exception ex) 
    { newValue = "Error: " + ex.Message + " [nothing written to db]"; }

    //now return what you want in the element:
    context.Response.Write(newValue);       
}

And that is all it takes.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

ajax | asp.net | c# | css | javascript | jquery | mvc

Use jQuery to add all the values in a table column

by naspinski 5/8/2009 10:53:00 AM

simple way to get the total for a table

There are a lot of times when a table of data needs to be added up, or a summary provided; for Asp.Net users specifically, when you are using a dynamic GridView. Using jQuery, we can do this in just a few lines of code. If you don't care how it's done, skip to the bottom for a quick function to do the work for you. Here is a quick example:
nameageweightbenchpress
stan27177325
rj3013595
jose29230375

agesweightsbenchpresses
86542795

The first table is just a normal table holding the data, the second table is filled dynamically with jQuery adding up the columns. Yes, the numbers are real... RJ needs to hit the gym. Here is the jQuery to get these sums the manual way:
//these will hold the totals
var ages = 0;
var weights = 0;
var benchpresses = 0;

//reference the rows you want to add
//this will not include the header row
var rows = $("#data tr:gt(0)");
rows.children("td:nth-child(2)").each(function() {
    //each time we add the cell to the total
    ages += parseInt($(this).html());
});
rows.children("td:nth-child(3)").each(function() {
    weights += parseInt($(this).html());
});
rows.children("td:nth-child(4)").each(function() {
    benchpresses += parseInt($(this).html());
});

//then output them to the elements
$("#ages").html(ages);
$("#weights").html(weights);
$("#benchpresses").html(benchpresses);

First, I grabbed the table and each of it's rows; I also skipped the first row with the :gt() selector; I am grabbing all rows in #data (the id of the table with the data in it) greater than index '0' (the header that we don't want to include in the calculation). Also notice that I grabbed var rows = $("#data tr:gt(0)"); just once since we would be looping through it 3 times, there is no need to call it each time we sum a column; if I were just doing one column, I wouldn't use an extra line to place this into a var.

Next, I iterate through each cell using children() grabbing the correct index with :nth-child(); one thing I found strange was that though gt() started with a 0-based index, this uses a 1-based index.

Last, using the each() loop, I simply added the cell value to the total, then dumped them out to the cells I had reserved for the totals. All very simple and really only a few lines of jQuery to get it done.

function

If you don't really care how it works and just want a little snippet to get it to work, here you go:
function sumOfColumns(tableID, columnIndex, hasHeader) {
  var tot = 0;
  $("#" + tableID + " tr" + (hasHeader ? ":gt(0)" : ""))
  .children("td:nth-child(" + columnIndex + ")")
  .each(function() {
    tot += parseInt($(this).html());
  });
  return tot;
}

It takes in 3 arguments, and returns the sum of the columns:
  • tableID [string] id of the table
  • columnIndex [int] 1-based column index to sum up
  • hasHeader [bool] if the table has a header to exclude

The same thing above would be accomplished like this (though this is less efficient as jQuery is selcting the same table 3 times):
$("#ages").html(sumOfColumns("data", 2, true));
$("#weights").html(sumOfColumns("data", 3, true));
$("#benchpresses").html(sumOfColumns("data", 4, true));

As you can see, my function uses integers, so change it if you need to.

Currently rated 4.2 by 5 people

  • Currently 4.2/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

javascript | jquery

Javascript replaceAll function

by naspinski 4/5/2009 12:57:00 PM

javascript .replace() only replaces on instance of a character/string, this will replace them all (with no looping)

This may be well know, but I did not realize this until it broke a js function where I was converting currency to integers; anything over $999,999 would not work right, apparently because the extra comma when you hit $1,000,000 was not getting replaced.

At first I went through and looped though a string, but that turned out to be ugly, and likely not as efficient as it could be. So I rooted around with some regexs and figured out the following, turns out it is clean and simple:

function replaceAll(txt, replace, with_this) {
  return txt.replace(new RegExp(replace, 'g'),with_this);
}

So now, replaceAll('1,000,000', ',', '') will return '1000000' and *not* '1000,000'.

Currently rated 4.3 by 9 people

  • Currently 4.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

javascript

Use and Manipulate Asp.Net Validators with jQuery and/or Javascript

by naspinski 3/14/2009 4:40:00 AM

Asp.Net has some great validation included - no need to learn new validation, just grab them with client-side scripting

The problem I found when going into jQuery validation, is that they mostly seem to assume that you can have more than one form on your page; which, for us Asp.Net devs, is not the case. After some digging, I figured out how to use the current validators which I am already familiar with (RegularExpressionValidator, RequiredFieldValidator, etc.) and just hook into them with javascript; no need to reinvent the wheel. It is very easy, and we don't lose the server-side validation that is so great with the Asp.Net validators (if you call on it that is).

want to make a certain validator(s) fire?

Let's say I have a RequiredFieldValidator named 'rfvFirst' and I want it to test for validation on the click event of the element with id 'some_link'. With jQuery, this is what I would do:
$("#some_link").click(function() {
    ValidatorValidate($("#<%= rfvFirst.ClientID %>")[0]);
});

Now, the ErrorMessage of 'rfvFirst' will show if it is not valid - it will behave just as if you tried to submit the form, but just for that validator itself. Notice that I am using <%= rfvFirst.ClientID %> which will pull the crazy Asp.Net id that is produced in Asp.net which is often something strange like 'ctl00_ContentPlaceHolder1_rfvFirst'.  Now, if you want to use a class or something else to select the Validators, that is fine too.  This will fire all of the Validators that have the CssClass 'fire':
$(".fire").each(function() {
    ValidatorValidate($(this)[0]);
});

want to have a button/link cause validation and only work if the page is valid?

For this, you need to make sure your validators each have a ValidationGroup set. In this example I am attaching it to a button with id 'submit' and the ValidationGroup 'new'. Simply attach the click event to Page_ClientValidate() method:
$("#submit").click(function() {
    return Page_ClientValidate('new');
});

This was the only method I found that actually returned something that could be used to tell if the page was valid (I am far from a JS pro). There is likely more ways to do this and I am interested in any that people have found!

want to stroll through the validators?

The are all held in an object (array) 'Page_Validators' and you can access it like any other array, looping or whatever. This will look through and output into the 'report' element the ids of all the Validators you have on your page:
for(var i=0,len=Page_Validators.length;i<len;++i){
    $("#report").append($(Page_Validators[i]).attr('id'));
    $("#report").append("<br />");
}

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

asp.net | javascript | jquery

Is Nick Stakenburg bullying his open-source 'competitor'

by naspinski 3/9/2009 6:00:00 AM

corporate scare tactics on a lower level?

Due to my recent affection for jQuery, I was looking around for a good tooltip framework. I found SimpleTip written by Craig Thompson and it is great! I noticed that there was a SimpleTip 2.0 link on the top and was pretty aggravated at what I saw:

Simpletip2 is now discontinued due to supposed copyright infringement

It was a good run, but unfortunately my own arrogance to the scope of this project led me to make some very bad decisions regarding the design of this site, most notably the copying of anothers layout for my own purposes. I gladly hold my hands up to the fact that, in my rush to provide others with a service, I neglected to think about what I was actually doing - infringing copyright.

I'd like to make one thing abunduntly clear about this whole situation, and that is that no code was ever "adapted, transformed or built upon" as per the terms of the license I supposidly infringed upon. My one and only mistake in this matter was utilising a, to use the term loosely, "competitors" design, and since I have neither the time nor the resources to fight a legal battle between myself and Nick, the project will henceforth be discontinued.

I take this as a life lesson, and hopefully those of you who read this will too, because this incident almost cost me something very valuable to me, my reputation.

Please direct all questions about this infringement to Nick Stakenburg and his Prototip2 project.

A log of all email transactions between me and Nick is available, strictly for reference purposes.

Well, I guess I should re-phrase, I wasn't upset at first, as I am all about following copyrights and such, but I became upset after I read the email transcript. Apparently, according to Nick Stakenburg a developer that charges for a similar Prototype plug-in (he gets no links from my page) Craig is infringing on his rights and he threatened him:
You either stop distributing the javascript or I will make sure you do through a different route, the latter comes with a pricetag.

Not only that, but he goes on to attack Craig's reputation, which is just not cool - as he is doing a great thing by releasing an open-source alternative to a commerical product; nothing unethical about it!

I will leave it to you if you want to read the transcript, but I highly recommend you do. The part that sticks out to me the most was from Nick:
The result of the code is the important thing, the code itself could be rearanged or coded in a different language, just like a translation on licensed text would not be allowed unless given permission. I could code Prototip for any framework and make the underlying code look different every time I go over it. If I did it in jquery I'd start from the ground up as well and end up with a similar result. When you look at the results it's clear that you've based the code on my API and ideas ending up with basically the same thing.

So, apparently, if the 'result' of software is the same as another, they are infringing upon the software itself, even though it may be completely different. Someone better let Open Office know, because they are in a world of hurt, considering this would be no different than if MS came after OO. Not to mention all those email apps and web browsers... almost the entire software industry is in legal trouble if Nick was writing the books. In fact, Nick seems to have released a product (that he charges for) that is amazingly similar to countless js image viewing plugins out there... isn't he breaking his own 'rules'?

Though the most brazen and ridiculous statement though comes at the end:
If you want to use your code for personal use while there is no alternative that works with jquery I'm okay with that but I can't give a green light on distribution.

The master of javascript distribution has spoken!

This is obviously a scare tactic used by Nick to frighten a 'competitor' who is encroachinig on his sales. Nick is jarred by the fact that someone has released a comparable (better?) product that his and it is free. It behaves somewhat the same, so obviously it was copied...

I could be wrong, I am no legal genius, but this seems like a big load of BS and Nick is trying scare tactics to get Craig to remove his competition. I hope Craig calls his bluff (I am assuming that Craig did in-fact not copy Nick's work, as he doesn't have the source).

In fact, isn't this the EXACT reason for the open-source community? Making free alternatives to commercial software that developers can dig in to? Collaboration of minds on software? This whole situation disgusts me. What does everyone else think?

Shout it kick it on DotNetKicks.com

Currently rated 3.7 by 16 people

  • Currently 3.6875/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

javascript | jquery | other