Copied to clipboard

Flag this post as spam?

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


  • Lars Pedersen 8 posts 58 karma points
    Sep 11, 2014 @ 21:50
    Lars Pedersen
    1

    How to create a link to a SurfaceController action

    As I am new to Umbraco, I have not quite understood the routing mechanisms it seems.

    I have a custom surface controller (myProject.Controllers.CompanySurfaceController) with a getCompanyList() and getCompany(int companyId) function.

    The basic idea is to get the list of companies from the db, render the partial view with classic <a> links to the getCompany(..) function and retrieve/display that company from the db.

    Everything is working fine except one thing: I cannot grasp how to create the <a> links to the child action of the controller! I have no problem including child actions in partial views when POSTing and using Html.Action.

    I have tried @Html.ActionLink and other helpers but the closest I get to, is a link for /umbraco/Surface/CompanySurface/Company, which doesn't work of course and it does not include the id parameter (e.g. Company/3). I have also tried to put the controllers in the umbraco/Surface namespace without luck (and it does not seem necessary).

    What am I missing here?

    Your help would be much appreciated on my steep hill climp here!

  • Steve Morgan 1349 posts 4459 karma points c-trib
    Sep 15, 2014 @ 11:57
    Steve Morgan
    0

    Hi Lars,

    Can I just check that you're getting data from a third party DB / set of tables? Just want to check you're not just trying to get data from Umbraco itself (which you can do with much more simple vanilla Razor script). It can be easy to get a bit confused when you start with Umbraco so sorry to ask a basic question.

    If you're getting it from a set of custom tables / custom DB then I just want to double check first that a SurfaceController is the best course for you. They are the best for choice forms - getting data from the web visitor and validating etc (e.g. blog comments, sign ups that kind of thing). I wouldn't use them for just rendering data though there are people that do that (as they use them for AJAX forms and filters etc)!

    I prefer to get data via an API Controller and then call this from a Razor Partial View Macro... this also makes it easy to use AJAX on your pages as you can return JSON. These also "auto route" so are reletively easy to implement.

    There's a good section in the videos (if I'm understanding your requirements properly the example is very similar to what you want to do):

    http://umbraco.tv/videos/developer/fundamentals/api-controllers

    Or in the documentation:

    http://our.umbraco.org/documentation/reference/webapi/ ;

  • Alex Skrypnyk 6175 posts 24186 karma points MVP 8x admin c-trib
    Sep 15, 2014 @ 12:43
    Alex Skrypnyk
    0

    Hi Lars,

    SurfaceController are used mostly for submitting forms. For Accessing for data Umbraco has API as said Steve, Render controllers, content services.

    Thanks

  • Steve Morgan 1349 posts 4459 karma points c-trib
    Sep 15, 2014 @ 13:43
    Steve Morgan
    0

    Re-reading your post .. it does sound like you've got the companies as nodes in Umbraco.. if that is what you're doing then you just need a Partial View Macro listing the companies (no code files!). Read through this example where it lists the child article nodes http://our.umbraco.org/documentation/Using-Umbraco/Creating-Basic-Site/Articles-Parent-and-Article-Items

  • Lars Pedersen 8 posts 58 karma points
    Sep 15, 2014 @ 14:58
    Lars Pedersen
    1

    Hi Steve and Alex and thanks for helping out!

    I am using petaPOCO to get data from the local umbraco db (with my own tables) so that should be very basic. I have a simple Model with attributes such as [columnname], [ignore], etc. and using the umbraco db context in my controllers: ApplicationContext.DatabaseContext.Database.

    This works like a charm and I can retrieve, insert and update my Models with no issues.

    As you said, SurfaceControllers are good for forms, but that is also what I am trying to do in my opinion (editing and inserting companies)

    The problem comes when creating links and posting forms in the Umbraco routing system which does not work as normal MVC and I get confused :-)
    Let's take the short version (many features left out) of my issue:

    Model:
    namespace myProject.Models {
        public class Company
        {
            [HiddenInput(DisplayValue = false)]
            public int companyId { get; set; }

            public string companyCode { get; set; }

            public string company { get; set; }

        }
    }

    Controller:
    namespace MyProject.Controllers
    {
        public class CompanySurfaceController : SurfaceController
        {
            public ActionResult GetList()
            {
                //Get the Umbraco db
                var db = ApplicationContext.DatabaseContext.Database;

                //Get an IENumberable of BlogComment objects to iterate over
                var allCompanies = db.Query("SELECT * FROM company");

                //return the list
                return PartialView("_companyList", allCompanies);
            }

            public ActionResult Get(int param_model)
            {
                //Get the Umbraco db
                var db = ApplicationContext.DatabaseContext.Database;

                //Get a single record
                Company result = db.SingleOrDefault("SELECT * FROM company WHERE companyId=@0", param_id);

                return PartialView("_companyForm", result);
            }

            [HttpPost]
            public ActionResult Set(Company model)
            {
                //model not valid, do not save, but return current umbraco page
                if (!ModelState.IsValid)
                {
                    ViewBag.CustomMessage = "Validation failed";
                    return CurrentUmbracoPage();
                }

                //Get the Umbraco db
                var db = ApplicationContext.DatabaseContext.Database;

                //Add the object to the DB
                if (param_model.companyId > 0)
                {
                    db.Save(param_model);
                   
                    TempData.Add("CustomMessage", "company saved successfully");
                }
                else
                {
                    db.Insert(param_model);
                    TempData.Add("CustomMessage", "company inserted successfully");
                }

                return RedirectToUmbracoPage(1051);
            }
        }
    }
    ----
    View (Company.cshtml):
    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @{
        Layout = "Master.cshtml";
    }
    @Html.Action("GetList", "CompanySurface")
    ----
    View (Partials/_companyList.cshtml):
    @inherits Umbraco.Web.Mvc.UmbracoViewPage>
    @{
        Layout = null;
    }
        @foreach (var p in Model)
        {
           


    •    //THIS IS WHERE I WOULD NORMALLY JUST LINK TO /Controller/Action/Id BUT I DO NOT KNOW HOW
         //BELOW CREATES A LINK TO /umbraco/Surface/Company which does not work
         @Html.ActionLink(p.company, "Get", "CompanySurface", @p.companyID, null)@
             


        }
    ----
    View (Partials/_companyForm.cshtml):
    @inherits Umbraco.Web.Mvc.UmbracoViewPage
    @{
        Layout = null;
    }
    @using (Html.BeginUmbracoForm("Set", "CompanySurface"))
    {
        @Html.EditorForModel()
    }
    ----

     

    Basically, I think I need to refactor how to process forms/links. I am also running into an issue with the _companyForm because it is not included in a View and thus cannot find the Master.cshtml.
    All in all it tells me that the design of the flow is wrong.

    Thanks!

  • Lars Pedersen 8 posts 58 karma points
    Sep 17, 2014 @ 14:48
    Lars Pedersen
    0

    @Steve: I do not have each company as a node in Umbraco. They should (hopefully) be dynamically created/edited/deleted by the Controller. I have no use for them as viewable nodes in the back office.

    Is that where I am missing the point in Umbraco, i.e. that you cannot have a custom list of items in the Umbraco db without also having to create them as nodes in the back office? I certainly do not hope so, as it would not make sense.

  • Tim Anderson 20 posts 83 karma points
    Sep 17, 2014 @ 16:03
    Tim Anderson
    0

    I've only scanned the thread, so if I'm out of context slightly I apologise.

    Like you I wanted to link to a child action in a surface controller and found that the Url returned in the view was non-routable like you have found. In the end I got round this with a bit of route hijacking....

    This way I could render a link to a controller action from a view using the @Html.ActionLink helper method.

    I created a controller like so...

    public class MyHijackedController : UmbracoController
    {
        public ActionResult DoSomething(String myParam, String returnUrl)
        {
            /* Do something.... */
    
            String url = HttpUtility.UrlDecode(returnUrl.Replace("--", "%"));
            return Redirect(url);
        }
    }
    

    Then I registered the custom route with Umbraco so that it knew how to route requests to my controller...

    public class MyUmbracoHandler : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            base.ApplicationStarted(umbracoApplication, applicationContext);
    
            RouteTable.Routes.MapRoute(
                "umbMyRoute",
                "customroute/{action}/{id}/{returnUrl}",
                new
                {
                    controller = "MyHijacked",
                    action = "DoSomething",
                    myParam = String.Empty,
                    returnUrl = "/"
                });
        }
    }
    

    I could then call the child action from my views using the @Html.ActionLink syntax like so....

    @{
        var routeValues = new RouteValueDictionary();
        routeValues.Add("controller", "MyHijacked");
        routeValues.Add("myParam", "Value");
        routeValues.Add("returnUrl", HttpUtility.UrlEnecode("/").Replace("%", "--"));
    }           
    @Html.ActionLink("Display Text", "DoSomething", @routeValues)
    

    This returned a link to the url /customroute/myhijacked/dosomething/value/--2f which redirected to my new controller happily

    Hope this is of some relevance / help to you

    Kind regards,

    Tim

  • Tim Anderson 20 posts 83 karma points
    Sep 17, 2014 @ 16:18
    Tim Anderson
    0

    I would like to add in my scenario, which is slightly different from yours, I wanted a search result to have a collection of links which would represent a list of selections that the user could click on.

    Cicking on a link would set/save a value somewhere and then return the user back to where they initiated the search from - hence why I redirect to a return Url at the end of my controller action.

    But fundamentally the concept should be the same for you as you can just direct to the page where you render your company details from or rework the action to render that page...

    In which case you should probably read this blog post by Shannon Deminick.

  • Steve Morgan 1349 posts 4459 karma points c-trib
    Sep 17, 2014 @ 17:20
    Steve Morgan
    0

    Nice work Tim - I've been meaning to try and build a test example to see where Lars was coming from but time has not allowed - I hope your pointers steer him the right way.

    As for the content nodes needed in Umbraco - not at all, sorry that was all a red herring from me mis-reading what you were trying to do (you said you were new to Umbraco and I was trying to work out if you really needed a Surface Controller - you can never tell how people have landed on a solution!). There's no restriction in using third party data but you just need to get the Surface Controller to play nice with your model and it sounds like route hijacking is what you need. 

  • Tim Anderson 20 posts 83 karma points
    Sep 17, 2014 @ 19:35
    Tim Anderson
    0

    Thanks Steve,

    Just for completeness - there is another thread related to this that Lars is commenting on which offers another solution - which would require a content node being created in the back office and the use of an IContentFinder.

    I think fundamentally the problem in a nutshell is how do I get data from an external data source displayed on the Umbraco front end website with nice friendly Urls without having to replicate that data as content nodes in the back office.

    I've had to do a fair bit of this in a current Umbraco project so have a few techniques.

    Click here for alternative solution

  • Dan Diplo 1554 posts 6205 karma points MVP 6x c-trib
    Sep 17, 2014 @ 21:21
    Dan Diplo
    0

    I don't understand why you wouldn't want to manage stuff like companies using Umbraco? Using a custom DB seems crazy to me when you have a CMS - it is much faster to just store the content in Umbraco. You don't need to assign a template to them, either, so they will never actually be viewable. Using Umbraco you get all the CRUD stuff out-of-the-box. You get a nice interface (that is familiar to editors), you get versioning, recycle bin, and really easy integration with media pickers, rich-text editor and lots of other controls.

  • Lars Pedersen 8 posts 58 karma points
    Sep 17, 2014 @ 21:53
    Lars Pedersen
    0

    @Dan Diplo: Fair point! It could be that I am preoccupied with the classic build-from-scratch development. I am building a sort of accounting system and each of our clients could have several companies (often in a network structure), chart of accounts, hundreds of ledgers and millions of transactions. From my point of view it is easy to manage this via SQL/MVC but doing it as nodes in the back office?

    As I am still learning the trade here, what do you mean by I 'don't need to assign a template' and 'they will never actually be viewable'? Could you give an example?

    Really appreciate your help!

  • Lars Pedersen 8 posts 58 karma points
    Sep 17, 2014 @ 22:38
    Lars Pedersen
    0

    Also, looking at my database structure, the Umbraco back office hierarchy seems very rigid to describe complex structures. There is already 15+ tables with foreign keys constraints, indexes and unique value restrictions.

    It could make sense to use the content API for some master data (companies, currencies, etc) but I think I have to build the complex ones (ownership structure, financial periods, transaction rules) in my own tables anyway, and then I will still hit a wall with simple links unless I do custom routing, or what do you think?

    Thanks!

  • Dan Diplo 1554 posts 6205 karma points MVP 6x c-trib
    Sep 18, 2014 @ 21:44
    Dan Diplo
    0

    Ahh, OK, if you are talking about a really huge database with tens of thousands of records and complex relationships then, no, it won't be a good idea to store these all as nodes in Umbraco. If it was, say, a list of companies and each company had some child records, that would be fine, but Umbraco doesn't really scale to that extent.

    However, I've built farily complex relationships using Umbraco - where you store things like categories as nodes and then assign these to another node to form a relationship. A quick example (in Umbraco 6) is a store I've created:

     

    So the Tax Rates there are nodes that don't have a template - they can't be viewed, they are just used as a list of values that can be assigned to a Product (using an XPath DropDownList that pulls in the values):

    You can also use the multi-node tree picker to relate other products to the main product. You should get the idea. 

    To answer your earlier question: "As I am still learning the trade here, what do you mean by I 'don't need to assign a template' and 'they will never actually be viewable'? Could you give an example?"

    Basically, when you create a Document Type in Umbraco you usually also create a template (a View) for it. However, you do not have to assign a template to a document type - you can select none. You can do this and publish the page and it will still have a URL - but if you visit the URL you will get a 404 not found. So, effectively, the conent is there but it is not viewable. You can then easily hide any pages without a template from menus etc. In effect, you are just using Umbraco as a data store (think of it a bit like a NoSQL document database).

    Back to your original query - you could adapt a "hybrid" approach where you store some content as Umbraco nodes and then the "child" transactions in the database. (You can use the Umbraco node id as the foreign key).

     

Please Sign in or register to post replies

Write your reply to:

Draft