Silverstripe: LinkingMode with Custom Controller - content-management-system

I have following problem:
Some links that show up in the Menu (the children of "Portfolio") are links to custom controllers. Of course now the LinkingMode is not available for that Links. Thats a image of the Menu:
So the children of Portfolio (Website, Application, etc.) are actually Category-DataObjects, which do not have a SiteTree Representation. The Submenu of Portfolio is created via checking and looping for all found categories in the Database.
The menu creation looks like that:
<ul>
<% loop Menu(1) %>
<li class="$LinkingMode">
[$LinkingMode] $MenuTitle.XML
<% if Children %>
<ul class="secondary">
<% if ClassName == 'ProjectsPage' %>
<% loop $Top.Categories %> <!-- loop all found categories, every found item links to the custom category controller -->
<li class="$LinkingMode">$Name</li>
<% end_loop %>
<% else %>
<% loop Children %>
<li class="$LinkingMode"><span class="text">$MenuTitle.XML</span></li>
<% end_loop %>
<% end_if %>
</ul>
<% end_if %>
</li>
<% end_loop %>
</ul>
Every Category (Website, Mobile) in the Menu links to a custom controller, which looks basically like that:
class Category_Controller extends Page_Controller {
public function show($arguments) {
return $this; //there will be more code to display all projects of a category
}
}
I expect that I have to add some custom code for the Category_Controller which tells the Portfolio Page which linkingmode it has...
Many thx,
Florian

I´ve found good tips here:
http://www.ssbits.com/snippets/2009/extending-linkingmode-to-handle-controller-actions/
http://www.ssbits.com/tutorials/2010/dataobjects-as-pages-part-1-keeping-it-simple/
Thats the Category_Controller.php (a public var CategoryID is set there):
class Category_Controller extends Page_Controller {
public $CategoryID;
public function index($arguments) {
$slug = $arguments->param("Slug");
$category = Category::get()->filter(array('Slug' => $slug))->First();
$this->CategoryID = $category->ID;
}
}
DataObject Category (LinkingMode function checks if the current CategoryID set in the Controller equals the ID of the Category DateObject):
class Category extends DataObject {
public function LinkingMode(){
$categoryID = Controller::curr()->CategoryID;
return ($categoryID == $this->ID) ? 'current' : 'link';
}
}
In the template you can check then the linking mode as usual:
<% loop $Categories %>
<li class="$LinkingMode">$Name</li>
<% end_loop %>
Cheers,
Florian

Related

Silverstripe loop over ALL children of the sitetree

I´m trying to create a sitemap of all children of the sitetree. For some pages ShowInMenus is set to false. But still these pages should be shown in the sitemao. I know you can loop over all children of a page like this (ignoring ShowInMenus):
<% loop AllChildren %>
$Title
<% end_loop %>
Can I do something similar on the top Level of the sitetree?
Thx,
Florian
i do not know any build in loop you can use for this but you can do in php
public function AllPagesWithParentIDZero() {
return SiteTree::get()->filter('ParentID', 0);
}
and use in template
<% loop $AllPagesWithParentIDZero %>
$Title
<% end_loop %>

How to access my model from my view?

I cannot seem to figure out how to access my Model from my View. I am confused.
Here is my Home controller. I have verified that "specimens" is being populated with data from the database:
public class HomeController : Controller
{
wildtropEntities wildlifeDB = new wildtropEntities();
public ActionResult Index()
{
ViewData["CurrentDate"] = System.DateTime.Now.ToString("MM/dd/yyyy");
var specimens = from s in wildlifeDB.specimen1
select s;
return View(specimens);
}
}
And here are couple snippets from my View:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<% foreach (WildlifeTropical.Models.specimen s in ??????)
{ %>
<div>s.Name</div>
<% } %>
I assumed I would be able to access "specimens" since I passed it to the View from the Controller (ie, return View(specimens))...but it isn't working.
You will have to make your view strongly typed to a collection of specimen which is what you are passing to it from your controller action (IEnumerable<specimen>):
<%# Page
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<WildlifeTropical.Models.specimen>>" %>
<% foreach (WildlifeTropical.Models.specimen s Model) { %>
<div><%= Html.Encode(s.Name) %></div>
<% } %>
Notice how the view inherits System.Web.Mvc.ViewPage<IEnumerable<WildlifeTropical.Models.specimen>> and it is now strongly typed to a collection of specimens that you will be able to loop through.
This being said, personally I don't like writing foreach loops in my views. They make them look ugly. In this case I would use a display template:
<%# Page
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<WildlifeTropical.Models.specimen>>" %>
<%= Html.DisplayForModel() %>
and then I would define a display template which will automatically be rendered for each element of the model collection (~/Views/Shared/DisplayTemplates/specimen.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<WildlifeTropical.Models.specimen>" %>
<div>
<%= Html.DisplayFor(x => x.Name) %>
</div>
See how the specimen.ascx user control is now strongly typed to System.Web.Mvc.ViewUserControl<WildlifeTropical.Models.specimen>. This is because it will be rendered for each specimen of the main view model.
I am not sure about MVC2. In MVC 3(with Razor View Engine), I can pass the Model / ViewModel to the View like this.
#model MyProject.ViewModel.UserViewModel
#{
ViewBag.Title = "Welcome To My Site";
}
<div class="divSubHead">
<h2>Hello #Model.FirstName</h2></div>
How about this? Note the Inherits for the Page.
<%# Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of IEnumerable(Of Specimen))" %>
<% foreach (WildlifeTropical.Models.specimen s in Model)
{ %>
<div>s.Name</div>
<% } %>

Get object out of List< Tuple < object1, object2 > > and store in ViewModel

[Suggestion: Want to read the answers in a logical manner ?? >> choose TAB [Oldest]
Goal:
Presentation of books with related inventorydetails on homepage
such as Book.Title, InventoryDetail.Quantity etc.
(Join|Book.BookId <=< InventoryDetail.BookId)
Problem 1: How to join
Problem 2: How to use a tuple (list of tuples)
Problem 3: How to store the separated objects (from the list of tuples) into a Strongly Typed ViewModel
Answer 1: An possible approach using Mike Brind's Guidance
Answer 2: Problem 1 and 2 tackled !!
Answer 3: Problem 3 tackled !!
Have fun with it. I'm happy to share!!!
public ActionResult Index()
{
// Return a list of tuples {(WebshopDB.Models.Book, WebshopDB.Models.InventoryDetail)}
// Each tuple containing two items:
// > Item1 {WebshopDB.Models.Book}
// > Item2 {WebshopDB.Models.InventoryDetail}
var tuple_booksinventorydetails = ListOfTuples_BookInventoryDetail(5);
// BEGIN UNDER CONSTRUCTION Setting up ViewModel
// See below the code for the ViewModel
var viewmodel = new HomeIndexViewModel()
{
// Problem // Book = tuple_books.Contains(Book).??,
// Problem // InventoryDetail = tuple_books.Contains(InventoryDetail).??
};
// END
return View( ..... );
}
private List<Tuple<Book, InventoryDetail>> ListOfTuples_BookInventoryDetail(int count)
{
var list_of_tuples = new List<Tuple<Book, InventoryDetail>>();
var showbooks = webshopDB.Books
.Join(webshopDB.InventoryDetails, b => b.BookId, i => i.BookId, (b, i) => new { b = b, i = i })
.Where(o => (o.b.ShowInWebshop == true))
.Where(o => o.b.BookThumbUrl.Contains(".jpg"))
.Take(count);
foreach (var item in showbooks)
{
list_of_tuples.Add( Tuple.Create<Book, InventoryDetail>( (item.b), (item.i) ) );
}
return list_of_tuples;
}
As promised! I did reach my goal!!!
<%-- Index.apsx --%>
<%# Page Title=" <Domain> " Language="C#" MasterPageFile="~/Views/Shared/Store.Master"
Inherits="System.Web.Mvc.ViewPage<HomeTupleIndexViewModel>" %>
<%-- Mandatory 'Import' --%>
<%# Import Namespace="<Domain>.ViewModels" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
<%--....--%>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%-- Exerecise Tuple --%>
<% Html.RenderPartial("BookPartial", Model.Book); %>
<% Html.RenderPartial("InventoryDetailPartial", Model.InventoryDetail); %>
</asp:Content>
<%-- BookPartial.ascx --%>
<%-- Currently this ViewPartial is a Strongly Typed ViewPartial %> --%>
<%# Control Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<IEnumerable< <Domain> .Models.Book>>" %>
<%# Import Namespace=" <Domain> .Helpers" %>
<%-- Strongly Typed ViewPages--%>
<ul id="...">
<% foreach (var item in Model) {%>
<li>
<div id="...">
<p>
<a href="<%: Url.Action("Details", "Store", new { id = item.BookId }) %>">
<div id="...">
<h2>
<span><%: item.Genre %></span>
</h2>
</div>
<div id="...">
<img alt="<%: item.Title %>" src="<%: item.BookThumbUrl %>" />
</div>
<div id="...">
<span><%: item.Title %></span>
</div>
</a>
</p>
</div>
</li>
<% } %>
</ul>
<%-- InventoryDetailPartial.ascx --%>
<%# Control Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<IEnumerable< <Domain> .Models.InventoryDetail>>" %>
<%# Import Namespace=" <Domain> .Helpers" %>
<%-- Strongly Typed ViewPages--%>
<ul id="...">
<% foreach (var item in Model) {%>
<li>
<div id="...">
<p>
<div id="...">
<span>
<% if (item.Quantity == 1)
{ %>
<%: Server.HtmlDecode(Html.Translation("Stock_Amount"))%> <%: item.Quantity %> <%: Server.HtmlDecode(Html.Translation("Copy"))%>
<% }
else if (item.Quantity > 1) %>
<% { %>
<%: Server.HtmlDecode(Html.Translation("Stock_Amount"))%> <%: item.Quantity %> <%: Server.HtmlDecode(Html.Translation("Copies"))%>
<% }
else
{ %>
<%: Server.HtmlDecode(Html.Translation("Sold_Out"))%>
<% } %>
</span>
</div>
</p>
</div>
</li>
<% } %>
</ul>
So, finally I ended up with an Index.aspx calling two separate Partial Views (I prefer to speak about 'controls' instead of 'partials', however, this is just a matter of personal taste).
Compared to Mike Brind's guide (mentioned in my first answer) by using this tuple 'path' guide you'll finally have more 'freedom' to do as you like on the index page. The 'related' objects (!! the 'join' remember !!) are nicely separated in the presentation layer, however still strongly related!
Answer ( Solution for my own posted question! )
// Instantiate new lists needed to add the to be separated objects of the tuple
List<Book> T1 = new List<Book>();
List<InventoryDetail> T2 = new List<InventoryDetail>();
// Iterate through the tuple and add each 'item' of each tuple into the new instantiated lists
for (int i = 0; i < tuple_booksinventorydetails.Count; i++)
{
var tuple = tuple_booksinventorydetails[i];
// Item1
T1.Add(tuple.Item1);
// Item2
T2.Add(tuple.Item2);
}
// Instantiate a new viewmodel to store the separated lists
// ==HomeTupleIndexViewMode Class==
//using System;
//using System.Collections.Generic;
//using <Domain>.Models;
//namespace <Domain>.ViewModels
//{
// public class HomeTupleIndexViewModel
// {
// public List<Book> Book { get; set; }
// public List<InventoryDetail> InventoryDetail { get; set; }
// }
//}
// ==
HomeTupleIndexViewModel tupleviewdata = new HomeTupleIndexViewModel()
{
Book = T1,
InventoryDetail = T2
};
return View(tupleviewdata);

Having difficulty with ModelBinding and Dictionary, ASP.NET MVC 2.0

Using the following model, I expect that when the Submit button is hit, the Edit Method to Fire, and the model parameter to have the adjusted values. But it keeps returning an instance of the class with all null values. It is as if no model binding ever happens.
class Trait
{
string Name { get; set; }
// other properties
}
class DesignViewModel
{
Dictionary<Trait, int> Allocation { get; set; }
}
Controller
public ActionResult Create()
{
var model = new DesignViewModel();
// retrieve traits from database
foreach(var trait in Repository.Traits)
model.Allocation.Add(trait, 0);
return View(model);
}
[HttpPost]
public ActionResult Edit(DesignViewModel model)
{
// nothing works yet, so I don't have a lot of code here...
}
HTML
Top Level Page
<%# Page Title="" Language="C#" MasterPageFile="~/Areas/Setup/Views/Shared/Setup.master"
Inherits="System.Web.Mvc.ViewPage<OtherModel>" %>
<% Html.RenderAction("Design", "Test"); %>
Partial View
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DesignViewModel>" %>
<% using (Html.BeginForm("Edit", "Test", FormMethod.Post ) ) {%>
<div id="eq">
<% foreach (var trait in Model.Allocations) { %>
<div style="margin: 15px;">
<%: trait.Key.Name %>
<br />
<span class="slider"></span>
<%: Html.TextBox(trait.Key.Name, trait.Value, new { #class = "spent" , #readonly = "readonly" })%>
</div>
<% } %>
</div>
<p>
<input type="submit" value="Submit" />
</p>
<% } %>
You need to add [HttpPost] to your Edit method for it to be fired during POST requests.

asp.net mvc2 - two (or more) views using same controller?

Is it possible that two different views use the same controller?
I have very complex controller that displays some data. Now I need to display this data (which is retrieved using ajax) in two partial views because I want to place them on different positions in layout.
the View() function can be passed arguments, for instance:
return View(); // The view with the same name as the action.
return View("MyView") // The view named "MyView"
There are a few more overloads too. Does this fit the bill?
If not, why not partial views, for instance, given this model:
public class BlogItem
{
public string Title { get; set; }
public int Id { get; set; }
}
And this action:
public ActionResult Index()
{
var items = new List<BlogItem>
{
new BlogItem { Title = "Test Blog Item", Id = 1 }
};
return View(items);
}
And this view:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<BlogItem>>" %>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<% Html.RenderPartial("List", Model); %>
<% Html.RenderPartial("Icon", Model); %>
</asp:Content>
I can have two partial views using the same model:
List:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<BlogItem>" %>
<ul>
<% foreach (var item in Model) { %>
<li><%= item.Title %></li>
<% } %>
</ul>
Icon:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<BlogItem>" %>
<div>
<% foreach (var item in Model) { %>
<div class="icon"><img src="..." /></div>
<div class="text"><%= item.Title %></div>
<% } %>
</div>
Would that work?
Based on my understanding so far, you want one controller action to return two views. I somehow think that this is not possible.
You have mentioned that the views are used to display identical data is different ways. My suggestion would to return a JsonResult from the controller action and build the view client side.