14

I need to generate unique identifiers for html elements in asp.net mvc application. In classic asp.net i could use

<a id=<%=ClientID>%>

Is there some analog in asp.net mvc world ?

UPDATE:

For example, I want to make a reusable Button element. I would perfer code to look similar to

<div class="myButton" id="<%=ClientID%>">
<script>
  var button = document.getElementById(<%=ClientID%>);
  button.onclick = ....
</script>

If ClientId is not available then what is the best way to follow ? For now, I see two options - to generate it like Guid.NewGuid() or pass id from the outside ? Any other options ?

UPDATE: For now, I've come to following solution

    public static string UniqueId(this HtmlHelper html)
    {
        var idGenerator = html.ViewContext.HttpContext.Items[typeof (UniqueIdGenerator)] as UniqueIdGenerator;
        if (idGenerator==null)
            html.ViewContext.HttpContext.Items[typeof (UniqueIdGenerator)] = idGenerator = new UniqueIdGenerator();
        return idGenerator.Next();
    }
       ...
    private class UniqueIdGenerator
    {
        private int id;

        public string Next()
        {
            id++;
            return "_c" + id; // todo: optimize
        }
    }
Zaid Masud
  • 12,359
  • 8
  • 62
  • 84
Alex Ilyin
  • 1,244
  • 1
  • 12
  • 20
  • 1
  • Hi Alex, your question makes sense to me and I understand this, but I can't answer because I am new to MVC as well. Anyway, can you explain why do you need this in case somebody would have an idea on how to get the same as what you would like to do differently? – Davide Piras Jun 27 '11 at 11:57
  • 3
    It looks like you're very used to the webforms way of doing things, but I recomend you take another look at your problem and see if you can try something else, you just have to think differently. If you tell us what your real problem is we might be able to help you more. But if you just want Id's use a random number and a name string. That's all id's are, well appart from plus naming containers, but it's mvc so we don't need those. – Daniel Little Jun 27 '11 at 12:04
  • control.ClientID doesn't generate IDs it returns the value of the generated client id – Omu Jun 27 '11 at 13:39

4 Answers4

20

Simplest correct solution using built-in .NET libraries with no new custom application code required

Use Guid.NewGuid(), with the ToString() numeric representation "N" in order to prevent invalid characters that could browser JS issues.

Guid.NewGuid().ToString("N");

Quoting MSDN regarding the "N" format specifier:

32 digits: 00000000000000000000000000000000

Don't use the default GUID representation as hyphens can be problematic to work with in JS/jQuery.

For completeness, it's best prepend a letter to the beginning of the GUID. Although I've never experienced issues with this in modern browsers, technically an HTML id has to begin with a letter and not a number.

Community
  • 1
  • 1
Zaid Masud
  • 12,359
  • 8
  • 62
  • 84
1

I liked the answer you provided in your Update better than using a Guid, because the latter will be different each time which makes client-side debugging and finding an element in View Source more difficult.

I took it a step further and added a custom prefix.. each prefix uses its own counter to help even further in that regard.

    public static string GetUniqueHtmlid(this HtmlHelper html, string prefix)
    {
        var generator = html.ViewContext.HttpContext.Items[typeof (UniqueHtmlIdGenerator)] as UniqueHtmlIdGenerator;

        if(generator == null)
            html.ViewContext.HttpContext.Items[typeof(UniqueHtmlIdGenerator)] = generator = new UniqueHtmlIdGenerator();

        return generator.GetNextUniqueId(prefix);
    }

    private class UniqueHtmlIdGenerator
    {
        private readonly Dictionary<string, int> _items = new Dictionary<string, int>();

        public string GetNextUniqueId(string prefix)
        {
            if (string.IsNullOrEmpty(prefix))
                prefix = "item";

            int current;

            lock (typeof (UniqueHtmlIdGenerator))
            {
                current = _items.ContainsKey(prefix) ? _items[prefix] : 1;

                _items[prefix] = current + 1;
            }

            return string.Format("{0}-{1}", prefix, current);
        }
    }
Tobias J
  • 12,574
  • 7
  • 54
  • 55
1

There is no single solution to this.

You need to modify your code to generate IDs based on whatever is generating the elements.

For example, if you're looping over rows from a database, you can use the rows' primary keys to generate IDs.

Alternatively, you can eschew IDs altogether and use non-unique classes. (this is especially convenient with jQuery and descendant selectors)

SLaks
  • 800,742
  • 167
  • 1,811
  • 1,896
-2

May be this draft code help you:

static class MyIdGenerator
{
    public static int NextID()
    {
        static int _id = 0;
        return _id++;
    }
}

With static counter every call NextID() will return next Id;

Mehdi Golchin
  • 7,613
  • 2
  • 28
  • 36
Kuvalda.Spb.Ru
  • 431
  • 3
  • 6
  • 1
    Thanks for the suggestion, but code like this can fail if called from multiple threads. Imagine one client sending two ajax requests :) – Alex Ilyin Jun 27 '11 at 13:59
  • Yes, it code not thread safe and can generate unexpected sequence. I think that need refactoring code to avoid use generated id. In MVC paradigm no need use id like this code because it wrong to map model properties. In MVC can use html templates for complex types and collections to map to it - http://stackoverflow.com/search?q=MVC+collections+in+model – Kuvalda.Spb.Ru Jun 27 '11 at 19:54
  • 1
    The correct thread safe implementation is: static class MyIdGenerator { private static int _id; public static int NextId() { return System.Threading.Interlocked.Increment(ref _id); } } – Salvatore Previti Oct 26 '13 at 18:31