Wednesday, 15 June 2011

Stateless means stateless

Or how to screw up everything without true understanding of the technology.

Let's say we want show some dynamic data on the web page, like notes. Notes will be represented by the NoteControl which will be a bunch of textboxes, labels and buttons:


<asp:TextBox ID="TitleTextBox" runat="server"></asp:TextBox>
<asp:TextBox ID="NoteTextTextBox" runat="server" TextMode="MultiLine"></asp:TextBox>
<asp:ImageButton
ID="AcceptNoteButton"
runat="server"
ImageUrl="~/Resources/Icons/64x64/circle-check.png"
AlternateText="Save"
CausesValidation="true" />



 and will be rendered to something like that:


<input name="ctl00$MainContent$NotesView$ctl03$TitleTextBox" type="text" id="MainContent_NotesView_ctl03_TitleTextBox" />
<textarea name="ctl00$MainContent$NotesView$ctl03$NoteTextTextBox" id="MainContent_NotesView_ctl03_NoteTextTextBox" />
<input type="image" name="ctl00$MainContent$NotesView$ctl03$AcceptNoteButton" id="MainContent_NotesView_ctl03_AcceptNoteButton" data-objectid="57101f10-5aed-4285-99e7-2709c3ac41e9" src="../Resources/Icons/64x64/circle-check.png" alt="Save" />


Let's say we get our data from the database every time user reloads the page:


protected void Page_Load(object sender, EventArgs e)
{
       List<Note> notes = GetNotesFromDb();
       RenderNotes(notes);
}

private void RenderNotes(List<Note> notes)
{
       Panel notePanel = new Panel();
       Notes.ContentTemplateContainer.Controls.Add(notePanel);

       for (int i = 0; i < notes.Count; i++)
       {
              var note = notes[i];

              // Here is our NoteControl
              NoteControl noteCtrl = (NoteControl)Page.LoadControl("~/Controls/NoteControl.ascx");
              noteCtrl.Note = note;
              notePanel.Controls.Add(noteCtrl);
       }
}

And every NoteControl will have the event handler for the save button:

protected void acceptButton_Click(object sender, ImageClickEventArgs e)
{
       var button = sender as ImageButton;
       Guid id = Guid.Parse(button.Attributes[ObjectIdAttribute]);
       UpdateNoteDb(id);
}

And because we attached this event handler in the markup:

<asp:ImageButton
ID="AcceptNoteButton"
runat="server"
ImageUrl="~/Resources/Icons/64x64/circle-check.png"
AlternateText="Save"
CausesValidation="true"
OnClick="acceptButton_Click" />

This event handler will be *magically* called when you click save button in the browser. Am I wrong?


OK, let's say now we edited some note and now press the save button:


In a meanwhile, while we were editing this note, we opened the same page from the different machine and simply removed this note, and created a new one instead.


So when we hit the save button we get into Page_Load and... get all the notes from the database:

List<Note> notes = GetNotesFromDb();

Without even knowing that we are getting the new notes now.
And then we are rendering our page:

NoteControl noteCtrl = (NoteControl)Page.LoadControl("~/Controls/NoteControl.ascx");
...
notePanel.Controls.Add(noteCtrl);

And surprisingly we get controls rendered with the same ID's:

<input name="ctl00$MainContent$NotesView$ctl03$TitleTextBox" type="text" value="Another one" id="MainContent_NotesView_ctl03_TitleTextBox"/>
<textarea name="ctl00$MainContent$NotesView$ctl03$NoteTextTextBox" id="MainContent_NotesView_ctl03_NoteTextTextBox">Indeed</textarea>
<input type="image" name="ctl00$MainContent$NotesView$ctl03$AcceptNoteButton" id="MainContent_NotesView_ctl03_AcceptNoteButton" data-objectid="78ae8c7f-7236-4a16-950d-b226f710485d" src="../Resources/Icons/64x64/circle-check.png" alt="Save" />

But before they will be rendered, all the events must fire... And *magically* we still handle the acceptButton_Click event. On the newly created control, which hasn't even been on the client yet. 
Non-magically, data-objectid will be object id of the new note.

TitleTextBox.Text and NoteTextTextBox.Text will still get old values, because we set them only on initial page load:

if (!Page.IsPostBack)
{
       TitleTextBox.Text = ...;
       NoteTextTextBox.Text = ...;
}

What kind of makes sense, since these values will be submitted as parameters and extracted from the Request object automatically every time the page will be in postback.

Short story: we update the wrong object.

What can I say... You have to learn or you'll be the monkey which can code anything, if only the requirements are clear.

No comments:

Post a Comment