Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jason 17 posts 37 karma points
    Jul 10, 2015 @ 17:33
    Jason
    0

    Parent layout wants a RenderModel, but I want to use a custom model...

    Ok, so I think I finally get how to create custom controllers and use route hijacking enough to tell someone else how to get a basic one going. ;)

    I've created a minimal model, controller, and partial view. I have my document type and template, and it all generally seems to work nicely together. My model gets set up in the hijacked Index method in my controller and passed to my view, and I can display the properties I set. I was so excited to finally get that much working.

    Where I start to have trouble now is when I use another layout in my template. On our site, we have a "Master" layout, a "Submaster" layout, and then most of the other templates use Submaster.

    When my custom model/controller (what is the right terminology? haha) has Layout = null, it's unstyled, but it shows up on the page.

    When I use Submaster as my layout, I get this error: The model item passed into the dictionary is of type 'Models.MembersPageModel', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.

    What I think that means is that because SubMaster inherits UmbracoTemplatePage, it's what wants the RenderModel, even though I'm not referencing any model in SubMaster and my MembersPage template is inheriting Umbraco.Web.Mvc.UmbracoViewPage

    I've looked at changing things around so my model inherits from RenderModel, but then I get an error about my model not having a constructor that takes 0 arguments, but then I'm not sure which arguments I'd need to pass in.

    So - how do I nest layouts that use two different types? Changing what SubMaster inherits isn't going to be ideal, because we're creating several "plugins" like this with different models.

    What am I missing?

  • Jason Prothero 422 posts 1243 karma points c-trib
    Jul 10, 2015 @ 18:22
    Jason Prothero
    0

    You will need the model under Subtemplate to inherit from the subtemplate model.

    Remember that both templates get rendered. The subtemplate parent will need its model to render the razor on its page, then the inherited template (news, etc) from subtemplate will need its specific model. However, that all needs to be wrapped up in one model.

    Does that make sense?

    A good example of this is the Hybrid Framework project. However, one caveat is the most recent versions of that are more "Starter Kit" than "custom routing example" so it may be hard to work through exactly what their doing.

    I can post some simpler examples if you need more help.

    Thanks, Jason

  • Jason 17 posts 37 karma points
    Jul 10, 2015 @ 20:17
    Jason
    0

    Jason, I'd downloaded and messed around in Hybrid Framework, and I wasn't able to make much sense out of it in order to know what direction I should go in. I was looking for "custom routing example", and I couldn't extrapolate one. ;)

    If you have some simpler examples to share, that would be awesome.

    In the end, once I have this all working, I think I need to write a blog post about how to do it from start to end. Heh.

    Thanks! -j

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 10, 2015 @ 18:25
    Rusty Swayne
    0

    In your SubMaster try inheriting from UmbracoViewPage

    The constructor for these would be

        public MyViewModel(IPublishedContent content)
            : base(content)
        { 
        }
    

    Where the IPublishedContent would come from the RenderModel model

        public override ActionResult Index(RenderModel model)
        {
             // This is the constructor arg
              var content = model.Content;
        ...
    
  • Jason 17 posts 37 karma points
    Jul 14, 2015 @ 19:45
    Jason
    0

    Just wanted to follow up with what I've done to make this thing work.

    So in the end, since I'm inheriting a site that already had a bunch of work done on it, I didn't want to change what was already there if I could help it. There is one master template, a submaster, and then the layouts below those. So three layers of templates to inherit things from. I decided to stick with RenderModel.

    I changed my model to inherit from RenderModel, and the main error I was getting was that RenderModel doesn't have a constructor with 0 parameters. So I'm just sending the model's Content property into my constructor when I declare my model. I also had to add a constructor to my model that accepts an IPublishedContent argument.

    To break it down:

    • SubMaster inherits from UmbracoTemplatePage still. No change there.
    • Child template inherits UmbracoViewPage
    • Child template uses SubMaster as the Layout
    • MembersPageModel inherits from RenderModel
    • Added a constructor for MembersPageModel to accept one IPublishedContent argument, pretty much exactly what Rusty wrote.

    I appreciate the help, guys. You helped me through a dark time in my new job, and I feel like I've learned a lot. Hopefully I can help someone else who's coming to this stuff new as well. :)

    -j

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jul 14, 2015 @ 21:40
    Rusty Swayne
    0

    It feels a bit odd to me to be mixing dynamics and typed content: UmbracoTypePage (dynamic) / UmbracoViewPage (typed) and myself would tend to pick one of the other.

    I've seen good setups with UmbracoTemplatePage views and UmbracoViewPage

    Mixing the two in your views may be a bit like manipulating things to wind up with what Umbraco does for you - but then again you may be trying to do this for some reason understandable from the information in this thread.

    Is it that the site you inherited has typed models and you're trying to get your head around how they are working or are you trying to add something new and follow the existing pattern?

  • Jason 17 posts 37 karma points
    Aug 14, 2015 @ 20:45
    Jason
    0

    A month later, and I'm still playing with this. :)

    We eventually figured out our initial problem, but now we're running into similar ones because now we're creating views that need to display values after submitting a form.

    Rusty, to answer your question at the end, I'm trying to add something new and follow the pattern. Before I got involved, there was none of this stuff. Umbraco was just allowed to do what it does, and they had been adding macros via the editor. Really simple stuff.

    But now we're creating these tools that are almost like plugins, and we're following the paradigm of creating the Document Type to be the Controller name, and using the Template as the View that calls the partial view to pull it under the Umbraco umbrella.

    And now my issue is that I get my view to render nicely, but when I submit the form, the model state is wiped out. I know exactly why:

    Html.RenderPartial("~/Views/Partials/MeldCalculator.cshtml", new MeldCalculatorModel() {});
    

    When I do things like that, obviously each time the MeldCalculator partial is called, it's going to be getting a brand new model.

    If I take out the new MeldCalculatorModel call, the page won't load and I get the error that says I'm passing a RenderModel but it wants a MeldCalculatorModel.

    Every piece of what I'm trying to do works just fine until I want to send my new model values back to my view. If I return the partial after my form submit action, I get my view with the new values populated, it's just not part of the site (clearly).

    return PartialView("~/Views/Partials/MeldCalculator.cshtml", _model);
    

    When I use CurrentUmbracoPage() or View("MeldCalculator", _model), the model is nulled out by the new MeldCalculatorModel in my RenderPartial call.

    I've got to be missing something quite elementary with this. I just can't figure out what it is, nor can my teammates!

    Edit: I tried some shenanigans, and I found out that even when I pass my MeldCalculatorModel to the View, the type of Model is RenderModel, even before I send in the default new MeldCalculatorModel. Why would it be RenderModel if I'm passing it in explicitly?

    -j

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 14, 2015 @ 22:45
    Rusty Swayne
    0

    Hi Jason,

    I think you might want to try to use an Html.Action to render your form rather than RenderPartial.

  • Jason 17 posts 37 karma points
    Aug 17, 2015 @ 16:58
    Jason
    0

    Thanks, Rusty. I'll need to check out Action to see how better to make it work. :) I read up on it, and I think I see how it would be used, but I still am not sure how to get away with the model difference, because I still need to know whether or not the model I'm passing is the first time the page loads or a result of the form submit. There are a couple things I'm thinking about to try. Nearly there!

    -j

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Aug 17, 2015 @ 22:16
    Rusty Swayne
    0

    Generally you would have two methods in your surface controller, one to render the form and then one to handled the form submit.

    [PluginController("MyApp")]
    public class MembershipController : SurfaceController
    {
    
        [ChildActionOnly]
        public ActionResult RenderSignInForm(SignInModel model)
        {
            return PartialView("SignInForm", model));
        }
    
        [HttpPost]
        [ValidateAntiForgeryToken]
       public ActionResult HandleSignIn(SignInModel model)
       {
            if (!ModelState.IsValid) return this.CurrentUmbracoPage();
    
            if (Members.Login(model.Login.Username, model.Login.Password))
            {
                // successful login
                return this.RedirectToUmbracoPage(model.AccountPageId);
            }
    
            // unsuccessful login
            return this.CurrentUmbracoPage();
       }
    }
    

    The "SignInForm" partial would look something like:

            @{
            using (Html.BeginUmbracoForm<MembershipController>("HandleSignIn", new { area = "MyApp" }, new { @class = "form-horizontal" }))
            {
                <fieldset>
                    <legend>Login</legend>
                    @Html.AntiForgeryToken()
                    <div class="control-group">
                        @Html.LabelFor(x => x.Login.Username, new { @class = "control-label" })
                        @Html.ValidationMessageFor(x => x.Login.Username)
                        <div class="controls">
                            @Html.TextBoxFor(x => x.Login.Username, new { @placeholder = "Your email address" })
                        </div>
                    </div>
                    <div class="control-group">
                        @Html.LabelFor(x => x.Login.Password, new { @class = "control-label" })
                        @Html.ValidationMessageFor(x => x.Login.Password)
                        <div class="controls">
                            @Html.PasswordFor(x => x.Login.Password, new { @placeholder = "Your password" })
                        </div>
                    </div>
                    <div class="control-group">
                        <div class="controls">
                            @Html.HiddenFor(x => x.AccountPageId)
                            <label class="checkbox">
                                @Html.CheckBoxFor(x => x.Login.RememberMe) Remember Me
                            </label>
                            <button type="submit" class="btn">Login</button>
                        </div>
                    </div>
                </fieldset>
            }
        }
    
Please Sign in or register to post replies

Write your reply to:

Draft