In-Place Editing with Atlas Behaviors

This post demonstrates a custom Atlas script component and associated extender control to enable in-place editing (i.e., Labels that morph into TextBoxes upon focus in an AJAXish manner). Source code included...

This morning I saw an email asking about enabling the in-place editable label experience using Atlas. It provided a nice catalyst for something I was planning on doing: publishing an Atlas component authoring post. I wrote a script component in the form of a behavior to encapsulate the functionality, and an extender control that makes it very easy to use on the server. It took me just an hour to get both of these developed. This post is focused on using this functionality, and the intent is to show how easy it is to develop using Atlas, esp. as the toolbox of Atlas components becomes more mature, as well as the possibilities for extending it with new scenarios.

Server-centric Approach
The idea here is to create a model very familiar to ASP.NET page developers, using the standard controls. Extender controls, as described earlier, enable attaching "AJAX" functionality to existing controls. I chose to build an InPlaceEditExtender control that extends <asp:TextBox> controls.

Here is some sample markup demonstrating the usage:

<atlas:ScriptManager runat="server">
  <Scripts>
    <atlas:ScriptReference Path="InPlaceEdit.js" />
  </Scripts>
</atlas:ScriptManager>

<asp:TextBox runat="server" id="nameTextBox" Text="Enter your name" />
<asp:TextBox runat="server" id="emailTextBox" Text="Enter your email address" />
<asp:TextBox runat="server" id="commentsTextBox Text="Comments go here"
  TextMode="MultiLine" Rows="5" Columns="80" />

<nk:InPlaceEditExtender runat="server" id="ipe1"
  LabelCssClass="inPlaceEditLabel" LabelHoverCssClass="inPlaceEditLabelHover">
  <nk:InPlaceEditProperties TargetControlID="nameTextBox" Enabled="true" />
  <nk:InPlaceEditProperties TargetControlID="emailTextBox" Enabled="true" />
  <nk:InPlaceEditProperties TargetControlID="commentsTextBox" Enabled="true" />
</nk:InPlaceEditExtender>

Simple as that! One thing to notice here is that the same extender control can be used to extend multiple textbox controls. This helps reduce the clutter of extender controls on your page, and in design view. Speaking of design view, each TextBox now as a new property called "InPlaceEditing" which can be used to set up in-place editing.

Client-centric Approach
In this approach, the assumption is you have static HTML, using standard <input> and <textarea> tags. You could use imperative script to hook things up to each other, but XML Script makes this so much easier, and readable. Here's a sample:

<input type="text" id="nameTextBox" value="Enter your name" />
<input type="text" id="emailTextBox" value="Enter your email address" />
<textarea id="commentsTextBox" rows="5" cols="80">
Comments go here
</textarea>

<script type="text/javascript" src="/ScriptLibrary/Atlas/Release/Atlas.js" />
<script type="text/xml-script">
  <page xmlns="http://schemas.microsoft.com/xml-script/2005" xmlns:nk="nk">
    <references>
      <add src="InPlaceEdit.js" />
    </references>
    <components>
      <textBox id="nameTextBox">
        <behaviors>
          <nk:inPlaceEdit
            labelCssClass="inPlaceEditLabel" labelHoverCssClass="inPlaceEditLabelHover" />
        </behaviors>
      </textBox>
      <textBox id="emailTextBox">
        <behaviors>
          <nk:inPlaceEdit
            labelCssClass="inPlaceEditLabel" labelHoverCssClass="inPlaceEditLabelHover" />
        </behaviors>
      </textBox>
      <textBox id="commentsTextBox">
        <behaviors>
          <nk:inPlaceEdit
            labelCssClass="inPlaceEditLabel" labelHoverCssClass="inPlaceEditLabelHover" />
        </behaviors>
      </textBox>
    </components>
  </page>
</script>

As you can see, no messy event-handling code or initialization/hookup logic in the page itself. The idea is this XML Script will eventually be designable and tool-able, so you won't have to author it by hand, like I just did. I love the ability to encapsulate code so the page is fully declarative, and Atlas enables me to do that on the client as well.

The script brings in the script files via a reference, declares a TextBox component for each input/textarea tag, and then adds an instance of my InPlaceEditBehavior class to the behaviors collection of each TextBox. The Atlas framework and the behavior implementation work together to do the initial hookup, and subsequent event handling.

You can download the script code for the behavior, the extender control and a couple of sample pages, and run them locally (against the December Atlas CTP). A brief description of the functionality would be useful:

  • The TextBox controls are dynamically replaced with Labels. The text from the textbox is copied into the Labels.
  • The Labels can be styled using a CSS class specified by the LabelCssClass property. They can display a different look when the mouse hovers over them via the LabelHoverCssClass property.
  • The Label is hidden and the textbox is restored, either when you click the label, or when you tab into it (so it is more accessible, which is something I think a bunch of implementations out there don't handle). Note that apparently Firefox doesn't raise focus events for labels, so for that browser you must click on the Label. In IE you do get the nice tab-based activation as well.
  • The textbox is replaced with the label when you move out of it.
  • Hitting Esc cancels the changes (this is standard HTML behavior).
  • The behavior integrates with the Validation feature of Atlas, so that if the textbox in question is in an invalid state, it remains a textbox even after you move away from it. Again, the samples out there miss this scenario. The sample page demonstrates this.

You might notice the page actually contains an <input> and not a label to begin with, despite the fact that the textbox appears to be transient. This is contrary to various samples out there. As a result, InPlaceEdit might be a misnomer, but I chose to go with it, since that is the concept people associate the functionality with. Why did I choose to go down this route? Various reasons:

  • First the input element will remain in the absence of scripting capabilities
  • Second the developer will likely want the textbox to be present to customize with additional behavior such as validation. As such extending an <asp:TextBox> made more sense in the extender control scenario.
  • Finally, the input element will be present in the form, so that its value can be submitted along with a form post.

In all honesty, I didn't think of these reasons before I started the implementation. These reasons were justifications for my approach that dawned upon me as I worked on the code. How I decided on this approach actually provides some insight into designing Atlas components. I'll share them in a subsequent post, when I cover how to get down and dirty with C# and JavaScript to build the actual functionality running behind the scenes.

Here's a quick video of the demo. I am experimenting with the video thing - let me know if it works, helps, or distracts.

Posted on Tuesday, 12/27/2005 @ 2:49 PM | #ASP.NET


Comments

34 comments have been posted.

Ivan Porto Carrero

Posted on 12/27/2005 @ 6:33 PM
Looking forward to the post on how to make the actual controls.

And definitely I need to say keep up the good work. You've taught me already heaps of stuff.

In your previous post about the updater panel. I love the idea behind the update panel but wouldn't it be possible to have it just return the neccesary information instead of the whole html of the page.

rbfigueira

Posted on 12/28/2005 @ 12:07 AM
Hi Nik:P

Just great !! You make my day :)
Thanks for sharing !

sam

Posted on 12/28/2005 @ 6:37 AM
Suppose this scenario, I want to change in runtime ("code-behind" file) the enabled property of some particularly TargetControlID of the InPlaceEditExtenderProperties.

For example, if the user are login I want to Enabled="true" the (TargetControlID ="commentsTextBox") else put Enabled="false" to that particularly TargetControlID (InPlaceEditExtenderProperties).

How can I do that ?
Thanks

sam

Posted on 12/28/2005 @ 8:17 AM
What I asked in my preview comment not fit so well in this sample but what I really want to know, in general meaning, is how we can change or manipulate properties of some Atlas “controls”.

Let me explain:
For example, suppose that the page only have the “normal” textbox and I want in runtime ("code-behind" file) associate some particularly Behaviour to this textbox.
It is possible to have that great power control? Remove or associate some particularly Behaviour in runtime ("code-behind" file)?

rbfigueira

Posted on 12/28/2005 @ 8:49 AM
I have forgot to say one think…
It’s great to see the video because we have a better idea of what the sample can do :p

>>I'll share them in a subsequent post, when I cover how to get down and dirty with C# and JavaScript to build the actual functionality running behind the scenes

Sounds great! Thanks :P We will wait :P

Henk Feijt

Posted on 12/28/2005 @ 9:13 AM
Great stuff. You stated that properties of the extender control show up in the Properties window of a text box, but I don't see "InPlaceProperties" listed with the text box.

This goes also for the auto suggest control in the Atlas CTP. Am I doing something wrong here<

Thanks

Nikhil Kothari

Posted on 12/28/2005 @ 9:29 AM
Sam, (and Ricardo - you asked for this as well) - you can programmatically do things as well.

For example, you can create an instance of InPlaceEditProperties at runtime, initialize it, and add it to the extender control's TargetProperties collection. Or you can call the ExtenderControl's GetTargetProperties() method passing in the textbox, and get back an instance of the properties object if one exists. Then you can modify it, for example, in your scenario, set Enabled = false. Its probably best or rather simplest, if you added all the property objects statically, and then just modified their Enabled properties at runtime. Just make sure to make your changes on the properties object or add/remove items into the collection before PreRender. That should enable you to implement dynamic pages. This applies to all extender controls obviously.

However, I am not sure I'd use this approach for your particular scenario, where you want to disable editing if the user is not logged in. Simply not showing the textbox isn't going to be secure. Someone could just craft a form post including a value for the textbox while side-stepping the browser completely.

Ivan - the idea behind UpdatePanels is indeed to send back only portions of the page, and not the whole page.

Michael Teper

Posted on 12/28/2005 @ 10:47 AM
First of all, the video is very helpful. Second of all, looking at the video, two improvement ideas immediately come to mind:

- I would style the label with a soft border so that there is a hint of an input field there.
- I would have the contents of the field selected on click (or tab). At least that should be an optional behavior.

Looks really nice!

PS. Unrelated, but it would help if the "Refresh" button in the comment section maintained the scroll position (or just took you down to the form).

Tom

Posted on 12/28/2005 @ 10:57 AM
> ".. It took me just an hour to get both of these developed"
What ? Really !! What kind of books you read to have this knowledge ? :p

>"...Simply not showing the textbox isn't going to be secure"
well, you are right, but i think this is perfect to use in "admin" pages !! To be one perfect sample is missing the "update" and "accept" button (generated automatically) to submit the changes to database :P
Can you had that ? Be nice ;)

Tom

Posted on 12/28/2005 @ 11:03 AM
hummm... perhaups not missing the "accept" button. But it will be very helpfull if we have the "update" and "cancel" button (generated automatically when we are editing the data) !!

I think that could be a great idea to update this sample ;)
cheers :o)

Secret Admirer

Posted on 12/28/2005 @ 12:18 PM
Can you blog about how you are so good technically? How did you get to where you are today so quickly? Got any tips for wannabe architects, software engineers and managers?

Please. :-)

Nikhil Kothari

Posted on 12/28/2005 @ 12:43 PM
Tom, when I said it took an hour, I was implying that Atlas makes it super easy. Yes, it does mean one has to know Atlas pretty well to start with... but I was simply alluding to the capabilities and promises of the platform that are in store... and not to my knowledge. But I am flattered ;-)

I did think a bit about those additional buttons - I'll cover a bit more about why I didn't add them in my post on authoring these controls. The short reason is the overall philosophy of the atlas client side framework is to leave the UI to the designer/HTML & CSS portion of the app, rather than building it programmatically, because that gets in the way of customizability.

You could actually set up buttons yourself, and bind the visibility of the buttons to the isEditing property of the script components, and the click of the buttons to the beginEdit/endEdit methods of the script component. For example, if you had ok button in your HTML, you could do the following in XML script once you set up an id for the inPlaceEdit instances:

<button id="okButton" visible="false">
<bindings><binding property="visible" dataContext="inPlaceEdit1" dataPath="isEditing" /></bindings>
<click><invokeMethod target="inPlaceEdit1" method="endEdit" /></click>
</button>

Hope that provides some clues... to make this fully work, I need to add a parameter to endEdit whether to commit or cancel.

Petter

Posted on 12/29/2005 @ 3:05 AM
This sample is very good and helpful. Please put more like this one :P
The main problem here is that we don’t have sufficient internal documentation enough and knowledge to build controls like this one!!
Please explain with more detail what you have made in this sample!!!

Can you explain we deep details what you have made in the InPlaceEditExtender.cs and InPlaceEdit.js files and why that approach?

This is the only way we can learn and start to build our own samples! Perhaps you can start to write a book :p

RichB

Posted on 12/29/2005 @ 6:50 AM
Sounds very nice. I would like to add another feature - based on the "in place editing" behaviour I wrote only last week!

I would like the "auto-edit" feature which is enabled through focussing or clicking to be disabled and the edit mode to be programmatically controlled.

For example, my use-case is webpage containing a list of predicates to be included in the WHERE clause of a SQL statement. I have hyperlink-style buttons called "Include in Search" alongside the labels. Clicking on these hyperlinks causes the label to go into edit mode and display a textbox. However, conceptually these buttons also act like checkboxes - telling the code to include these predicates in the query. (When the label is in edit mode, the hyperlink changes to "Exclude".)

I hope this is enough justification to have the ability to disable the auto-edit mode and make it programmatic only. If not, ping me and I can send screenshots etc.

Tom

Posted on 12/29/2005 @ 7:46 AM
Yeap, I agree with Petter !
A good explanation would help ;p

Note: Can be directly in the source files or one post dedicated to this excellent sample! :-)
Nikhil, you are the best :P

Samuel

Posted on 12/29/2005 @ 8:01 AM
I am in!
Nikhil, please put some good explanation !

Zan

Posted on 12/29/2005 @ 8:05 AM
I also agree. Detail explanation would be amazing: p

rbfigueira

Posted on 12/29/2005 @ 8:16 AM
Such as I had suspected, this example would make a success ! ;p

Cheers,
Ricardo Figueira aka rbfigueira ;-)

Steve

Posted on 12/31/2005 @ 4:42 AM
I really like this - I'd like to see it tie in with objectdatasource and other data sources as well?

Russ Helfand

Posted on 12/31/2005 @ 9:18 AM
Cool stuff, Nikhil. This is a great approach to decorating existing elements with extended behavior.

One minor note. I found that I had to manually add a reference to System.Design in my web site project in VWD in order to get the samples to work. Here is what I recall doing:

1) Downloaded the zip you provided in this article. Unzipped it into the foo folder.
2) (I'd previously downloaded and installed VWD and the Dec Atlas release.) In VWD, created a new C# ASP.NET Atlas web site based on the Dec Atlas ASP.NET web site template.
3) Manually copied the files from foo into this new web site's folder.
4) Deleted the readme, EULA and default page files.
5) Opened SampleControls.aspx in VWD's code editor.
6) Hit F5. At this point VWD warned me of errors. Specifically, it could not find a needed reference to System.Design.
7) I tried running the page anyway but it gave me the same sort of exception info via the browser window.
8) Added a reference to System.Design via the Solution Explorer in VWD.
9) Hit F5 again and *poof* it worked perfectly.

None of this is a big deal. I just thought I'd pass the info along in case it helps.

The sample you provided is really nice. There is a pretty big advantage (in my opinion) in NOT rendering the textbox all the time. Rendering it only when you are in edit mode for one particular form element has the advantage of providing a dramatically less cluttered page presentation. Bottom line: it's much easier to read, to "take in" at a glance. The visual cue you provide on-hover (highlighting the field) seems like a sufficient suggestion to the visitor that that field is potentially editable.

Steve

Posted on 1/2/2006 @ 7:39 AM
This is very nice. Nikhil, I look forward to your next book on Atlas (you really need to publish one on Atlas) :)

Russ, thanks for posting that info, that enabled my project to work as well.

madwww

Posted on 2/16/2006 @ 11:59 PM
Great!
It is too Nice!

Pavan

Posted on 2/19/2006 @ 6:39 PM
Russ Helfand, thanks for your post. I was lost until I read your post.

Driss Selhoum

Posted on 2/22/2006 @ 1:58 AM
Hi all,

I have started playing around with Atlas, and the least I can say is that it's opening up a door of countless opportunities for IT Professionals and I'm am starving for the March release. However, I have a curious question about Atlas as it is right now. Here is what I'm trying to do:
I have a couple of user controls (.ascx) that I load asynchronously using Atlas, but when the User control is loaded I cannot fire any events from within, the Button-click Event handler of the user control does not fire any events. How can I surpass this problem?

Many thanks

Driss

Driss Selhoum

Posted on 2/22/2006 @ 1:59 AM
Hi all,

I have started playing around with Atlas, and the least I can say is that it's opening up a door of countless opportunities for IT Professionals and I'm am starving for the March release. However, I have a curious question about Atlas as it is right now. Here is what I'm trying to do:
I have a couple of user controls (.ascx) that I load asynchronously using Atlas, but when the User control is loaded I cannot fire any events from within, the Button-click Event handler of the user control does not fire any events. How can I surpass this problem?

Many thanks

Driss

Azeezulla Mohamad

Posted on 2/24/2006 @ 11:24 AM
<div>
<asp:TextBox ID="TextBox1" runat="server" />&nbsp;
<span id="validator1" style="color: red">Please Enter FirstName</span>
<asp:TextBox ID="TextBox2" runat="server" />&nbsp;
<span id="validator2" style="color: red">Please Enter LastName</span>
<asp:Button ID="BtnSubmit" runat="server" Text="Submit" />
</div>
<div id="formGroup"></div>

<script type="text/xml-script">
<page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
<references>
<add src="ScriptLibrary/AtlasUI.js" />
<add src="ScriptLibrary/AtlasControls.js" />
</references>
<components>
<textBox targetElement="TextBox1">
<validators>
<requiredFieldValidator errorMessage="You must enter some text." />
</validators>
</textBox>
<validationErrorLabel targetElement="validator1" associatedControl="TextBox1" />

<textBox targetElement="TextBox2">
<validators>
<requiredFieldValidator errorMessage="You must enter some text." />
</validators>
</textBox>
<validationErrorLabel targetElement="validator2" associatedControl="TextBox2" />

<validationGroup id="formGroup" targetElement="formGroup">
<associatedControls>
<reference component="TextBox1" />
<reference component="TextBox2" />
</associatedControls>
</validationGroup>

<button targetElement="BtnSubmit" >
<bindings>
<binding dataContext="formGroup" dataPath="isValid" property="enabled" />
</bindings>
</button>
</components>
</page>
</script>
</asp:Content>

--end code

I know the reason why it is failing.
Page controls are rendered at server end, and has different client IDs for controls.
So for validation, xml-Script has client IDs, instead of targetElement="TextBox1"



How do i achieve to validate these controls.

Please let me know.

thanks
-Azeez

Azeezulla Mohamad

Posted on 2/24/2006 @ 11:25 AM
I am new to atlas, I would like to add validations to webControls.
Here is the following code is working fine without masterpage.

--code

<%@ Page MasterPageFile="~/MasterPage.master" Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<atlas:ScriptManager ID="ScriptManager1" runat="server" />
<div>
<asp:TextBox ID="TextBox1" runat="server" />&nbsp;
<span id="validator1" style="color: red">Please Enter FirstName</span>
<asp:TextBox ID="TextBox2" runat="server" />&nbsp;
<span id="validator2" style="color: red">Please Enter LastName</span>
<asp:Button ID="BtnSubmit" runat="server" Text="Submit" />
</div>
<div id="formGroup"></div>

<script type="text/xml-script">
<page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
<references>
<add src="ScriptLibrary/AtlasUI.js" />
<add src="ScriptLibrary/AtlasControls.js" />
</references>
<components>
<textBox targetElement="TextBox1">
<validators>
<requiredFieldValidator errorMessage="You must enter some text." />
</validators>
</textBox>
<validationErrorLabel targetElement="validator1" associatedControl="TextBox1" />

<textBox targetElement="TextBox2">
<validators>
<requiredFieldValidator errorMessage="You must enter some text." />
</validators>
</textBox>
<validationErrorLabel targetElement="validator2" associatedControl="TextBox2" />

<validationGroup id="formGroup" targetElement="formGroup">
<associatedControls>
<reference component="TextBox1" />
<reference component="TextBox2" />
</associatedControls>
</validationGroup>

<button targetElement="BtnSubmit" >
<bindings>
<binding dataContext="formGroup" dataPath="isValid" property="enabled" />
</bindings>
</button>
</components>
</page>
</script>
</asp:Content>

--end code

I know the reason why it is failing.
Page controls are rendered at server end, and has different client IDs for controls.
So for validation, xml-Script has client IDs, instead of targetElement="TextBox1"



How do i achieve to validate these controls.

Please let me know.

azeezulla@gmail.com

Peter Kellner

Posted on 3/23/2006 @ 2:17 PM
I just tried the above in the March CTP and get the error "Assertion: Unrecognized tag nk:inPlaceEdit

Also, are there issues with master pages here?

Kanz

Posted on 3/23/2006 @ 4:25 PM
This is great. Did somebody test this code with the March CTP? I tried but failed. I wonder if somebody can help!

Kanz

Posted on 3/23/2006 @ 4:27 PM
BTW. I am also facing the same [Assertion: Unrecognized tag nk:inPlaceEdit] problem. Help will be greatly appreciated.

Werner

Posted on 4/4/2006 @ 5:25 AM
Wat the march CTP consideres, is to fix.
Just replace all 'Web.' tags to 'Sys.'
Furthermore the one before last line in InPlaceEdit.js you need to replace Type.registerSealedClass to nk.InPlaceEdit.regiesterSealedClass

that's about it.

but i have another problem.
I cannot (i don't know how) create these inplace edit things programatic.
i do it like:
InplaceEdit ipe=new InplaceEdit();
TextBox tb=new Textbox();
InPlaceEditExtenderProperties ipEP;
ipEP=ipe.GetTargetProperties(tb);
if(ipEp==null)
ipeP = new InPlaceEditExtenderProperties();
//then set the extended properties
ipe.ExtenedProperties.Add(ipEP);
this.controls.add(tb);
this.controls.add(ipep);

//then render the controls (tb.render(writer) and ipe.render(writer))


but doesnot work..
i have no clue i had a simulair problem with the DragAndDrop stuff, created draggable controls but absolute nothing works :(

pls

tnkx

W

Art.and.co

Posted on 5/2/2006 @ 1:59 AM
There is one problem with it. Parent for textbox and label is created DIV section. However, if it is not placed inside some formated container like table cell it takes all available space and breaks layout. For instance, if place on empty page only textbox and button if it is in edit mode it shows OK but if label displayed- button goes to the next row.

Art.and.co

Posted on 5/2/2006 @ 4:53 AM
seems problem is in display property. Textbox usually have "inline" style. In that case if style is "inline" style "inline-block" can be applied to underlying SPAN (not DIV :) ).

gregor sUttie

Posted on 5/29/2006 @ 2:35 AM
I am trying this demo using the march release of atlas and I get an error in InPlaceEdit.js second last line - Type.registerSealedClass('nStuff.Samples.InPlaceEdit.InPlaceEditBehavior', Web.UI.Behavior);

Web.Ui.Behaviour - says Web is undefined when i run it - anyone got any ideas?
The discussion on this post has been closed. Please use my contact form to provide comments.