úterý 18. listopadu 2014

Zábavní parky okolo ČR

Bohužel nám v ČR chybí pořádný zábavní park a tak se musí vždy do zahraničí. Tady je přehledná mapka zábavních parků v Evropě - ty lepší jsou zvýrazněny hvězdičkou.  Snažil jsem se zahrnout všechny parky, které mají něco, co alespoň trochu připomíná horskou dráhu.

Jak je vidět, naprostá většina parků leží v Německu - zejména asi jeden z nejlepších parků v Evropě Europa Park.


Ne všechny parky v přehledu stojí za návštěvu, - například bych varoval před polským parkem Wesole Miasteczko  - je to asi tak nejhorší park, co jsme kdy navštívil, díky neschopnosti obsluhy neskutečné čekací doby i když byl park prázdný. U jiných parků je třeba zvážit i vhodný věk na návštěvu. Rakouský Family Park se hodí spíše pro menší děti, německý Legoland asi nadchne děti do 10ti let a pak už je to horší a tak podobně. 






pondělí 17. listopadu 2014

Feraty u jezera Lago di Garda 1 - Colodri a Drena


Colodri

Naše první ferata u jezera a velmi pěkná - k příznivému dojmu přispěl i pěkný slunečný den. 



Výstup stojí za to, postupně se odkrývá údolí s řekou a po zdolání posledního úseku se otevře výhled na hrad Arco na sousedním vrcholu a také dále k jezeru.

čtvrtek 6. listopadu 2014

Stránkování v MVC

Tenhle článeček je o stránkování záznamů v MVC trochu jinak - tak, aby jednotlivé části (controller's actions, models, views) zůstaly co nejvíce jednoduché.

Zdrojový kód je dostupný zde.

Models

Aby to nebylo moc složité, bude se zobrazovat seznam jmen, tedy jako základní třída  nám poslouží toto:
public class UserViewModel
{
 public string FirstName { get; set; }
 public string LastName { get; set; }
}

Pro zobrazení seznamu bude sloužit generická třída - takže ani tento příklad nebude pevně svázán s třídou UserViewModel, ale generickou třídu budeme moci použít s jakoukoliv třídou, z níž chceme zobrazovat data.
public interface IItemListInfo
{
 int TotalItems { get; }
}

public class ItemListViewModel<T> : IItemListInfo
{
 private IEnumerable<T> items;
 private int totalItems;

 public ItemListViewModel(IEnumerable<T> items, int totalItems)
 {
  this.items = items;
  this.totalItems = totalItems;
 }
 public IEnumerable<T> Items { get {return this.items;} }
 public int TotalItems { get { return this.totalItems; } }
}

a ještě musíme mít třídu, která bude držet "stránkovácí" informace:
public class PagingViewModel
{
 public int PageNumber { get; set; }
 public int PageSize { get; set; }
 public int Pages {get;set;}
}
Objekt této třídy bude vlastně jen držet číslo aktuální stránky, velikost stránky a celkový počet stránek.

Controller - poprvé

Akce v kontroleru bude jednoduchá - hlavním úkolem je načíst daný počet dat (=počet záznamů na stránku)  pro danou stránku (počet stránek)


public ActionResult Index(PagingViewModel paging)
{
 ItemListViewModel<UserViewModel> model = this.GetUsers(paging.PageSize, paging.PageNumber);

 return View(model);
}


Views

Ve view dojde k zrendrování informací a i k vykreslení navigační části. První view je docela jednoduché a vykreslí informace o uživateli - pro vykreslení navigační části pak předá řízení zpět do kontroleru a zavolá akci "Paging"




@using MvcPagingExample.Models;

@model ItemListViewModel<UserViewModel>
@{
    ViewBag.Title = "Home Page";
}
<div class="list">
    <h2>List of Users</h2>

    <table>
        <thead>
            <th width="120px">First name</th>
            <th width="120px">Last name</th>
        </thead>
        @foreach (UserViewModel user in Model.Items)
        { 
            <tr>
                <td>@user.FirstName </td>
                <td>@user.LastName</td>
            </tr>
        }
    </table>
    <br/>
    @{Html.RenderAction("Paging");}
</div>



Controller - podruhé

Tato metoda může být použita pro vykreslení stránkování libovolného seznamu - odpovídající view jen využívá javascript pro ajax volání zpět na server a vrácenou hodnotou nahrádí stávající obsah:


[ChildActionOnly]
public ActionResult Paging(PagingViewModel paging)
{
	return PartialView(paging);
}

a view:


@using MvcPagingExample.Models;

@model PagingViewModel
<script type="text/javascript">
    $(document).ready(function () {
        jQuery("a").click(function () {
            var $anchor = jQuery(this);
            var url = $anchor.closest('.ajaxPaging').attr("data-source");
            jQuery.get(
                url,
                { PageNumber: $anchor.attr("rel")},
                function (data) {
                    jQuery(".list").replaceWith(data);
                });
        });
    });
</script>

<div class="ajaxPaging" data-source="@Request.Url.AbsolutePath">
    @NaviLink(active: Model.PageNumber > 1, targetPageNumber: 1, label: "first")
    @NaviLink(active: Model.PageNumber > 1, targetPageNumber: (Model.PageNumber - 1), label: "previous")
    <b>page @Model.PageNumber of @Model.Pages</b>
    @NaviLink(active: Model.PageNumber < Model.Pages, targetPageNumber: (Model.PageNumber + 1), label: "next")
    @NaviLink(active: Model.PageNumber < Model.Pages, targetPageNumber: Model.Pages, label: "last")
</div>

@helper NaviLink(bool active, int targetPageNumber, string label)
{
    if (active)
    {
    <a href="#" rel="@targetPageNumber">@label</a>
    }
    else
    { 
    @label
    }
}


Prvotní nastavení

Zbývá ještě ošetřit případ, kdy při prvním volání akce není žádné stránkování nastaveno.  K tomu slouží atribut třída, která dědí z třídy ActionFilterAttribute a která nabízí dvě metody - jednu spouštěnou před provedením vlastní akce kontroleru (OnActionExecuting) a druhou, která se provadí  po vykonání akce v kontroleru (OnActionExecuted).



public override void OnActionExecuting(ActionExecutingContext filterContext)
{
	var paging = filterContext.ActionParameters.Select(p => p.Value as PagingViewModel).Where(f=> f !=null).FirstOrDefault();

	if (paging != null)
	{
		paging.PageSize = PageSize;
		filterContext.RouteData.Values.Add("PageSize", this.PageSize);

		if (paging.PageNumber == 0)
		{
			filterContext.RouteData.Values.Add("PageNumber", 1);
			paging.PageNumber = 1;
		}
	}
	base.OnActionExecuting(filterContext);
}

Nejprve se metoda pokouší najít hodnotu parametru předávaného metodě kontroleru - tedy PagingViewModel.  Pokud takový parametr nenajde, tak jej vytvoří a nastaví velikost stránky a první stránku jako aktuální. Zároveň tyto hodnoty vloží i do kolekce RouteData. Tato kolekce je MVC používáná při bindování a pokud tedy budeme později volat metodu Paging, tak MVC použije data z této kolekce pro vytvoření parametru (instance třídy PagingViewModel) předávaného do této metody.

Po spuštění metody se naplní kolekce a zároveň se zjistí, kolik záznamů je celkem k dispozici. Což je ten správný okamžik pro spuštění metody OnActionExecuted, která vypočte celkový počet stránek a získá tak poslední hodnotu, kterou je nutno uložit do kolekce RouteData, aby MVC mohlo později, při volání metody Paging, vytvořit plnohodnotnou instanci třídy PagingViewModel.



public override void OnActionExecuted(ActionExecutedContext filterContext)
{
	var model = filterContext.Controller.ViewData.Model as IItemListInfo;

	if (filterContext.HttpContext.Request.IsAjaxRequest())
	{
		ViewResult result = filterContext.Result as ViewResult;
		filterContext.Result = new PartialViewResult() { TempData = result.TempData, View=result.View, ViewData=result.ViewData, ViewEngineCollection=result.ViewEngineCollection};
	}
						
	if (model != null)
	{
		filterContext.RouteData.Values.Add("Pages", (int) Math.Ceiling( (double) model.TotalItems/this.PageSize));
	}

	base.OnActionExecuted(filterContext);
}

Můžete si také všimnout, že je kontrolován typ příchozího volání a pokud se jedná o Ajax volání, je výsledek změněn na PartialView - takže se rendreju pouze view odpovídající danému kontroleru a master view je ignorováno - vrací se tak pouze zrendrovaná tabulka a nikoliv celá stránka.


Tento atribut je pak použit pro dekorování akce  a zároveň nastavuje velikost stránky:


[Paging(PageSize = 5)]
public ActionResult Index(PagingViewModel paging)
{
	ItemListViewModel<UserViewModel> model = this.GetUsers(paging.PageSize, paging.PageNumber);

	return View(model);
}

Může se použít na libovolnou akci, která vyžaduje pro své výsledky stránkování. Vývojář se o stránkování nemusí starat a v kontroleru se tedy může soustředit pouze na získání dat.






pondělí 3. listopadu 2014

Proč si nekupovat nosič od Atery (Atera Strada 3 DL)


Minulý rok jsem si koupil nosič na kola Atera Strada 3 DL s rozšířením na čtvrté kolo. S nosičem jsem byl celkem spokojen, dokud nebyl při couvání poškozen a vyvstala nutnost opravy - jednalo se o výměnu jednoho zadního dílu, který zajišťuje blokování pohyblivé části.

A s překvapením jsem zjistil, že vlastně žádný servis těchto poměrně drahých výrobků u nás neexistuje. Našel jsem sice nějaký soukromý servis, který se značkou Atera zabývá, ale tam mne jen politovali s tím, že jim to mohu poslat a oni budou čekat, až nějaký jiný neš´tasník hodí svůj nosič do šrotu a oni tak získají potřebnou součástku - Atera totiž náhradní díly nerada dodává. Tomu se mi nechtělo věřit, přeci jen je to výrobek v cenové relaci kolem €500 a tak jsem napsal přímo výrobci, popsal svůj problém, přiložil fotografie a už za pár dnů jsem měl odpověď:


Hello, pleace contact your dealer.....


Takže jsem nelenil a požádal o seznam dealerů a během dne obdržel kontakt na firmu Neumann a Elit CZ.

Z výše dvou uvedených Elit CZ vůbec nereagoval, u Neumanna se ozval jejich pracovník a začala zdlouhavá výměna mailů - když už se zdálo, že je díl úspěšně objednán, ozval se jejich pracovník s tím, že firma Atera není schopna díl dodat......

Naštěstí měl i řešení - měli skladem použitý poškozený nosič, který jim tam zřejmě nějaký méně trpělivý zákazník nechal a u kterého byl daný díl v pořádku a tak mi nabídl jeho zaslání jen za cenu poštovného - a tak jsem měl po třech měsících konečně nosič opět funkční - díky nadstandardní ochotě pracovníka firmy Neumann.

A poučení?

Výrobek od firmy Atera si už nikdy nekoupím, jakýkoliv - firma, která není schopna dodat náhradní díl (asi chce zákazníka, aby si koupil nový výrobek) je špatná firma.