Learning Django by Example(12) Tag it in place
django pythonThe main challenge of tagging is the usability: tags are usually used for navigation, but they are subject to updating at any time. Therefore, tags needs to switch back and forth in display mode and edit mode in a non-intrusive way. Dojo’s dijit.InlineEditBox seems a good candidate for this purpose.
I failed to declare a dijit widget inside the HTML file, maybe the mechanism of
dojo.parser
prevents me to do so? So I went back to a local copy of dojo-svn
and declared FilteredInlineEditBox
in dijit
namespace:
dojo.provide("dijit.FilteredInlineEditBox");
dojo.require("dijit.InlineEditBox");
dojo.declare("dijit.FilteredInlineEditBox", dijit.InlineEditBox, {
postMixInProperties: function () {
this.inherited("postMixInProperties", arguments);
this.disabled = true;
},
setValue: function (val) {
this.value = this.filterIn(val);
this.displayNode.innerHTML = dojo.trim(val) || this.noValueIndicator;
},
filterIn: function (val) {
return val.replace(/<[^>]+>/g, "");
},
});
No extra functionalities added, the inherited functions just manipulate the status for different behaviors. First, the dijit widget is disable by default to block any mouse event.
In the client side, the edit
control tailgates the FilteredInlineEditBox:
<p>
Tags:
<span
id="tags"
dojoType="dijit.FilteredInlineEditBox"
editorParams="{lowercase:true, trim:true,}"
onChange="updateTags"
>
{{ object.tags|popuptags|safe }}</span
>
(<a href="javascript:void(0);" id="edit">edit</a>)
</p>
and its onclick
is hooked to invoke _edit
:
dojo.connect(dojo.byId("edit"), "onclick", function () {
dijit.byId("tags")._edit();
});
Notice that _edit
never references/modifies disabled, so when the
FilteredInlineEditBox
exits edit mode, it is still disabled. This is the
exact behavior we ask for!
A xhrPost
request is used to update the tags asynchronously when onChange
is
invoked:
function updateTags(vals) {
dojo
.xhrPost({
url: "/admin/bookshelf/tag/update/",
content: { isbn: "{{object.isbn}}", tags: vals },
})
.addCallback(function (newTags) {
dijit.byId("tags").setValue(newTags);
});
}
In the serve side, the tags are updated, and HTML links are rendered with link attributes:
def tag_update(request):
newTag = "none";
book = Book.objects.get(isbn=request["isbn"])
if book:
book.tags = request["tags"]
book.save()
tags = [ x for x in re.split("\W+", request["tags"]) if x ]
newTag = " ".join(['<a href="/bookshelf/tags/%s">%s</a>' % (t, t) for t in tags ])
return HttpResponse(newTag)
Now in our hack of setValue
, the displayNode
is still rendered as HTML
links, but all the HTML elements are filtered by filterIn
, so only the raw
text is passed to value
, which is used to construct the edit widget later.
Therefore, the tags are displayed to users as HTML links, but modified as raw
text.