Diskuze: ASP.NET MVC - Náhled článku
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 27 zpráv z 27.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Co si treba udelat model, ktery by obsahoval napr.: Id, Title, Description, Content, ..., s tim, ze v description bys mel takovy lehci naznak, o cem clanek je a po kliknuti na "pokracovat" se ti zobrazi cely clanek i s decription a content?
No a nebo si udelat pro Index dotaz, ktery zobrazi pouze urcity pocet znaku a v Detailu zobrazis cely obsah.
Btw. ja pouzivam properties Description a Content.
Mě jde o zobrazení třeba prvních 80ti znaků, abych ten seznam nemusel mít "víceřádkový", ale ke každému požadavku by byl výpis jen začátek toho popisu, třeba jako je to tady na ITN... Tak nevím, jak je to nejlepší a nejjednodušší udělat...
Tak vytahnout prvnich 80 znaku asi problem nebude ne? Pomoci linq a Take(). Jinak si klidne udelej dalsi vlastnost a nemusis nic resit.
Někde jsem našel, že by se to dalo udělat nějak takto:
@Html.DisplayFor(modelItem => item.Text.Substring(0,80))
, ale to háže chybu
Templates can be used only with field access, property access, single-dimension
array index, or single-parameter custom indexer expressions.
Ja bych si radeji udelal dotazy v controlleru, nez natahnout vse, ale zobrazit jen malinkou cast.
btw.
@Html.DisplayFor(modelItem => item.Text).ToString().SubString(0,80)
?
To poslední taky háže chybu...
P.S.
proč, když udělám View z modelu (jako výpis/seznam - Index), tak to
normálně jde a když to chci udělat z ViewModelu, tak to nejde?
[DatabaseGenerated(DatabaseGeneratedOption.Identity), Key()]
public int Id { get; set; }
[Display(Name = "Zadáno")]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime CreationDate { get; set; }
[Display(Name = "Text úkolu")]
[Required(ErrorMessage = "Text úkolu je povinný")]
[MaxLength()]
public string Text { get; set; }
[Display(Name = "Splněno")]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime? ReleaseDate { get; set; }
To mám v modelu a View pro výpis dat je OK. Když toto překopíruji do ViewModelu a udělám View pro výpis, tak mi to po spuštění řve, že to není IEnumerable či co...
Tuto chybu to zahlásí
The model item passed into the dictionary is of type 'System.Collections.Generic.List
1[Technician.Models.Proposal]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable
1[Technician.ViewModels.ProposalIndexViewModel]'.
takto mám ViewModel
public class ProposalIndexViewModel
{
[Key]
public int Id { get; set; }
[Display(Name = "Zadáno")]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime CreationDate { get; set; }
[Display(Name = "Text úkolu")]
[Required(ErrorMessage = "Text úkolu je povinný")]
[MaxLength()]
public string Text { get; set; }
[Display(Name = "Splněno")]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime? ReleaseDate { get; set; }
public IEnumerable<Proposal> proposal;
}
a toto v Controlleru
public ActionResult Index()
{
return View(db.Proposals.OrderBy(p=>p.ReleaseDate).ToList());
}
Co tím myslíš "správně namapované"?
Když do toho prvního řádku View dám
@model IEnumerable<Technician.Models.Proposal>
tak je to ok
@model IEnumerable<Technician.ViewModels.ProposalIndexViewModel>
toto hodí výše uvedenou chybu
Schválně jsem ho pro jistotu teď vytvořil znovu (to View) a chyba je
tam...
Když dělám Create, Detail... z "ViewModelu", tak je to OK. Jen List musím
dělat z "Modelu"
Viewmodel ma slouzit jako DTO, takze musis prevest model na viewmodel (cteni z db) nebo naopak (zapis do db).
Koukni na ty privatni metody dole v controlleru:
https://github.com/vajkuba1234/KVHTO/blob/master/KVHTO/Controllers/InformationController.cs
Čumim na to jako vrána a moudrej z toho nejsem...
Teď musím jít s dcerou na její koncert, večer to zkusim ještě
prozkoumat... Dík za rady
Souhlasím s "vajkuba1234". V tom controleru vracíš kolekci objektů typu
"Technician.Models.Proposal" ale view očekává kolekci typu
"Technician.ViewModels.ProposalIndexViewModel". Mapování z entity v db
na tvůj VieModel musíš udělat ty, buď ručně nebo se často používá
knihovna AutoMapper, která
pracuje na základě jmenné konvence a provádí automatické mapování podle
názvů vlastností. (To znamená, že namapuje ty vlastnosti, které se stejně
jmenují v db a v DTO)
Na začátku se nakonfiguruje které páry objektů se na sebe váží a pak v
kódu se použije funkce .ProjectTo<>.
// V nějaké inicializační třídě, která se spouští jednou na začátku aplikace se definuje mapování:
Mapper.Initialize(cfg => cfg.CreateMap<DatabazovaEntita, ViewModel>());
// Pak při dotazování do db v modelech např.:
var person= context.Osoby.ProjectTo<MojeZjednodusenaTrida>()
.Where(x => x.ID== id)
.FirstOrDefault();
Mapování lze provést i naopak z ViewModelu na databázovou entitu v
případě ukládání.
Doporučuji zvážit používání. Knihovna má vyřešeno získávání info i
relačních tabulek apod...
Asi jsem tupej, ale ať to čtu jak chci, netuším, jak to mám udělat... Mohl bys mi to zkusit ukázat na mé třídě?
public class Proposal
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity), Key()]
public int Id { get; set; }
[Display(Name = "Zadáno")]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime CreationDate { get; set; }
[Display(Name = "Text úkolu")]
[Required(ErrorMessage = "Text úkolu je povinný")]
[MaxLength()]
public string Text { get; set; }
[Display(Name = "Splněno")]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime? ReleaseDate { get; set; }
}
public ActionResult Index()
{
//Select data which is visible (Where)
var tvaKolekce = db.Proposals.OrderBy(p=>p.ReleaseDate);
var list = MapModelsToViewModel(tvaKolekce, new List<TvujViewModel>());
return View(list);
}
private List<PubListInformationViewModel> MapModelsToViewModel(IQueryable<TvujModel> models,
List<TvujViewModel> viewModels)
{
foreach (TvujModel i in models)
{
viewModels.Add(new TvujViewModel
{
IdInformation = i.Id,
TitleInformation = i.Title,
DescriptionInformation = i.Description,
DateCreatedInformation = i.DateCreated,
DateEventInformation = i.DateEvent,
Category = i.Category,
IdCategory = i.IdCategory
});
}
return viewModels;
}
Koukni na tu privatni mapovaci metodu... Uz z ni vidis, ze defacto prekopirujes data z modelu do tveho viewmodelu, tady se jedna o vypis z db do stranky. Metoda prijima IQueryable, coz je typ, ktery ti vraci linq dotaz a prevedes si to na seznam viewmodelu. Ty kolekce jsou tam proto, ze v Indexu vypisujes vetsinou nejaky seznam neceho (clanku treba). Ty properties si doufam dokazes predstavit v tom foreachi, takze jsem je neprepisoval.
Treba tady to mas taky vysvetlene, mozna asi i lepe. Mas tam jak manualni mapovani v controlleru, tak jak to mam ja, a nebo za pouziti AutoMapperu. Akorat, ze ja to nemam v ActionResult, ale udelal jsem si pro to pomocne metody, at ten controller vypada trochu citelne.
Link:
https://www.mikesdotnetting.com/article/188/view-model-design-and-use-in-razor-views
Jen jeste abys vedel, kdyz kouknes na to mapovani z modelu na VM, tak prave tam si zvolis, ktere property chces zobrazit ve view. Tzn. kdyz ti bude stacit jen Title a Description, tak si das do VM Id, Title a Description a prave v controlleru to namapujes pomoci Automapperu nebo jako treba ja:
IdInformation = i.Id,
TitleInformation = i.Title,
DescriptionInformation = i.Description,
Diky tomu do view posilas pouze to co skutecne potrebujes. Proto jsem ti treba i psal, at si udelas dalsi propertu Description na nejaky kratky popis o co v clanku pujde. Nemusis tam rvat dalsi data, kdyz je stejne potrebovat nebudes a zobrazis je az tehdy, kdy jsou potreba, tedy v detailu. Protoze si treba predstav, ze budes mit model s Id, Content, Galerie a ja nevim co jeste a ty vytahnes vse, zobrazis akorat prvnich 80 znaku z Contentu no a zbytek jsi tahal zbytecne.
Nakonec jsem to spáchal takto
private List<ProposalViewModel> MapModelsToViewModel(IQueryable<Proposal> models, List<ProposalViewModel> viewModels)
{
foreach (Proposal i in models)
{
//zobrazení části popisu
string nahled = i.Text;
if(nahled.Length > 80)
{
nahled = (nahled).Substring(0, 80) + "...";
}
viewModels.Add(new ProposalViewModel
{
Id = i.Id,
CreationDate = i.CreationDate,
Text = nahled,
ReleaseDate = i.ReleaseDate
});
}
return viewModels;
}
a funguje to přesně podle mých představ
Asi bych to neoznacil za moc stastne reseni davat nejakou praci se stringem do metody, ktera se stara o mapping. Proc si to nevytahnes v indexu podle tvych predstav a znovu ve view kompletni objekt?
Proc si to nevytahnes v indexu podle tvych predstav a znovu ve view kompletni objekt?
Můžeš mi to trošku objasnit?
V Indexu si vytahnes pouze Id a 80 znaku z Textu, namapujes na viemodel a posles do view. V Detailu si pak namapujes vsechny property, ktere ve view potrebujes a posles do view.
K tomu slouzi prave ten viewmodel. Predstav si, ze mas model, ktery obsahuje 20 properties, ale v Indexu ti staci pouze 5 properties, prece to tam cele nenarves, ne? Proto si vytvoris treba IndexViewModel, ktery obsahuje prave tech 5 potrebnych properties, namapujes model do IndexViewmodel a ten posles do view.
V Detailu, kdyz budes potrebovat treba 15 properties, provedes uplne stejny scenar. Proste 1 view == 1 viewmodel.
Zobrazeno 27 zpráv z 27.