Writing an Orchard Webshop Module from scratch - Part 11

a href="/blog/writing-an-orchard-webshop-module-from-scratch-part-1">introduction.

In this part, we'll be handling the following:

  • DisplayTypes: what they are and how to use them;
  • Customize the content list in the Admin by creating summary admin shapes and templates for ProductPart.

Download the source code

In part 4 we saw how to create the ProductPart and render it by generating two shapes using content part drivers: Parts_Product and Parts_Product_AddButton.
When we have a look at the list of contents from within the admin, we see that these shapes are rendered exactly the same (with the difference being that the HTML is styled using TheAdmin theme):

Now, most of us would rather not display the "Add to shoppingcart" buttons here.
We might even want to customize the rendering of the Parts_Product shape as well. For example, we may want to put both Price and Sku on a single line in order to minimize the height of the content record.

Let's see how we can do that.

First, we'll start with removing the "Add to shoppingcart" button from the admin view.

Removing the Shoppingcart button

If you remember from part 4, we are generating the Parts_Product_AddButton shape from the ProductPart driver:

Drivers/ProductDriver.cs:

protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper) {
            return Combined(
                ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
                    Price: part.Price,
                    Sku: part.Sku
                )),
                 ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton(
                     ProductId: part.Id
                     ))
                );
        }

What we want to do is only render the Parts_Product_AddButton shape on the front end, not on the backend.

So how can we do this?
 

DisplayTypes

DisplayTypes enable us to further control where and if a shape should be rendered.
Whenever Orchard renders a content item, it will invoke the IContentManager.BuildDisplay method, which looks like this:

dynamic BuildDisplay(IContent content, string displayType = ""string groupId = "");

Notice the second parameter called displayType.

Whenever a content item is being rendered on the front end, Orchard passes the string "Detail" into the displayType argument.
Likewise, Orchard passes the display type "Summary" whenever it renders a List of content items.

And finally, whenever a content item is being rendered as part of a list in the back end, "SummaryAdmin" is specified as the displayType.

So how can we use these? We can take advantage of it in at least two places:

1. From within our driver's Display method.

2. From within Placement.info.

So we can do two things here: we could either update our Display method so that it will only create the Parts_Product_AddButton shape if displayType != "SummaryAdmin", like so:

protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
        {
            var driverResults = new List<DriverResult> {
                ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
                    Price: part.Price,
                    Sku: part.Sku
                ))
            };
 
            if (displayType != "SummaryAdmin")
            {
                driverResults.Add
                (
                    ContentShape("Parts_Product_AddButton", () => 
                        shapeHelper.Parts_Product_AddButton(ProductId: part.Id))
                );
            }
 
            return Combined(driverResults.ToArray());
        }

 

Or, we could simply update Placement.info to achieve the exact same effect:

<Match DisplayType="SummaryAdmin">
    <Place Parts_Product_AddButton="-" />  
  </Match>

That was even more easy! The benefit of using Placement.info over implementing logic in our driver is simplicitly: using some simple configuration, we can determine where and when to render a shape.
Rendering, positioning and hiding shapes based on certain conditions is a common task in Orchard, and Placement.info is a great and easy way to orchestrate things.

The way Placement.info works is as follows: when we create shapes from our content part drivers, Orchard needs to decide where to render these shapes. To make the decision, Orchard uses the Placement.info file.

Now you may be wondering if first creating some shapes and then checking if they should be rendered or not is efficient. Especially in cases where shapes might be expensive to create (in terms of construction time and memory allocation).
However, note that we're not actually passing shapes into the ContentShape method. Instead, we're passing in a lambda that creates the shape. This lamda is only invoked if the shape needs to be actually rendered. How nifty!

Perhaps you have already made the mistake (I did) by accidently passing in a shape directly, instead of a lamda:

ContentShape("Parts_Product_AddButton", shapeHelper.Parts_Product_AddButton(ProductId: part.Id))


What will happen now is that an exception will be thrown and catched (logged and swallowed) up somewhere in the stack. The result is that the shape will not be rendered.

Should you ever wonder why some shape isn't being rendered even though you did everything else right, make sure that you passed in a lamda instead of a shape:

ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton(ProductId: part.Id))

Right, so now our admin screen is fixed:

One final thing we are going to do is to put the Price and Sku fields on a single line to cleanup the screen a little.
We'll do this by creating another shape to be rendered for the "SummaryAdmin" display type.

Let's go ahead and modify the Display method of our ProductDriver as follows:

Drivers/ProductDriver.cs:

protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
        {
            return Combined(
                ContentShape("Parts_Product_SummaryAdmin", () => shapeHelper.Parts_Product_SummaryAdmin(
                    Price: part.Price,
                    Sku: part.Sku
                )),
                ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
                    Price: part.Price,
                    Sku: part.Sku
                )),
                 ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton(
                     ProductId: part.Id
                     ))
                );
        }

 

Notice that we added a content shape called "Parts_Product_SummaryAdmin".

The next thing to do is create a template for it:

Parts/Product.SummaryAdmin.cshtml:

@{
    var price = (decimal)Model.Price;
    var sku = (string)Model.Sku;
}
<article>
    <strong>Price:</strong> @price.ToString("c") | <strong>Sku:</strong> @sku
</article>

 

Finally, we need to add a <Place> element for this shape in Placement.info. We need to wrap it inside a <Match> element so that it will only be rendered for the SummaryAdmin display type. We also need to specifiy that the "Parts_Product" shape should not be rendered in SummaryAdmin mode.

Placement.info:

<Placement>
...
 
  <Match DisplayType="SummaryAdmin">
    <Place Parts_Product_AddButton="-" />
    <Place Parts_Product="-" />
    <Place Parts_Product_SummaryAdmin="Content:0" />
  </Match>
</Placement>

And now our admin screen looks like this:

Much better!

In conclusion, Placement.info is a powerful tool that enables fine-grained control over what shapes get rendered where under which circumstances, such as DisplayType.

74 comments

  • ... Posted by Guest Posted 02/11/2012 06:13 PM

    Very informative! I've learned more about Orchard from your "real world" tutorial series than everything else out there.

  • ... Posted by Mel Shellam Posted 02/21/2012 07:40 AM

    Great to see a 'Practical' guide to Orchard, keep it up!

  • ... Posted by Mel Shellam Posted 03/15/2012 03:54 PM

    I'm trying to alter the "View | Unpublish | Edit | Delete" actions menu when displaying a list of my content type. I can add items to this menu, but how do I stop the above 4 items from being shown? (It's at times like this I wish there was shape tracing on the admin pages too)

  • ... Posted by Artzfam Posted 03/26/2012 06:29 AM

    I dont think you mentioned tree frogs?

  • ... Posted by TJ Havens Posted 03/28/2012 04:26 PM

    R we going to continue this now that Orchard 1.4 is out?

  • ... Posted by wadep Posted 04/06/2012 10:22 PM

    This has really helped my understanding of orchard module development. Thank You.

    I would appreciate it if you would finish the development. re. Part 12 - Part 15

  • ... Posted by Sipke Schoorstra Posted 04/07/2012 10:27 PM (Author)

    Yes. Also, the posts will be updated to accomodate for 1.4.

  • ... Posted by Sipke Schoorstra Posted 04/07/2012 10:28 PM (Author)

    I did, but they jumped off the trees!

  • ... Posted by Sipke Schoorstra Posted 04/07/2012 10:29 PM (Author)

    Glad to hear it, thanks. Yes, working on it!

  • ... Posted by Dkotorac Posted 04/08/2012 04:18 PM

    First things first: congratulations on great tutorial and many thanks! We really need more of these "real life" examples. When do you expect to update the posts to Orchard 1.4?

  • ... Posted by pubgrub Posted 04/09/2012 10:54 PM

    Do you have an ETA for the 1.4 follow-ups Sipke [a.k.a sfmYoda as the orchard forum denotes; 'much better than my rash comparison, but you got the point' ;)]? Anyhow, like everyone else's sentiments -- your tutorial is by far the best getting around! Keep up the good work mate ;)

  • ... Posted by Offshore Development Posted 04/26/2012 06:54 AM [http://mindinventory.com/]

    Orchard module development is fast growing technology in world wild,and this tutorial is help for me.Come again for new idea.

  • ... Posted by K Utby Posted 05/01/2012 12:01 AM
    <p>Howdy, sipke - have you shelved this project? I'm yet to dive into orchard, but have read enough on the forums to know their documentation isn't up to scratch; in doing so, I note your tutorial is the only one that seems to get recommended by orchard users. My dilemma is, do I try and hunt down Orchard 1.3 and use your existing tut, or are the planned upgrades imminent and worth waiting for? Thankyou for any advice, Kristin <br>P.s. From what I've read your content is very exciting. </p>
  • ... Posted by Sipke Schoorstra Posted 05/01/2012 10:40 AM (Author)

    Hi K Utby, as a matter of fact I'm updating this tutorial for Orchard 1.4, taking advantage of Projector and Autoroute. I highly recommend diving into 1.4 instead of 1.3, as there are quite some breaking changes between the two versions. Please expect an updated tutorial by the end of May.

  • ... Posted by pubgrub Posted 05/02/2012 05:57 AM
    <p>Great to hear Sipke -- you'll be pleasing a lot of saplings out there mate, well done! <br>PS&gt; Will this revised edition include the final parts 12-15 [or thereabouts]?</p>
  • ... Posted by Sipke Schoorstra Posted 05/02/2012 12:12 PM (Author)

    That's great to hear as well, thanks! As for 12-15, I'm afraid the revised version will not include these parts, sorry. I hope to find some more time later this year and complete the series.

  • ... Posted by Gary Burke Posted 05/04/2012 06:53 AM [http://www.tatvasoft.com/]

    Your tutorials are really proving helpful to me, I am learning lot from it. I was working on the same application but unfortunately it didn't worked. Finally from your tutorial I understand to where exactly I was lacking and was making a mistake.

  • ... Posted by Roshni Posted 05/23/2012 12:58 AM

    Hi good sir, I wish to start developing 1.4.2 Orchard websites and the idea of your updated tutorial sounds beyond wonderful. Are you still releasing this update before the end of this month? Very much look forward to your response, thanking you Roshni.

  • ... Posted by Sipke Schoorstra Posted 05/24/2012 08:17 PM (Author)

    Hi Roshni, glad to hear it. As it is now it looks like it will take a week or two longer than expected. But, I will certainly try to make it in time.

  • ... Posted by Roshni Posted 05/28/2012 01:24 AM

    Thank you, I be very excited! Look forward to learning from your great text.

  • ... Posted by Guest1 Posted 06/26/2012 08:50 PM

    Any reason this tutorial ends at part 11?

  • ... Posted by Sipke Schoorstra Posted 06/26/2012 09:15 PM (Author)

    No particular reason other than having too little time to complete it.. However, things are changing for me (for the better), and I definitely intend to complete it. The first thing I'll do, however, is to revise the existing posts so they're updated for 1.4 (or probably 1.5 by then).

  • ... Posted by Guest1 Posted 06/27/2012 07:56 PM

    Great! Can't wait as we are just starting on 1.4.

  • ... Posted by donzauker78 Posted 08/30/2012 08:16 AM

    Excellent job, man. I only have an issue when updating shopping cart by knockout/ajax. Antiforgery check fails even if I add __RequestVerificationToken value to params (I found some advices in orchard forums). Perhaps the check routine don't like Json POST or similar, I can't understand if token value sent to server is uncorrect because I get a generic error.

  • ... Posted by Tom Posted 08/31/2012 03:51 AM

    What would we do if we needed to then change the layout for when a product is listed as part of a widget out of interest?

  • ... Posted by zgofmontreal Posted 09/25/2012 01:52 AM
    <p> hi i modified and finished all ur webshop to part 11,i'd like to publish it to <a href="http://codeplex.com" rel="nofollow">http://codeplex.com</a> . what do u think? andy</p>
  • ... Posted by Sipke Schoorstra Posted 09/25/2012 03:42 AM (Author)

    I think that's great! It will make it easier for everyone to download and help improve the module. Let me know when it's there. Will you create a Nuget package as well and upload it to the gallery?

  • ... Posted by zgofmontreal Posted 09/26/2012 02:51 AM
    <p>hi i added u (<br>sfmskywalker in codeplex?) as a <br>Coordinator at <br><a href="http://skywalkerwebshop.codeplex.com" rel="nofollow">http://skywalkerwebshop.codepl...</a><br>pls give me a feedback.<br>also pls choose a license </p> <p>thanks<br>andy</p>
  • ... Posted by zgofmontreal Posted 09/26/2012 02:51 AM
    <p>hi i added u (<br>sfmskywalker in codeplex?) as a <br>Coordinator at <br><a href="http://skywalkerwebshop.codeplex.com" rel="nofollow">http://skywalkerwebshop.codepl...</a><br>pls give me a feedback.<br>also pls choose a license </p> <p>thanks<br>andy</p>
  • ... Posted by Sipke Schoorstra Posted 09/26/2012 03:10 AM (Author)

    Great stuff, thanks! I can't pick a license for some reason, so you choose. Just select the most liberate one. I saw that you pushed the bin and obj folders, you may want to remove those. Use the .hgignore file that comes with Orchard. I will help whenever I can. Please share your thoughts as to what you think the goal of this project should be. E.g. should it be course material, or should it become the defacto standard commerce module with more features to come? Or will you be using it for your specfic needs?

  • ... Posted by derek kowald Posted 12/11/2012 02:59 AM

    Thank you so much for this series! I've been struggling to learn the ins-and-outs of Orchard; your blog has been fantastic resource.

  • ... Posted by stalker313 Posted 01/21/2013 03:31 PM

    Excellent! This helped me a lot!

  • ... Posted by Bobt59 Posted 11/07/2014 08:05 AM

    I've been following along and this is a great article! However, I had a problem and ther must be somthing I'm doing wrong. I add my UpdateFrom2() method to the migrations.cs class then open up the web, sign in and go to the dashboard and update the Webshop module. Then I see the Product Catalog in the New area. I click on it but the page I get is not like the one you show. I do not see the "Publish Now" button; I do not see the Title or Permalink button either. I do see both the "Show on Main Menu" and "Show on admin menu" check boxes.

    I thought that maybe the migrations.cs UpdateFrom2() method should have included a .WithPart("CommonPart") and .WithPart("RoutePart") so I added a new UpdateFrom3() method to add that, but it didn't work; I still get the same page. Everything else up to there came out exactly as the tutorial.

    I'm going to keep looking for my mistake, but maybe it's simple oversite and you know exactly what it is. Thanks

  • ... Posted by Bobt59 Posted 11/07/2014 08:05 AM

    I added one more update in the migrations class and this got me most of it, but I'm still not seeing the "Publish" button at the bottom next to the "Save" button. But I get the rest. Do I need to include the .WithPart("Common") and .WithPart("RoutePart") in my method? It appears that those were not there when I followed the tutorial so I don't know how you got them. Maybe I don't have them spelled correctly? Maybe that's why the Publish button doesn't appear yet? I copied an image of what my method looks like (below)

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:05 AM [http://www.ideliverable.com]

    I'm not sure what's causing the Publish button not to appear, but I'll include the source of the module so you can try it with a clean installation and perhaps resolve it. Perhaps I omitted something in the tutorial which I did in the project itself.

  • ... Posted by Bobt59 Posted 11/07/2014 08:05 AM

    Hi again. I'm following along in the code and ran up against another problem. In the section where you add the ViewModels folder and the UpdateShoppingCartItemVM class, I kept gettin a compile error saying:

    'Orchard.Webshop.Services.IShoppingCart' does not contain a definition for 'UpdateItems' and no extension method 'UpdateItems' accepting a first argument of type 'Orchard.Webshop.Services.IShoppingCart' could be found (are you missing a using directive or an assembly reference?) H:\BT.CMS\BT.OrchardWebshopDemo\src\Orchard.Web\Modules\Orchard.Webshop\Controllers\ShoppingCartController.cs 97 27 Orchard.Webshop

    I fixed this by adding a method definition to the IShoppingCart interface for UpdateItems by adding: void UpdateItems();

    My errors went away after that. Just wondering if that is the correct fix. I searched and searched the tutorial but I did not see where you indicated adding that to the interface.

    This is the best article I've seen to date on how to use Orchard. Thanks for doing this!

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:06 AM [http://www.ideliverable.com]

    The IShoppingCart interface should have an void UpdateItems() method, so your fix is correct. I accidentally omitted that step. Thanks for letting me know!

  • ... Posted by nxcong Posted 11/07/2014 08:06 AM

    I saw a problem about : HttpContext.Session["ShoppingCart"],when I delete a product,in session still have one,session has not been updated.

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:06 AM [http://www.ideliverable.com]

    I see. Please check the attached sourcecode to see if you're using the correct UpdateItems implementation. Let me now if you're still having problems.

  • ... Posted by Bobt59 Posted 11/07/2014 08:07 AM

    I hope I am not missing something, but I'm continuing on with following the tutorial and updating my code. After the section where you add the MVC3Futures, I was getting build errors because the IShoppingCart interface had no definition for Clear and AddRange. Again, I don't know if this is the correct fix, but i added these two definitions to the IShoppingCart class:

    void Clear();

    void AddRange(IEnumerable<shoppingcartitem> items);

    And then in the ShoppingCart class I implemented those two methods by changing the

    private void Clear(){...} to public void Clear(){...}

    and then adding this AddRange method:

    public void AddRange(IEnumerable<shoppingcartitem> items) { foreach (var item in items) { this.Add(item.ProductId, item.Quantity); } }

    This eliminated the compile errors but I still got a couple of exceptions. The first was due to not having Microsoft.Web.Mvc.dll in the bin folder of the Orchard.Web application (it was in the Orchard.Webshop\bin folder) so I copied Microsoft.Web.Mvc.dll to the Orchard.Web\bin folder.

    Nothing blew up after these changes so I'm continuing on. If anyone else is seeing this, I hope my comments will help. If I am wrong in what I'm doing, I hope someone will be kind enough to tell me.

    Again, thanks for this wonderful tutorial! Bob

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:07 AM [http://www.ideliverable.com]

    Those are the correct fixes. I'll update this and the previous posts with these missing steps. With regards to the exception, copying Microsoft.Web.Mvc.dll to Orchard.Web\bin should indeed work. Alternatively, you could copy the file to Orchard.Web\App_Data\Dependencies. I believe that the Orchard module installer will copy dependencies to that folder.

    Glad to see you got it working, and thanks for sharing your findings.

  • ... Posted by Bobt59 Posted 11/07/2014 08:07 AM

    I'm afraid I may have messed things up. I am not able to see any indication that GetItems is ever being invoked. I put a breakpoint at the beginning of the GetItems() method in the SHoppingCartController. I have the [AjaxOnly] attribute in front of the GetItems() method, but it never gets here. Perhaps my fixes (in my earlier comments) are not meant to be.

    Thanks, Bob

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:07 AM [http://www.ideliverable.com]

    It's probably something small. I'll be posting the source up until part 7 tomorrow, so you could compare it with your version. In the mean time, maybe you could use F12 Developer Tools or FireBug to see if any javascript errror occurs or if the call returns an HTTP error.

  • ... Posted by Bobt59 Posted 11/07/2014 08:08 AM

    I found the problem. I had NOT copied the web.config file into my Scripts folder. You were correct! it was something simple. As you suggested, I used the F12 tools to discover that my scripts were not being found - thus always returning a 404 error. Thanks again for your excellent work! Bob

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:08 AM [http://www.ideliverable.com]

    You're welcome! Great to see that you have worked it out. In any case, you may download the sourcecode via the link at the top of this post.

  • ... Posted by nxcong Posted 11/07/2014 08:08 AM

    data-update-shoppingcart-url="@Url.Action("Update", "ShoppingCart", new { area = "Orchard.WebShop" })" A required anti-forgery token was not supplied or was invalid.

  • ... Posted by nxcong Posted 11/07/2014 08:09 AM

    I had resolved this problem.Reference : http://orchard.codeplex.com/discussions/249394

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:09 AM [http://www.ideliverable.com]

    Thanks for bringing that up! I haven't come cross this issue yet, but I can imagine that the Anti Forgery filter in Orchard will at some stage throw an exception when a user is authenticated, so the antiforgery token should be posted as well.

  • ... Posted by Leo Furze-Waddock Posted 11/07/2014 08:10 AM

    Looks like a solution at the following link: https://orchard.codeplex.com/discussions/406844

  • ... Posted by nxcong Posted 11/07/2014 08:10 AM

    //AntiForgeryToken.js

    //----------------------------- /// <reference path="jquery-1.4.2.js"/>

    (function ($) { $.getAntiForgeryToken = function (tokenWindow, appPath) { // HtmlHelper.AntiForgeryToken() must be invoked to print the token. tokenWindow = tokenWindow && typeof tokenWindow === typeof window ? tokenWindow : window;

    appPath = appPath && typeof appPath === "string" ? "_" + appPath.toString() : ""; // The name attribute is either _RequestVerificationToken, // or RequestVerificationToken{appPath}. var tokenName = "RequestVerificationToken" + appPath;

    // Finds the <input type="hidden" name="{tokenName}" value="..."/> from the specified window. // var inputElements = tokenWindow.$("input[type='hidden'][name=' + tokenName + "']"); var inputElements = tokenWindow.document.getElementsByTagName("input"); for (var i = 0; i < inputElements.length; i++) { var inputElement = inputElements[i]; if (inputElement.type === "hidden" && inputElement.name === tokenName) { return { name: tokenName, value: inputElement.value }; } } };

    $.appendAntiForgeryToken = function (data, token) { // Converts data if not already a string. if (data && typeof data !== "string") { data = $.param(data); }

    // Gets token from current window by default. token = token ? token : $.getAntiForgeryToken(); // $.getAntiForgeryToken(window).

    data = data ? data + "&" : ""; // If token exists, appends {token.name}={token.value} to data. return token ? data + encodeURIComponent(token.name) + "=" + encodeURIComponent(token.value) : data; };

    // Wraps $.post(url, data, callback, type) for most common scenarios. $.postAntiForgery = function (url, data, callback, type) { return $.post(url, $.appendAntiForgeryToken(data), callback, type); };

    // Wraps $.ajax(settings). $.ajaxAntiForgery = function (settings) { // Supports more options than $.ajax(): // settings.token, settings.tokenWindow, settings.appPath. var token = settings.token ? settings.token : $.getAntiForgeryToken(settings.tokenWindow, settings.appPath); settings.data = $.appendAntiForgeryToken(settings.data, token); return $.ajax(settings); }; })(jQuery);

    //Edit file shoppingcart.js //.... var saveChanges = function () {

    var token = $.getAntiForgeryToken().value; var data = $.Enumerable.From(shoppingCart.items()).Select(function (x) { return { productId: x.id, quantity: x.quantity() }; }).ToArray(); var url = $("article.shoppingcart").data("update-shoppingcart-url"); var config = { url: url, type: "POST", data: data ? JSON.stringify(data) : null, dataType: "json", contentType: "application/json; charset=utf-8", __RequestVerificationToken:token }; $.ajax(config); }; //.... //Edit file ResourceManifest.cs //... manifest.DefineScript("AntiForgeryToken").SetUrl("AntiForgeryToken.js").SetDependencies("jQuery"); manifest.DefineScript("Webshop.ShoppingCart").SetUrl("shoppingcart.js").SetDependencies("jQuery", "KnockoutJS", "LinqJS", "AntiForgeryToken", "Globalize.Cultures"); //....

  • ... Posted by skizNat Posted 11/07/2014 08:10 AM

    Has this changed for 1.4? I can't seem to get past this. Either I can supply the antiforgerytoken but then the model binding doesn't seem to work, or I can submit the data but run into the antiforgerytoken issue. I've used the code above to no avail.

  • ... Posted by skizNat Posted 11/07/2014 08:11 AM

    I've updated the js method to the following (using the antiforgerytoekn.js):

    var data = $.Enumerable.From(shoppingCart.items()).Select(function (x) { return { ProductId: x.id, Quantity: x.quantity(), IsRemoved: false }; }).ToArray(); var url = $("article.shoppingcart").data("update-shoppingcart-url"); $.post(url, { items: data, __RequestVerificationToken: $.getAntiForgeryToken().value });

    However, the model binding contains an array of items that are all empty (productid:0, quantity:0). Am I just overlooking something simple?

  • ... Posted by Rhys Lloyd Posted 11/07/2014 08:11 AM

    Hi mate, were you able to sort this issue? I have the exact same problem.

  • ... Posted by David Posted 11/07/2014 08:11 AM

    Enjoying your tutorial thank you. http://curiousdeveloper.wordpr... makes the case that drivers in modules should include 'import' and 'export'. Is the omission to keep the tutorial simple, or because you have a different view of the matter? Thanks.

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:12 AM [http://www.ideliverable.com]

    Glad to hear it! To be honest, I didn't know what the use of the Identity part was until you pointed it out. Based on that post, I think it's a good idea to attach the Identity part to any content type that is not routable. Thanks for pointing this out!

  • ... Posted by Ha Doan Manh Posted 11/07/2014 08:12 AM

    Thank you for the tutorials

  • ... Posted by Jgross Posted 11/07/2014 08:14 AM

    Hello Sipke, it's a great tutorial. I enjoy it ver much. But in the New Product Ctalog, i did not see the title section so that I can't enter a title for the new Product Catalog. I did the things exactly as you described in the UpdateFrom2-Method. What's wrong? Best regards

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:14 AM [http://www.ideliverable.com]

    Thanks Jgross. I had a look at my installation, and I'm afraid I forgot to include the line in UpdateFrom2 method that attaches the RoutePart to the ProductCatalog. I added the RoutePart via the Admin so that it had a name and slug. I'll update this post to include that line, as it is quite confusing without it. Thanks for letting me know!

  • ... Posted by C M G Posted 11/07/2014 08:14 AM

    I deleted the Product Catalog content type and then checked in all of the database tables to make sure that those tables that mentioned content type in their names had no mention of the Product Catalog. Then I added the following line to my UpdateFrom2 function:-

    .WithPart(typeof(RoutePart).Name)

    Finally I edited the OrchardFrameworkDataMigrationRecord table to set the version number back to 2.

    I don't know if there is an easier way to back out the changes nor do I know if I've done it correctly. I do know that I could add books to my new product catalog and then see them on the Books menu option.

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:14 AM [http://www.ideliverable.com]

    That should work just fine. Another method is to create another UpdateFromX method, run it via the admin. You could also directly update the UpdateFrom2 method by including the RoutePart. Although Orchard will not perform the change for you, you then manually add the RoutePart via the admin.

  • ... Posted by C M G Posted 11/07/2014 08:14 AM

    Thanks for that Spike.

    I'm new to Orchard so it's nice to know that I'm not barking up the wrong tree!

    Great tutorial by the way so thanks for that too.

  • ... Posted by Andr3wll Posted 11/07/2014 08:15 AM

    Very good tutorial and great for learning! So far so good, only when I try the Dashboard > Books Catalog and click 'Choose Items' to add the 3 books it gave me 'Object reference not set to an instance of an object' error. Then I modified the 'Product Catalog' content type and add 'Common' content part to it and it works. I don't know maybe it just me.

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:15 AM [http://www.ideliverable.com]

    Thanks, glad to hear! I couldn't reproduce the problem you mentioned, but I just saw that I also added the CommonPart to the ProductCatalog. I ran into the same situation in another section. Anyway, I'm glad you figured it out.

  • ... Posted by Ned Stoyanov Posted 11/07/2014 08:15 AM

    Is there any chance you can post the source code? I get an exception when in Part 5 i try and add items to the book list. It looks like it happens when it tries to execute item.As<commonpart>, I tried adding the common part to the Migration.cs file UpdateForm2 method but it didn't work.

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:16 AM [http://www.ideliverable.com]

    Hi Ned, you can download the source up until part 9 from here: http://skywalkersoftwaredevelo...

    You should be able to manually add the CommonPart to the ProductCatalog content type using the admin UI. Migrations should absolutely work, make sure that you're welding CommonPart on to the ProductCatalog content type.

  • ... Posted by Claire Botman Posted 11/07/2014 08:16 AM

    Hi Sipke, you have a piece missing from your code for UpdateFrom2() above. There should be a .WithPart(typeof(CommonPart).Name) as per your source code, otherwise we get an exception as Ned said. Working through your tutorials & learning lots, thanks for taking the time to give us this great resource.

  • ... Posted by nxcong Posted 11/07/2014 08:17 AM

    Hi,I get an error : var customer = currentUser.ContentItem.As<customerpart>(); "Orchard.ContentManagement.ContentItem' does not contain a definition for 'As' and no extension method 'As' accepting a first argument of type 'Orchard.ContentManagement.ContentItem' could be found (are you missing a using directive or an assembly reference?)".Help me,please.Tks

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 08:17 AM [http://www.ideliverable.com]

    'As' is an extension method which exists in the Orchard.Framework assembly, so make sure you have a reference to that project. You also need to add a using statement for the 'Orchard.ContentManagement' namespace (since that's where 'As' is defined).

  • ... Posted by nxcong Posted 11/07/2014 08:17 AM

    Thanks.I have done.:)

  • ... Posted by Davi Posted 11/07/2014 08:17 AM

    hi, get an error when I click in the "Choose Items":

    Object reference not set to an instance of an object.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

    Source Error:

    Line 7: Layout.Title = T("Choose Items"); Line 8: Line 9: var targetContainers = ((IEnumerable<contentitem>)Model.Containers).Select( Line 10: contentItem => new SelectListItem { Line 11: Text = T("Move to {0}", contentItem.ContentManager.GetItemMetadata(contentItem).DisplayText ?? contentItem.ContentType).ToString(),

    Source File: d:\2TECNOLOGIA\ASP.NET\ORCHARDTRAINING\src\Orchard.Web\Modules\Orchard.Lists\Views\Admin\Choose.cshtml Line: 9

    Can some one help me

  • ... Posted by Mikael Östberg Posted 11/07/2014 08:18 AM

    The problem is that the Product Catalog Content Type is missing the CommonPart. Either add it though the Admin interface by Content -> Content Types -> Product Catalog -> Edit and the choose Add Parts. Tick the Common checkbox and save.

    Or, which I think is the way it should have been done, add .WithPart(typeof(CommonPart).Name) to the code which defines it, like so

    ContentDefinitionManager.AlterTypeDefinition("ProductCatalog", type => type .WithPart(typeof(CommonPart).Name) .WithPart(typeof(ContainerPart).Name) .WithPart(typeof(MenuPart).Name) .WithPart(typeof(AdminMenuPart).Name) .WithPart(typeof(RoutePart).Name) .Creatable() );

  • ... Posted by Davi Posted 11/07/2014 08:18 AM

    Thanks ! It works fine...

  • ... Posted by Paul Leuben Posted 07/04/2015 11:11 AM [https://www.calipus.in]

    A very nice and detailed information about orchard, i have really learned a lot from you sipke.

  • ... Posted by omid nasri Posted 02/26/2016 07:24 AM [http://www.omidnasri.com]

    Hi, Thank you for this tutorial.

Leave a comment