Orchard Projection Page Default View - orchardcms

I am using Orchard 1.8 and have created a new content type (called PressRelease), query for the results and projection to view the query with a custom template (using URL Alternates in the format List-ProjectionPage-url-PressRelease.cshtml) and all of that is working fine.
The one part that has me stumped is, if I use The Theme Machine as my theme (untouched), this projection view will show up in an unordered list with the corresponding AutoRoute links to the individual ContentItem entities, their metadata and so on. I'm trying to figure out how I access things such as the AutoRoute URL for a specific item, the metadata (create/publish dates) and so on for use with things like a Facebook Share button. Essentially I'm trying to recreate that default view, albeit with customizations.
Here is the code for List-ProjectionPage-url-PressRelease.cshtml:
#using Orchard.Utility.Extensions;
#using System.Linq
#functions
{
public class PressRelease
{
public PressRelease()
{
this.Attachments = new List<Attachment>();
}
public string Title { get; set; }
public string Source { get; set; }
public DateTime PublishDate { get; set; }
public string Body { get; set; }
public List<Attachment> Attachments { get; set; }
}
public class Attachment
{
public string Filename { get; set; }
public string Path { get; set; }
}
}
#{
//add list of dynamic objects to strongly typed class
var releases = new List<PressRelease>();
foreach (var item in #Model.Items)
{
var release = new PressRelease
{
Title = item.ContentItem.TitlePart.Title,
Source = item.ContentItem.PressRelease.Source.Value,
PublishDate = item.ContentItem.PressRelease.Date.DateTime,
Body = item.ContentItem.BodyPart.Text
};
//load attachment(s) to class
var attachments = (Orchard.MediaLibrary.Fields.MediaLibraryPickerField)item.ContentItem.PressRelease.Attachment;
if (attachments.MediaParts.Count() > 0)
{
foreach (var part in attachments.MediaParts)
{
release.Attachments.Add(new Attachment { Filename = part.FileName, Path = part.MediaUrl });
}
}
releases.Add(release);
}
}
#{
foreach (var item in releases)
{
<div class="press-release">
<div class="press-release-title">#item.Title</div>
<div class="press-release-meta">
<span class="press-release-source">Source: #item.Source</span>
#if (item.PublishDate != DateTime.MinValue)
{
<span class="press-release-date">#item.PublishDate.ToShortDateString()</span>
}
</div>
#if (item.Attachments.Count() > 0)
{
<div class="press-release-attachments">
<span class="press-release-attachments-title">Attached: </span>
#foreach (var attachment in item.Attachments)
{
var linkText = attachment.Filename;
var url = attachment.Path;
#Html.Link(linkText, url);
if (attachment != item.Attachments.Last())
{
<span>, </span>
}
}
</div>
}
<div class="press-release-body">
<p>#Html.Raw(item.Body.Replace("\r\n", "<br />"))</p>
</div>
</div>
<div class="social">
<!-- ** This is where I need AutoRoute URL so I can do FB share link **-->
<div class="fb-share-button" data-href="" data-type="button_count"></div>
</div>
if (item != releases.Last())
{
<hr />
}
}
}
Thoughts?

Utilizing the Shape Tracer (in conjunction with #Bertrand's assistance in the comments above) helped me get to where I need. Here is the final layout code I went with (which has some super hacky stuff in it):
#using Orchard.Utility.Extensions;
#using System.Linq
#functions
{
public class PressRelease
{
public PressRelease()
{
this.Attachments = new List<Attachment>();
}
private string _NavigateUrl = string.Empty;
public string Title { get; set; }
public string Source { get; set; }
public DateTime PublishDate { get; set; }
public string Body { get; set; }
public List<Attachment> Attachments { get; set; }
public string NavigateUrl
{
get { return string.Format("{0}://{1}/{2}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority, _NavigateUrl); }
set { this._NavigateUrl = value; }
}
}
public class Attachment
{
public string Filename { get; set; }
public string Path { get; set; }
}
}
#{
//add list of dynamic objects to strongly typed class
var releases = new List<PressRelease>();
foreach (var item in #Model.Items)
{
var release = new PressRelease
{
Title = item.ContentItem.TitlePart.Title,
Source = item.ContentItem.PressRelease.Source.Value,
PublishDate = item.ContentItem.PressRelease.Date.DateTime,
//this is super hacky to get a chopped version of the HTML submitted for a summary
Body = item.ContentItem.BodyPart.Text,
NavigateUrl = item.ContentItem.AutoroutePart.Path
};
//load attachment(s) to class
var attachments = (Orchard.MediaLibrary.Fields.MediaLibraryPickerField)item.ContentItem.PressRelease.Attachment;
if (attachments.MediaParts.Count() > 0)
{
foreach (var part in attachments.MediaParts)
{
release.Attachments.Add(new Attachment { Filename = part.FileName, Path = part.MediaUrl });
}
}
releases.Add(release);
}
}
#{
foreach (var item in releases)
{
<div class="press-release">
<div class="press-release-title">#item.Title</div>
<div class="press-release-meta">
<span class="press-release-source">Source: #item.Source</span>
#if (item.PublishDate != DateTime.MinValue)
{
<span class="press-release-date">#item.PublishDate.ToShortDateString()</span>
}
</div>
#if (item.Attachments.Count() > 0)
{
<div class="press-release-attachments">
<span class="press-release-attachments-title">Attached: </span>
#foreach (var attachment in item.Attachments)
{
#attachment.Filename
if (attachment != item.Attachments.Last())
{
<span>, </span>
}
}
</div>
}
<div class="press-release-body">
#{
var body = new HtmlString(Html.Excerpt(item.Body, 200).ToString().Replace(Environment.NewLine, "</p>" + Environment.NewLine + "<p>"));
<p>#body (read more)</p>
}
</div>
</div>
<div class="social">
<div class="fb-share-button" data-href="#item.NavigateUrl" data-type="button_count"></div>
</div>
if (item != releases.Last())
{
<hr />
}
}
}

Related

Blazor creating a generic drop-down

I'm trying to create a generic dropdown component for use across our system. However, I'm encountering issues when binding the EventCallback for when the selected item is changed.
This is my current musings for the generic drop down:
<div class="inputItem #(SizeClass) dropdown" style="min-width:#(Width);">
<SfDropDownList TItem="object" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="#(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="object" TValue="int" ValueChange="#OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="#(TextField)" Value="#(ValueField)" />
</SfDropDownList>
</div>
#code {
[Parameter]
public IEnumerable<object> DataSource { get; set; }
[Parameter]
public EventCallback<ChangeEventArgs<int, object>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
And here's one example component that would call it:
#page "/news/create"
#inject NavigationManager NavManager;
#using Microsoft.EntityFrameworkCore;
#inject IDbContextFactory<FIS2_DbContext> contextFactory;
#inject IFileService fileService;
#using FIS2withSyncfusion.Controls;
#using FIS2withSyncfusion.Models;
#using FIS2withSyncfusion.Utility;
#using Syncfusion.Blazor.RichTextEditor;
#using System.Collections.Generic;
#using System.Threading.Tasks;
#using Newtonsoft.Json;
<div class="dashWrapper">
<SfDashboardLayout AllowDragging="false" AllowFloating="false" AllowResizing="false" CellAspectRatio="2.5" CellSpacing="#(new double[]{20,20})" Columns="3">
<DashboardLayoutPanels>
<DashboardLayoutPanel Column="0" Row="0" SizeX="2" SizeY="2" Id="createNews">
<HeaderTemplate>
<h3>Create A News Item</h3>
</HeaderTemplate>
<ContentTemplate>
<div class="form-wrapper">
<div class="inputRow">
<TextBox AutoComplete="#(Syncfusion.Blazor.Inputs.AutoComplete.Off)" Placeholder="Title" Text="#(title)" HTMLAttributes="#textboxValidation" Width="450px" SizeClass="half-width"></TextBox>
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="#(itemModel.Category)" OnSelectedItemChanged="#(OnSelectedItemChanged)"></DropDownList>
#*<SfDropDownList TItem="spGetNewsCategoriesResult" TValue="int" Placeholder="Select a category" #ref="sfDropDown" DataSource="categories" CssClass="inputItem half-width" #bind-Value="#(itemModel.Category)">
<DropDownListFieldSettings Text="name" Value="id" />
</SfDropDownList>*#
</div>
<div class="inputRow">
<CheckBox Checked="isChecked" Label="Suggest Dates This Should Be Active?" OnCheckChange="#(OnCheckChange)" SizeClass="one-third" Width="300px"></CheckBox>
#if (isChecked)
{
<DateTimePicker Label="Active From:" SelectedDate="#activeFrom" Width="450px" SizeClass="one-third"></DateTimePicker>
<DateTimePicker Label="Active To:" SelectedDate="#activeTo" Width="450px" SizeClass="one-third"></DateTimePicker>
}
</div>
<div class="inputRow">
<FileUploader MaxSize="#(MaxSize)" OnClearFiles="OnClearFiles" OnFileRemove="OnFileRemove" OnFileUpload="OnFileUpload" SizeClass="full-width" Width="400px"></FileUploader>
</div>
<RichTextEditor DeniedAttributes="#DeniedAttributes" text=#(itemModel.Content) Height="400px" Width="1600px"></RichTextEditor>
</div>
</ContentTemplate>
</DashboardLayoutPanel>
</DashboardLayoutPanels>
</SfDashboardLayout>
</div>
#if (ShowDialog)
{
<Dialog Title="Create News Item" message="#Message" OKText="#OKText" cancelText="#CancelText" OnClose="OnDialogClose">
</Dialog>
}
#code {
[CascadingParameter]
Task<AuthenticationState> authenticationStateTask { get; set; }
public string userName { get; set; }
private int MaxSize { get; set; }
private string title { get; set; }
private int selectedCategory { get; set; }
private string content { get; set; }
int count { get; set; }
private bool ShowDialog { get; set; } = false;
private string Message { get; set; } = "";
private string OKText { get; set; } = "";
private string CancelText { get; set; } = "";
public DateTime activeTo { get; set; }
public DateTime activeFrom { get; set; }
private bool isChecked { get; set; }
SaveNewsItemModel itemModel = new SaveNewsItemModel();
List<string> DeniedAttributes = new List<string>() {
"id", "title", "style"
};
Dictionary<string, object> textboxValidation = new Dictionary<string, object>(){
{"maxlength", "100"}
};
List<spGetNewsCategoriesResult> categories = new List<spGetNewsCategoriesResult>();
private async Task OnCheckChange(bool check)
{
isChecked = check;
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var authState = await authenticationStateTask;
var user = authState.User;
userName = user.Identity.Name;
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
categories = await procedures.spGetNewsCategoriesAsync();
MaxSize = 15 * 1024 * 1024;
}
}
private List<ToolbarItemModel> Tools = new List<ToolbarItemModel>() {
new ToolbarItemModel()
{
Command = ToolbarCommand.Bold
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Italic
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Underline
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Undo
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Redo
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.OrderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.UnorderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.FontColor
},
new ToolbarItemModel()
{
Command = ToolbarCommand.CreateLink
},
new ToolbarItemModel()
{
Command = ToolbarCommand.RemoveLink
}
};
private async Task OnFileUpload(UploadChangeEventArgs args)
{
foreach (var file in args.Files)
{
var fileName = file.FileInfo.Name;
using (var ms = file.Stream)
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
int count = 1;
string tempFileName = fileName;
while (fileService.TempFileExists(tempFileName))
{
tempFileName = $"({count}) {fileName}";
count++;
}
var bytes = ms.ToArray();
await fileService.SaveFileToTempAsync(bytes, tempFileName);
var mimetype = fileInfo.Extension;
itemModel.AddFile(fileName, mimetype, tempFileName, contextFactory);
}
}
}
private async Task OnClearFiles(ClearingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnFileRemove(RemovingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnSelectedItemChanged(ChangeEventArgs<int, spGetNewsCategoriesResult> eventArgs)
{
itemModel.Category = eventArgs.Value;
StateHasChanged();
}
private async Task OnSave()
{
if (isChecked)
{
itemModel.RequestDates(activeFrom, activeTo);
}
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
var addedFiles = await procedures.spCreateNewsItemAsync(JsonConvert.SerializeObject(itemModel), userName);
if (addedFiles.Count > 0)
{
foreach (var file in addedFiles)
{
await fileService.MoveTempToNewsAsync(file.fileName, file.newsID, file.fileID);
}
}
Message = "This has been successfully saved and is now pending review; pressing OK will refresh the page.";
OKText = "OK";
ShowDialog = true;
}
private async Task OnDialogClose(bool r)
{
ShowDialog = false;
NavManager.NavigateTo(NavManager.Uri, true);
}
}
My issue is that I'm getting an error at this point: OnSelectedItemChanged="#(OnSelectedItemChanged)"
The error is:
Cannot convert from method group to EventCallback
The hunting I've done seems to imply that I need to explicitly pass the type in as a parameter, instead of using object and trying to infer it at runtime - I'm just a bit woolly on the details of how to do that?
TValue being an int is something that shouldn't change anywhere. But the TItem could be just about anything (in this particular scenario it's a spGetNewsCategoriesResult) - how do I cater for that?
After much hunting and tinkering, I've found the solution. By changing the component to this:
#typeparam T
<div class="inputItem #(SizeClass) dropdown" style="min-width:#(Width);">
<SfDropDownList TItem="T" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="#(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="T" TValue="int" ValueChange="#OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="#(TextField)" Value="#(ValueField)" />
</SfDropDownList>
</div>
#code {
[Parameter]
public IEnumerable<T> DataSource { get; set; }
[Parameter]
public EventCallback<Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, T>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
And referencing it as such:
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="#(itemModel.Category)" OnSelectedItemChanged="#(OnSelectedItemChanged)" T="spGetNewsCategoriesResult"></DropDownList>
The error is resolved. Decided to answer my own question as opposed to just deleting it because I figured it'll probably pop up for people on their own search.

Calling multiple Blazor components with two binding

I have a blazor component:
#inject IJSRuntime JSRuntime
#using bullDocDBAcess
#using bullDocDBAcess.Models
#inject ITagData _db
#if(tags == null){
<p><em>Loading...</em></p>
}else{
<input type="text" #bind=#tags data-role="tagsinput" />
<input type="hidden" #onchange="onChange" value=#tags />
}
#code{
private string tags = "";
private List<TagModel> tagsList;
private Task<IJSObjectReference> _module;
private Task<IJSObjectReference> Module => _module ??= JSRuntime.InvokeAsync<IJSObjectReference>("import", "./components/tagsinput/tags_inputs_imports.js").AsTask();
[Parameter]
public int organizationId { get; set; } = 0;
[Parameter]
public int documentId { get; set; } = 0;
[Parameter] public EventCallback<Notification> OnTagsChange { get; set; }
private void onChange(Microsoft.AspNetCore.Components.ChangeEventArgs args)
{
tags = (string) args.Value;
OnTagsChange.InvokeAsync(new Notification(tags, documentId.ToString()));
}
protected override async Task OnInitializedAsync()
{
if(organizationId == 0){ //inserção
tags = "";
}else{
tagsList = await _db.GetTagsFromDocument(organizationId, documentId);
foreach (TagModel t in tagsList){
if(String.IsNullOrEmpty(tags)){
tags += t.nome;
}else{
tags += "," + t.nome;
}
}
}
var module = await Module;
await module.InvokeVoidAsync("loadScripts");
}
public async ValueTask DisposeAsync()
{
if (_module != null)
{
var module = await _module;
await module.DisposeAsync();
}
}
}
Basically, the idea is to use a bootstrap input tag (called using jsinterop) and include it several times for each product in a table. When tags and updated in this table, the database should be updates with the tags for that specific values.
The parent component:
(...)
#for(int i=0;i<this.document.Count;i++)
{
DocumentModel p = this.document.ElementAt(i);
<tr>
<td><TagManagement #key=#p.id organizationId=#p.organizacao_id documentId=#p.id OnTagsChange="TagsHandler" /></td>
</tr>
}
(...)
void TagsHandler(Notification notification)
{
Console.WriteLine("Hello"); //only for testing purposes
}
A two-way binding is implemented between the parent and child. However, o have no idea how to handle multiple component invocation with callback function for each one. When a new tag is inserted, only works on the first element of the table and the event is fired two times (because in this test I only have two rows).
I tried to include the key attribute but didn't work. Can you please help me?
Best regards

ASP.Net MVC: How to show checkboxes selection in webgrid column after postback data

I have developed a tabular UI with webgrid. i am showing student information through webgrid. i am showing multiple checkboxes for hobbies in each row of webgrid. when i select hobbies and click submit button then i saw hobbies selection is not going to action.
i guess there is my mistake in view model class design. please have a look at my code and tell me which area i need to change in code.
i want all hobbies should go to action when i click submit button and selected hobbies also should post to action for each student. a student may have multiple hobbies selected.
here is my viewcode
#model MVCCRUDPageList.Models.StudentListViewModel
#{
ViewBag.Title = "Index";
}
<h2>Student View Model</h2>
#using (Html.BeginForm("Index", "WebGridMoreControls", FormMethod.Post))
{
var grid = new WebGrid(Model.Students, canSort: false, canPage: false);
var rowNum = 0;
var SelectedHobbies = 0;
<div id="gridContent" style=" padding:20px; ">
#grid.GetHtml(
tableStyle: "table",
alternatingRowStyle: "alternate",
selectedRowStyle: "selected",
headerStyle: "header",
columns: grid.Columns
(
grid.Column(null, header: "Row No", format: item => rowNum = rowNum + 1),
grid.Column("ID", format: (item) => #Html.TextBoxFor(m => m.Students[rowNum - 1].ID, new { #class = "edit-mode" })),
grid.Column("Name", format: (item) => #Html.TextBoxFor(m => m.Students[rowNum - 1].Name, new { #class = "edit-mode" })),
grid.Column("Country", format: (item) =>
#Html.DropDownListFor(x => x.Students[rowNum - 1].CountryID,
new SelectList(Model.Country, "ID", "Name", item.CountryID),
"-- Select Countries--", new { id = "cboCountry", #class = "edit-mode" })),
grid.Column(header: "Hobbies",
format: #<text>
Hobbies
#foreach (var hobby in Model.Hobbies)
{
<div class="checkbox">
<label>
#Html.HiddenFor(e => e.Hobbies)
<input type="checkbox"
name="Hobbies"
value="#hobby.ID" /> #hobby.Name
</label>
</div>
}
</text>)
))
<input type="submit" value="Submit" />
</div>
}
Action code
public class WebGridMoreControlsController : Controller
{
// GET: WebGridMoreControls
public ActionResult Index()
{
StudentListViewModel osvm = new StudentListViewModel();
return View(osvm);
}
[HttpPost]
public ActionResult Index(StudentListViewModel oStudentListViewModel)
{
return View(oStudentListViewModel);
}
}
View model code
public class StudentListViewModel
{
public IList<Student> Students { get; set; }
public List<Country> Country { get; set; }
public IList<Hobby> SelectedHobbies { get; set; }
public IList<Hobby> Hobbies { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student{ID=1,Name="Keith",CountryID=0,Hobby=0},
new Student{ID=2,Name="Paul",CountryID=2,Hobby=0},
new Student{ID=3,Name="Sam",CountryID=3,Hobby=0}
};
Country = new List<Country>
{
new Country{ID=1,Name="India"},
new Country{ID=2,Name="UK"},
new Country{ID=3,Name="USA"}
};
Hobbies = new List<Hobby>
{
new Hobby{ID=1,Name="Football"},
new Hobby{ID=2,Name="Hocky"},
new Hobby{ID=3,Name="Cricket"}
};
}
}
Model code
public class Student
{
public int ID { get; set; }
[Required(ErrorMessage = "First Name Required")]
public string Name { get; set; }
public int CountryID { get; set; }
public int Hobby { get; set; }
}
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Hobby
{
public int ID { get; set; }
public string Name { get; set; }
}
please help me to rectify view, viewmodel and model class code. thanks
Add a viewmodel for Hobby:
public class HobbyViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
Modify your StudentListViewModel as following:
public class StudentListViewModel
{
public IList<Student> Students { get; set; }
public List<Country> Country { get; set; }
public IList<HobbyViewModel> Hobbies { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student{ID=1,Name="Keith",CountryID=0,Hobby=0},
new Student{ID=2,Name="Paul",CountryID=2,Hobby=0},
new Student{ID=3,Name="Sam",CountryID=3,Hobby=0}
};
Country = new List<Country>
{
new Country{ID=1,Name="India"},
new Country{ID=2,Name="UK"},
new Country{ID=3,Name="USA"}
};
Hobbies = new List<HobbyViewModel>
{
new HobbyViewModel{ID=1,Name="Football"},
new HobbyViewModel{ID=2,Name="Hocky"},
new HobbyViewModel{ID=3,Name="Cricket"}
};
}
Replace the foreach in the view as following:
#for (var i = 0; i < Model.Hobbies.Count; i++)
{
<div class="checkbox">
#Html.HiddenFor(m => m.Hobbies[i].ID)
#Html.CheckBoxFor(m => m.Hobbies[i].Checked)
#Html.LabelFor(m => m.Hobbies[i].Checked, Model.Hobbies[i].Name)
</div>
}

Dropdownlist in MVC 5

I want to display multiple DropDownList MVC5. I have corresponding classes for the same. Kindly advise for the same. I am totally new to MVC.Value (SysId) needs to stored in database and description will be displayed in page. There are 4 dropdownlists in web page. All classes have same properties.
Kindly help for the same ..
public ActionResult Registration()
{
RegistrationClass obj = new RegistrationClass();
obj = obj.getAllDropdown();
return View(obj);
}
public class RegistrationClass
{
[Required(ErrorMessage = "Please enter Name")]
[StringLength(50, ErrorMessage = "Name can not be more than 50 characters ")]
[DisplayName("Name")]
public string name { get; set; }
[Required(ErrorMessage = "Please select Gender")]
[DisplayName("Gender")]
public string gender { get; set; }
[Required(ErrorMessage = "Please enter Date of Birth")]
[DisplayName("Date of Birth")]
[DataType(DataType.Date,ErrorMessage = "Invalid Date ")]
public DateTime dob { get; set; }
[DisplayName("Caste")]
public List<Caste> lcaste { get; set; }
public int cast_id { get; set; }
public RegistrationClass getAllDropdown()
{
RegistrationClass obj = new RegistrationClass();
Connection cobj = new Connection();
string strConn = cobj.getConnectionString();
SqlConnection con = new SqlConnection(strConn);
SqlCommand cmd = new SqlCommand("proc_get_preference_dropdown", con);
SqlDataAdapter ada = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
ada.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
for (int i = 0; i < ds.Tables[0].Rows.Count;i++ )
{
if (ds.Tables[0].Rows[i]["code"].ToString() == "04") // Set Caste Preference
{
lcaste = new List<Caste>();
Caste obj1 = new Caste();
obj1.decription = ds.Tables[0].Rows[i]["description"].ToString();
obj1.sysId = Convert.ToInt32(ds.Tables[0].Rows[i]["id"]);
//obj1.isChecked = "N";
lcaste.Add(obj1);
obj.lcaste = lcaste;
continue;
}
}
}
return obj;
}
public class Caste
{
public int sysId { get; set; }
public string decription { get; set; }
public string isChecked { get; set; }
}
}
#model Matrimony.Models.RegistrationClass
<tr>
<td>
#Html.LabelFor(model => model.subcaste)
</td>
<td>
#Html.DropDownListFor(model => model.cast_id, new SelectList(Model.lcaste))
</td>
</tr>
Finally I resolved this by adding simple option tags.
<td>
<select name="cast_id">
<option value="0">--Select--</option>
#for (int i = 0; i < Model.lcaste.Count; i++)
{
<option value=#Model.lcaste[i].sysId>#Model.lcaste[i].decription</option>
}
</select>
</td>
The name property of select will get the selected item's value in post method. Accordingly we can handle it in controller. I also added property in my model class to store value as
public int cast_id { get; set; }
public List<Caste> lcaste { get; set; }

Passing a list to partialview, BeginCollectionItem()

I want to pass a list to PartialView that has BeginCollectionItem(). Here is the code,
InquiryOrderViewModel
public class InquiryOrderViewModel
{
public InquiryOrder InquiryOrder { get; set; }
public List<InquiryOrderDetail> InquiryOrderDetails { get; set; }
public List<InquiryComponentDetail> InquiryComponentDetails { get; set; }
}
InquiryComponentDetail model
public class InquiryComponentDetail
{
[Key]
public int InquiryComponentDetailId { get; set; }
public int DesignCodeId { get; set; }
public int QualityReferenceId { get; set; }
public int Height { get; set; }
public int Length { get; set; }
public int GscmComp { get; set; }
public int Wastage { get; set; }
public int TotalYarn { get; set; }
public virtual DesignCodeQltyRef DesignCodeQltyRef { get; set; }
}
InquiryOrderIndex View and the Script to render multiple items at once
#model eKnittingData.InquiryOrderViewModel
#using (Html.BeginForm("Save", "InquiryOrder"))
{
..........
<div id="cmpDts">
#foreach (var item in Model.InquiryComponentDetails)
{
}
</div>
..........
}
<script>
var prev;
$(document).on('focus', '.class03', function () {
prev = $(this).val();
}).on('change', '.class03', function () {
if (prev != "") {
$.ajax({
url: '#Url.Action("ComponentDts", "InquiryOrder")', // dont hard code your url's
type: "GET",
data: { DesignCdId: $(this).val() }, // pass the selected value
success: function (data) {
$('.cmpCls').last().replaceWith(data);
}
});
}
else {
$.ajax({
url: '#Url.Action("ComponentDts", "InquiryOrder")', // dont hard code your url's
type: "GET",
data: { DesignCdId: $(this).val() }, // pass the selected value
success: function (data) {
$(".class03 option[value='']").remove();
$('#cmpDts').append(data);
}
});
}
});
</script>
The _DetailEditorRow PartialView which gives ddls with class03 and in main view where it got appended.(This is just to show you what is class03)
#model eKnittingData.InquiryOrderDetail
#using eKnitting.Helpers
#using (Html.BeginCollectionItem("InquiryOrderDetails"))
{
<div class="editorRow">
#Html.DropDownListFor(a => a.ComponentId, (SelectList)ViewBag.CompList, "Select", new { Class = "class02" })
#Html.DropDownListFor(a => a.DesignCodeId, (SelectList)ViewBag.DCodeList, "Select", new { Class = "class03" })
#Html.TextBoxFor(a => a.NoOfParts, new { Class = "class01" })
delete
</div>
}
and in main view it got appended to
<div id="editorRows">
#foreach (var item in Model.InquiryOrderDetails)
{
Html.RenderPartial("_DetailEditorRow", item);
}
</div>
_ComponentDetails PartialView to render items(a list has been passed at once)
#model List<eKnittingData.InquiryComponentDetail>
#using eKnitting.Helpers
<div class="cmpCls">
#foreach(var icd in Model)
{
using (Html.BeginCollectionItem("InquiryComponentDetails"))
{
<div class="innerCmpCls">
#Html.DisplayFor(a => icd.DesignCodeId)
#Html.DisplayFor(a => icd.QualityReferenceId)
#Html.TextBoxFor(a => icd.Height, new { Class="clsHeight clsSameHL"})
#Html.TextBoxFor(a => icd.Length, new { Class = "clsLength clsSameHL" })
#Html.TextBoxFor(a => icd.GscmComp, new { Class = "clsGscmComp clsSameHL" })
#Html.TextBoxFor(A => icd.Wastage, new { Class = "clsWastage" })
#Html.ActionLink("Fds", "View", new { id = icd.QualityReferenceId }, new { #class = "myLink", data_id = icd.QualityReferenceId })
#Html.TextBoxFor(a => icd.TotalYarn, new { Class = "clsTotalYarn" })
<br>
<div class="popFds"></div>
</div>
}
}
</div>
ActionResult that Passes a list at once and returns the PartialView
public ActionResult ComponentDts(int DesignCdId)
{
var objContext = new KnittingdbContext();
var QltyRefList = objContext.DesignCodeQltyRefs.Where(a=>a.DesignCodeId==DesignCdId).ToList();
var iocdList = new List<InquiryComponentDetail>();
foreach(DesignCodeQltyRef dcqr in QltyRefList)
{
iocdList.Add(new InquiryComponentDetail {
DesignCodeId=dcqr.DesignCodeId,
QualityReferenceId=dcqr.QltyRefId
});
}
return PartialView("_ComponentDetails", iocdList);
}
ActionResult for GET
var objContext = new KnittingdbContext();
var newIovm = new InquiryOrderViewModel();
var newIo = new InquiryOrder();
var iocdL = new List<InquiryComponentDetail>();
newIovm.InquiryOrder = newIo;
newIovm.InquiryComponentDetails = iocdL;
return View(newIovm);
ActionResult for POST
public ActionResult Save(InquiryOrderViewModel inquiryOrderViewModel)
{
.........
}
When user selects an item from a dropdownlist(class03), the items related to that item are rendered to the view using the PartialView(_ComponentDetails') and get appended. Then user selects another item from another ddl(class03), the related items are rendered and appended after earlier appended ones. User can go on like this.
Rendering and appending items works fine. But for the PostBack even though i get the number of items in the list correctly(I checked it by putting a break point on POST ActionResult ) all items content show null values. Pls guide me in the correct way for achieving this. All help appreciated. Thanks!
Your _ComponentDetails view is generating form controls that have name attributes that look like (where ### is a Guid)
name="InquiryComponentDetail[###].icd.Height"
which does not match your model because typeof InquiryComponentDetail does not contain a property named icd. In order to bind to your model, your name attribute would need
name="InquiryComponentDetail[###].Height"
To generate the correct html, you will need 2 partials
_ComponentDetailsList.cshtml (this will be called by the ComponentDts() method using return PartialView("_ComponentDetailsList", iocdList);)
#model List<eKnittingData.InquiryComponentDetail>
<div class="cmpCls">
#foreach(var item in Model)
{
Html.RenderPartial("_ComponentDetails", item);
}
</div>
_ComponentDetails.cshtml
#model eKnittingData.InquiryComponentDetail
using (Html.BeginCollectionItem("InquiryComponentDetails"))
{
<div class="innerCmpCls">
#Html.DisplayFor(a => a.DesignCodeId)
#Html.DisplayFor(a => a.QualityReferenceId)
#Html.TextBoxFor(a => a.Height, new { #class="clsHeight clsSameHL"}) // use #class, not Class
#Html.TextBoxFor(a => a.Length, new { Class = "clsLength clsSameHL" })
....
</div>
}

Resources