This project is read-only.

Invocation Order of Parts and Fields

Topics: Writing modules
Jul 4, 2011 at 11:50 AM


I have a part that contains a field. The part's driver depends on the data of the field to be filled, but I can see that the part's driver is being invoked before the field's driver. Is there a way to guarantee the part's driver to run after the field's driver, or do I have to resort to listening to some event in the handler (e.g. OnVersioned)?

Jul 4, 2011 at 11:54 AM

I should have noted that each of the part and the field is defined in a separate module, and the part's module has an explicit dependency on the field's module. I used to think that this dependency would cause the order of invocation to be as I want it, but it doesn't seem to be the case (using Orchard 1.2).

Jul 4, 2011 at 12:48 PM

I partially understand what you just said. So correct me if I'm wrong, I'll attempt to correct your terminology here.

1. You have a ContentField defined in a module.

2. You have a ContentPart defined in another module.

3. You defined a ContentType with ContentField and ContentPart added.

4. When you create a ContentItem, based on the ContentType definition, you want to trigger the ContentPartDriver after your ContentFieldDriver, because you have your logic split between them.

At this point, you should see that you're doing it wrong. Drivers should execute independently of one another. I suggest you change your abstractions.

Create one ContentPart with whatever fields you need doing everything that it needs to do.

Jul 4, 2011 at 1:10 PM

Thanks for your reply @sharpoverride. I apologize if my message wasn't clear enough.

You got most of it correctly. I'd just be more specific to say that MyContentPart is defined to have a field of MyContentField. MyContentType just has a MyContentPart.

Suppose I have an ImageField that exposes a URL property, and a MediaItemPart that uses the field and adds its own MimeType property. Now I want to set the MimeType in MediaItemPartDriver, after the URL has been set. If this is not the correct way, could you please suggest a better way?

Jul 4, 2011 at 1:42 PM

Ok. now we are getting to something more concrete.

So you want to reuse the existing MediaPicker functionality. That is easy.

On your content part, and in the ContentRecord, define two fields. ImageUrl and MymeType.

In your view, copy paste the code for the ImageUrl. It should be a Label, input with type set to text, and a button. For the button there is a simple jquery call that selects your button and invokes the MediaPicker. I know I've done this before, I just can't find it right now.

Jul 4, 2011 at 2:33 PM

Okay, it took me a while but you can find in the samples.ecommerce a field that allows you to add the PickImage functionality.

I'll just copy paste the Prodcut.cshtml content here for reference.


@model Samples.Commerce.Models.ProductPart
@using Orchard.Utility.Extensions;



    <label class="sub" for="ImageUrl">@T("Description")</label><br />
    @Html.TextAreaFor(m => m.Description, 5, 200, new { @class = "text", @style = "width:99%" })

    <label class="sub" for="Sku">@T("Sku")</label><br />
    @Html.TextBoxFor(m => m.Sku, new { @class = "text" })<br />

    <label class="sub" for="Price">@T("Price")</label><br />
    @Html.TextBoxFor(m => m.Price, new { @class = "text" })<br/>

    <label class="sub" for="ImageUrl">@T("Image Url")</label><br />
    @Html.TextBoxFor(m => m.ImageUrl, new { @class = "text", @style = "width:70%" })

    <input class="button" id="PickImage" value="Pick Media" width="30" />

    <script language="javascript">
    var baseUrl = "@HttpContext.Current.Request.ToRootUrlString().TrimEnd('/')";
    jQuery("#PickImage").click(function () {
                        img: $("#ImageUrl").val(),
                        uploadMediaPath: "Products",
                        callback: function (data) {
                            $("#ImageUrl").val(baseUrl + data.img.src);                           

This should allow you to have a nice PickImage functionality for your ImageUrl string, and afterwards, on save or whatever, on your driver you can proceed to discover the MimeType and set it on behalf of the user.

Jul 4, 2011 at 4:11 PM

Thank you, but that's not what I want. I'm not using the MediaPicker, and I was just providing an example. What I need is to know how to set the value of a property of the part to something calculated from that of the field.

Jul 5, 2011 at 7:25 AM

Okay, that is fine. What I'm telling you is this: You should design your Parts to be independent of other Fields, or Parts.

Your scenario is currently not supported AFAIK.

Hope someone else can take over this thread and help you.

Good luck!

Jul 5, 2011 at 1:15 PM

The only way I could find was to create an interface for MyFieldHandlers and invoke it explicitly in the field's driver. I hope someone can shed some light on whether this is the best way of doing it.

Jul 6, 2011 at 8:33 AM

Good ideea!

I can suggest the following

1. I think Orchard has a MessageBus you could use to push the event, MyField has changed, and then have a service capture that.

2. The way you did it, but with maybe a bit more indirections.

Jul 6, 2011 at 9:14 AM
  1. Using the MessageBus would be the same, except for losing compiler's validation, as the message bus uses an "InterfaceName.MethodName" message format.
  2. I don't see how my way can be done "with a bit more indirection"...
Jul 6, 2011 at 9:41 AM

So. For your scenario, what you described sound great. I don't think there is a better way, there are only alternatives.

usually an alternative involves either being more direct or more indirect. By indirect I mean going through another level of abstraction that may or may not communicate your intention as best as it does now. It is a matter of taste, so I won't go any further than this. Your idea works, so it's good.

The message bus was designed for this type of interaction, where you need to communicate something to one or more subscribers; which is why some might consider it the best way to do it. I like the using a service for this approach also.  Again, a matter of taste. Personally I would have avoided doing it like this, and tried to designed it in another way. But, you have your constraints, and that's fine. Glad you got it working!