I am creating products from a product template. Each time a customer selects a product to view information about, the data from that product needs to get loaded. I have created a controller, model and view. The model is generated with TDS. I need to pass the item id to the [SitecoreId] from the controller. Here is the code I am using:
From the layout:
#{var id = Sitecore.Data.ID.Parse("{74A67488-8E33-47E2-86F5-25AD23FDF3D3}"); }
#Html.Sitecore().ControllerRendering("ProductOverview", "Index", new { ItemId = #id })
The controller:
public class ProductOverviewController : Controller
{
private readonly IMvcContext _mvcContext;
public ProductOverviewController(IMvcContext mvcContext)
{
_mvcContext = mvcContext;
}
// GET: ProductOverview
public ActionResult Index()
{
var itemId = string.Empty;
var rc = RenderingContext.CurrentOrNull;
if (rc != null)
{
var parms = rc.Rendering.Properties;
itemId = parms["ItemId"];
}
var dataSource = _mvcContext.GetContextItem<ProductOverviewModel> ();
return View(dataSource);
}
}
The itemId var has the correct id that I am passing from the layout (hard coded for now). From here I am at an absolute loss on how to get that into the model. I have tried dozens of suggestions from searches but the model always uses the current item (as set by GlassBase in the model itself) as opposed to the product id that contains the data for that product.
Is what I want to do even possible? Can the [SitecoreId] even be overridden?
The line where you are setting the value for dataSource using Glass Mapper is where you'll want to make your change..
Glass Mapper lets you use a number of different options to get the Item and cast to your "type" which looks to be ProductOverviewModel currently.
you can use the following for example (notice that I've used .SitecoreService.GetItem instead of .GetContextItem ):
//pass the GUID into here (you'd need to cast to a Guid first instead of ID)
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(guid);
//or if you wanted to get your ID as a Sitecore Item you could use
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(item.Paths.Path);
Related
I want to preface this by saying I am new to working with models. Please forgive me if this question has a simple answer.
I have been struggling to revert a listed view model back to view model. To give some background, I have a search form being passed to a model coming from my ActionResult and am then getting a filter out the results.
[ Controller ]
public ActionResult GetFilters(MembershipVM model)
{
var uDataList = new List<MembershipVM>();
model = _service.GetFilters(model);
return View("SendEmail", model);
}
[ Service ]
public List<MembershipVM> GetFilters(MembershipVM model)
{
var query = _context.Members.Where(f => f.Deleted == 0).AsQueryable();
var members = _context.Members.ToList();
query = query.Where(f => agencyTypes.Contains(f.AgencyType));
var uDataList = new List<MembershipVM>();
foreach (var member in members)
{
var uData = new MembershipVM();
uData.Email = member.Email;
uData.AgencyType = member.AgencyType;
...
uDataList.Add(uData);
}
return uDataList;
}
How can I cast the List from "_service.GetFilters" to MembershipVM? Is there a better/easier way to get the results as an object from the "_service.GetFilters" service?
Thanks so much in advance!
Daisy
I am not sure what you are trying to do here. First you get the results of your filter from this code:
model = _service.GetFilters(model);
And the definition of your method is this:
public List<MembershipVM> GetFilters(MembershipVM model)
So you would expect that this is a list of results. In short, a collection of results.
Now if you want to pass it on your ActionResult as one entity only, then getting one of your results will do the trick:
return View("SendEmail", model.Take(1).SingleOrDefault());
But why do you need to pass one entity only? But that should work with your current requirement.
I use asp.net mvc 5 and EF 6 .I have following code in my controller
var resultOne = stats.GetPlayerStatsByParam(year, modeOne); //first
ViewData["more"] = stats.GetTeamStatsID(year); //second
return View("StatsNew", resultOne.ToList());
I am able to display result in view using "resultOne". Now I want to pass another data to same view using ViewData[]. its a stored procedure and the result is paasing to "ViewData["more"]".
I have done following code for "ViewData["more"]" in View page
But I am getting an error saying that 'object reference not set'
#foreach (var item in ViewData["more"] as #List<demo.GetTeamStatsID_Result>)
{
#item.Pld;
}
Use a viewmodel and extend it by the data coming from stats.GetTeamStatsID(year);
Do not use ViewBag or ViewData if not necessary for some reason (which I canĀ“t imagine right now)
As the comments have already pointed out, build a ViewModel class, that wraps everthing you need:
//I am using fake types here, sinceI don't know your model classes
//substitute accordingly!
public class MyViewModel
{
public PlayerStatsType PlayerStats { get; set;}
public List<demo.GetTeamStatsID_Result> Teams { get; set;}
}
Then in your action method:
var vm = new MyViewModel();
vm.PlayerStats = stats.GetPlayerStatsByParam(year, modeOne); //first
vm.TeamId = stats.GetTeamStatsID(year); //second
return View("StatsNew", vm);
Amend the model declaration in your view:
#model Namespace.Models.MyViewModel //again use namespace for your vm class
Now you can access both properties from your model:
#foreach (var item in Model.Teams)
{
#item.Pld;
}
Update I have saved my problem a long time ago. The problem was that I was trying to call the view model on the wrong view method! I was calling the base view method (Document), instead of one of it's derived method (like NewDocument, PDFDocument, etc.) Thus it was only giving me the Documents data, which didn't change. I was looking and using the wrong view method all the time... Stephen, when you asked me
"Why do you create derived classes in a method but then return only the base class"
I couldn't answer the question at the time because I didn't even know myself, until I remember that originally, the method wasn't returning the base class. I only changed it so that it can work with the base view method, which was wrong in the first place!
That's what I get for only getting 3-4 hours of sleep in 3 days. Everything works right now. Thanks.
I'm having a hard time trying to figure out why the data in my view isn't changing after I do a post. Originally I was doing it via return View() and it worked, but since it was a partial view, the page didn't look great, so I was reading up and saw that it was better to do it by Post-Redirect-Get pattern (PRG) and to use an id value to retrieve the values instead of sending the entire model via Tempdata. I even used ModelState.Clear() and that didn't even work. When I debugged the code, the model only has the values from when I first called it.
Here's part of my Get controller:
NewDocument Get Controller
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// This doesn't work. The view keeps on showing the data from View(Services.CreateNewDocument()).
if (id != null)
{
return View(Services.GetdocumentViewModelData(DocEnum.Section.NEW_DOC_INDEX, (int)id));
}
// This works fine
return View(Services.CreateNewDocument());
}
And here's the post that calls the redirect:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
int? DocID = Services.AddingNewDocument(dvm);
// See, I even tried to clear it.
ModelState.Clear();
return base.RedirectToAction("NewDocument", new { id = DocID });
}
else
{
// Display errors in the modal
}
return base.RedirectToAction("NewDocument");
}
And here's the old way I did it:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
Services.AddingNewDocument(ref dvm);
dvm.NewRecordMode = DocEnum.Action.UPDATE;
// It worked, but only the partial view showed, and not the entire view.
return PartialView("_NewDocument", dvm);
}
else
{
// Display errors in the model
}
return base.RedirectToAction("NewDocument");
}
Could it be because I'm using a custom model binding?
My Custom Model Binding
public class BaseClassModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var modelType = bindingContext.ModelType;
var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ViewModel");
if (modelTypeValue == null)
throw new Exception("View does not contain the needed derived model type name");
var modelTypeName = modelTypeValue.AttemptedValue;
var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);
if (type == null)
{
throw new Exception(String.Format("Derived model type {0} not found", modelTypeName));
}
var instance = bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
return base.BindModel(controllerContext, bindingContext);
}
}
EDIT: And here's the GetDocumentViewModelData code:
GetDocumentFromViewModelData
public static DocumentViewModel GetDocumentViewModelData(DocEnum.Section docType, int id)
{
switch (docType)
{
case DocEnum.Section.NEW_DOCUMENT_INDEX:
// NewDocumentTypeViewModel is a child to DocumentTypeViewModel
DocumentTypeViewModel nd = NewDocumentService.GetViewModelByID(id);
return nd;
case DocEnum.Section.PDF_DOCUMENT:
DocumentTypeViewModel pdfvm = PDFDocumentService.GetViewModelByID(id);
return pdfvm;
case DocEnum.Section.XLS_DOCUMENT:
DocumentTypeViewModel xlsvm = XLSDocumentService.GetViewModelByID(id);
return xlsvm;
}
return null;
}
Edit: Also adding the GetViewModelByID function
GetViewModelByID
public static DocumentTypeViewModel GetViewModelByID(int id)
{
docEntities db = new docEntities();
NewDocumentTypeViewModel vm = new NewDocumentTypeViewModel();
// Calls a stored procedure called Select_Documents_ByID(id) to get the note entry
// that was submitted.
List<Select_Documents_ByID_Result> prevNotes = db.Select_Documents_ByID(id).ToList();
StringBuilder sNotes = new StringBuilder();
foreach (var note in prevNotes)
{
sNotes.AppendFormat("{0} - {1}: {2}\n\n", note.CreatedDate.ToString("yyyy-MM-dd HH:mm"), note.username, note.Entry);
}
vm.PreviousNotes = sNotes.ToString();
return vm;
}
Edit: I did a direct creation of the view model inside the Get controller, and it's the same result. when i debugged the view itself, the values from the new view model don't show up. Instead, the values from the initial view model, View(Services.CreateNewDocument()), shows.
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// Right here I created the view model to test thing, but I'm getting the same results. Nothing has changed.
if (id != null)
{
var d = new NewDocumentTypeViewModel(1, "Help!");
// This property is from the base class, DocumentTypeViewModel
d.DocumentTitle = "Testing!";
return View(d);
// Inside the view itself, none of the values in the view model, including the one
// belonging to the base class. It still shows the initial values.
}
// This works fine
// Or maybe not...
return View(Services.CreateNewDocument());
}
Edit: I wanted to see if it was also doing the same thing for the initial call to the view return View(Services.CreateNewDocument()), and decided to change the value for documentTitle in the base class from New Document to a randomly-generated number, after the object has been created.
Here's the code for DocumentTypeViewModel's default constructor:
public DocumentTypeViewModel()
{
DocumentTitle = "New Document";
NewRecordMode = DocEnum.Action.ADD;
DocumentID = 0;
}
And here's the Services.CreateNewDocument() code where I change the DocumentTitle after the View Model has been created.
public DocumentTypeViewModel CreateNewDocument()
{
DocumentTypeViewModel dtvm = new DocumentTypeViewModel();
Random r = new Random();
dtvm.DocumentTitle = r.Next(5, Int32.MaxValue).ToString();
return dtvm;
}
Now in the View, when I call DocumentTitle:
<div class="label-text-group">
#Html.LabelFor(model => model.DocumentTitle)
#Html.EditorFor(model => model.DocumentTitle)
</div>
You would expect to see a randomly-generated number every time the View gets called. Nope, what you would see is "New Document". Weird.
It's seems that Services.GetDocumentViewModelData() is not exactly working correctly. It only carries the values created by the base class' constructor when a view is created, not any values that have been added or changed within GetDocumentViewModelData() itself. Why is that? What's going on? Please help anybody!
I have solved it. Look at the Update section on top. Thanks Stephen.
In my MVC application I am trying to retrieve data using a stored procedure, then display it in a dropdown.
Here is My controller action
public ActionResult Register(string id )
{
RegistrationModel Student = new RegistrationModel();
using (var db = new StudentEntities())
{
var SportResultList = GetListOfSport();
var SportSelectList = new SelectList(SportResultList);
ViewBag.SportList = SportSelectList;
return View(Student);
}
Here is the Method to get the list using the stored procedure
public static List<GetSportsResult> GetListOfSport()
{
using (var db = new StudentEntities())
{
ObjectResult<GetSportsResult> SportResults = db.GetSportsByStudentIdAndSeason(11111, 1);
List<GetSportsResult> results = SportResults.ToList();
return results;
}
}
The stored procedure returns a complex type called GetSportsResults but I don't now how to access its fields.
Currently this code will display the GetSportsResults 20 times which is the right amount of records I should be getting
In the constructor for SelectList you can specify which fields are to be used for the text and for the value. This is done by passing string values to the constructor.
For example, if your GetSportsResult object has a .ID property as its identifier and a .Name property as its display value, then your code would look like this:
var SportResultList = GetListOfSport();
var SportSelectList = new SelectList(SportResultList, "ID", "Name");
ViewBag.SportList = SportSelectList;
This would indicate to the SelectList object that GetSportsResult.ID should be the value for each item in the list, and GetSportsResult.Name should be the displayed text for each item in the list.
Without specifying these fields, currently the object tries to make a "best guess" of what to display. It's probably doing this by calling .ToString() on each object by default. And the default behavior of .ToString() on a non-primative type is to display the name of the type itself, which is why you're seeing the string "GetSportsResult" for each item.
I have 2 views, one to return all the locations belonging to a project, the other returns a json file containing the locations that used to show them on a google map.
Listing the locations works like this as the id is sent with the actionlink, but how do I send the project ID to the Map view?
public ActionResult GoogleMaps(int id)
{
return View(Project.Find(id).DeviceLocations);
}
//[AutoRefresh(DurationInSeconds = 30)]
public ActionResult Map(int id)
{
var map = Project.Find(id).DeviceLocations;
Locations l = new Locations();
l.locations = map;
return Json(l, JsonRequestBehavior.AllowGet);
}
Some people construct a model (class) specifically to handle all of the values being passed from the controller. So, your model would have a property called DeviceID, and would thus be strongly typed.
Alternately, you can use ViewData:
You can put this in your controller:
ViewData["DeviceID"] = id;
Then, in your view, you will need to cast it before you use it, like this:
(int)ViewData["DeviceID"]
Include a get, set property "project ID" in your Locations class.