I'm trying to come up with a Blazor implementation of using an array type model with multiple checkboxes.
Vue component:
<template>
<div>
<b-form-group label="Using sub-components:">
<b-form-checkbox-group id="checkbox-group-2" v-model="selected" name="flavour-2">
<b-form-checkbox value="orange">Orange</b-form-checkbox>
<b-form-checkbox value="apple">Apple</b-form-checkbox>
<b-form-checkbox value="pineapple">Pineapple</b-form-checkbox>
<b-form-checkbox value="grape">Grape</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
<div>Selected: <strong>{{ selected }}</strong></div>
</div>
</template>
<script>
export default {
data() {
return {
selected: [], // Must be an array reference!
}
}
}
</script>
Blazor component:
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="#id" name="#name" #onchange="#((ChangeEventArgs) => CheckedChanged(ChangeEventArgs, value))">
<label class="custom-control-label" for="#id">#label</label>
</div>
#code {
[Parameter]
public string id { get; set; }
[Parameter]
public string name { get; set; }
[Parameter]
public object value { get; set; }
[Parameter]
public List<object> model { get; set; }
[Parameter]
public EventCallback<List<object>> modelChanged { get; set; }
[Parameter]
public string label { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
}
protected void CheckedChanged(ChangeEventArgs args, object value)
{
if(!model.Any(i => i == value))
{
model.Add(value);
}
else
{
model.Remove(value);
}
}
}
Usage:
#foreach (string timezone in DistinctTimezones)
{
<BCheckbox #bind-model="#FilterTimezones" value="#timezone" label="#timezone" id="#(string.Format("timezone_{0}", timezone))" name="#(string.Format("timezone_{0}", timezone))" />
}
<p>Selected:</p>
#foreach(var timezone in FilterTimezones)
{
#timezone
}
#code {
protected List<string> DistinctTimezones { get; set; } = new List<string>{"Central", "Eastern"};
protected List<object> FilterTimezones { get; set; } = new List<object>();
}
When I check the checkboxes, the FilterTimezone object doesn't get updated with the values from checked checkboxes. Is this something that is already possible and I am overcomplicating it? I'm only aware of binding values to a non-collection type.
I'd do it like that (string is here just to simplify to get the idea)
CollectionCheckBox.razor
<input type="checkbox" #bind=isChecked />
#code
{
[Parameter]
public string Value { get; set; }
[Parameter]
public List<string> Model {get; set;}
private bool isChecked
{
get => Model.Any(el => el == Value);
set
{
if(value)
{
if(!Model.Any(el => el == Value) Model.Add(Value);
}
else
Model.Remove(Value);
}
}
}
Then in parent component you just do
<CollectionCheckBox Model="#Model", Value="Orange" />
<CollectionCheckBox Model="#Model", Value="Apple" />
<CollectionCheckBox Model="#Model", Value="Pineapple" />
<CollectionCheckBox Model="#Model", Value="Grape" />
#code
{
private List<string> Model = new List<string();
}
Related
I have the following custom date input:
CustomDateInput.razor
<input type="date"
class="#CssClass"
#bind=CurrentValue
#attributes=AdditionalAttributes />
#if (ValidationFor is not null)
{
<ValidationMessage For="#ValidationFor" />
}
#using System.Linq.Expressions
#inherits InputBase<DateTime?>
#code {
[Parameter] public Expression<Func<DateTime>>? ValidationFor { get; set; }
protected override bool TryParseValueFromString(string? value, out DateTime? result, out string validationErrorMessage)
{
result = CurrentValue;
validationErrorMessage = "";
return true;
}
}
FooPage.razor
<CustomInputDate #bind-Value="FooDate" ValidationFor="() => FooDate"></CustomInputDate>
FooPage.razor.cs
public DateTime? FooDate { get; set; }
However, I get errors:
How can I modify my CustomDateInput to allow for a Validation parameter to show a ValidationMessage?
The component. I've lifted the BuildRenderTree code from InputDate and added in the ValidationMessage component. You need to do it this way as I don't know a way to do the For binding in Razor. I've tied the For directly into the ValueExpression property of InputBase. You'll probably need to add a bit of formatting/css to prettify it.
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Rendering;
using System;
namespace Blazor.Starter.Components.TestComponents
{
public class CustomDate : InputDate<DateTime?>
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttribute(2, "type", "date");
builder.AddAttribute(3, "class", CssClass);
builder.AddAttribute(4, "value", BindConverter.FormatValue(CurrentValueAsString));
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.CloseElement();
builder.OpenComponent<ValidationMessage<DateTime?>>(6);
builder.AddAttribute(7, "For", this.ValueExpression);
builder.CloseComponent();
}
}
}
And a demo page. There's a button to manually set an error message in the validationMessageStore.
#page "/editortest"
<h3>EditorTest</h3>
<EditForm EditContext="editContext">
<div>
<CustomDate #bind-Value="model.Date"></CustomDate>
</div>
</EditForm>
<div class="m-3 p-3"><input #bind-value="_errormessage"><button class="btn btn-dark ms-2" #onclick="SetError">Set Error</button></div>
#code {
private dataModel model { get; set; } = new dataModel();
private EditContext editContext;
private string _errormessage { get; set; } = "Error in date";
protected override Task OnInitializedAsync()
{
this.editContext = new EditContext(model);
return base.OnInitializedAsync();
}
private void SetError( MouseEventArgs e)
{
var validationMessageStore = new ValidationMessageStore(this.editContext);
validationMessageStore.Clear();
var fi = new FieldIdentifier(this.model, "Date");
validationMessageStore.Add(fi, _errormessage);
}
public class dataModel
{
public string Email { get; set; }
public DateTime? Date { get; set; }
}
}
I have different way to reach the same result, you can try it!:
[Parameter] public bool ShowError { get; set; } = false;
<input type="date"
class="#CssClass"
#bind=CurrentValue
#attributes=AdditionalAttributes #onfocus="#(() => ShowError =true)"/>
#if(ShoError)
{
foreach(var msg in EditCotext.GetValidationMessages(FieldIdententifier))
{
<div class="alidation-message">#msg</div>
}
}
you need aslo to keep you code TryParseValueFromString
Anyone who knows how to initialize a list of checkboxes with only myPicketItemsStoredInDabase items marked as checked and all other items marked as not checked?
When I try the code example below all checkboxes is marked as checked. I have tried with different solutions but non of them ends upp with a list of checkboxes with the right items marked as checked.
public class CheckboxItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
public class ItemFromDatabase
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Page.razor
#foreach (var item in itemsToEdit)
{
<CheckBoxItemComponent Items="#itemsToEdit" Value="#item.Name" Item="#item" />
}
#code {
private IEnumerable<ItemFromDatabase> myPickedItemsStoredInDatabase;
private IEnumerable<ItemFromDatabase> allItems;
private List<CheckboxItem> itemsToEdit;
protected override async Task OnInitializedAsync()
{
myPickedItemsStoredInDatabase = await getFromJsonAsync<ItemFromDatabase>.....
allItems= await getFromJsonAsync<ItemFromDatabase>.....
InitCheckboxItems();
}
private void InitCheckboxItems()
{
itemsToEdit = new List<CheckboxItem>();
for (int i = 0; i < allItems.Length; i++)
{
CheckboxItem item = new CheckboxItem()
{
Id = allItems[i].Id,
Name = allItems[i].Name,
Checked = false
};
if (myPickedItemsStoredInDatabase.Any(p => p.Id == item.Id))
{
item.Checked = true;
}
itemsToEdit.Add(item);
}
}
}
CheckBoxItemComponent.razor
Activate multiple checkbox in a loop using Blazor
<input type="checkbox" #bind=isChecked />#Item.Name<br />
#code
{
[Parameter]
public string Value { get; set; }
[Parameter]
public CheckboxItem Item { get; set; }
[Parameter]
public List<CheckboxItem> Items { get; set; }
private bool isChecked
{
get => Items.Any(el => el.Name == Value);
set
{
if (value)
{
if (!Items.Any(el => el.Name == Value))
Items.Add(Item);
}
else
Items.Remove(Item);
}
}
}`
I don't understand what should happen in isChecked.
To set checked you can modify the CheckBoxItemComponent.razor like this:
<input type="checkbox" #bind=isChecked /> #Item.Name
<br />
#code
{
[Parameter]
public CheckboxItem Item { get; set; }
public bool isChecked
{
get => this.Item.Checked;
set => this.Item.Checked = value;
}
}
And use it in Page.razor:
#foreach (var item in itemsToEdit)
{
<CheckBoxItemComponent Item="#item" />
}
You need to be very careful when using:
#foreach (var item in itemsToEdit)
{
<CheckBoxItemComponent Items="#itemsToEdit" Value="#item.Name" Item="#item" />
}
This is Razor, not a C# code file. You can find and examine the C# pre-compiled file in obj/debug/net5.0/razor/pages/page.razor.g.cs if your razor file is called page in the pages directory.
if you consume item during the loop - like setting a value to a primitive type, all works fine. BUT any object references - such as Item="#item" - can easily end up pointing to the last item - the one after the foreach finishes. This may be what's happening - your testing checking on the last item. Try setting a local variable in the loop to item and then using that for the component parameters. See below.
#foreach (var item in itemsToEdit)
{
var thisitem = item;
<CheckBoxItemComponent Items="#itemsToEdit" Value="#thisitem.Name" Item="#thisitem" />
}
I giv up to solve this. i dont't know what is wrong with my code, if the problem is instance of object, i tried to give my pagging class an instance but still no clue.
This is my index class;
<DataGridComponent TItem="Employee"
DataItems="listEmployee"
Columns="columnDefinitions"
Paging="#(new PagingConfig {
Enabled = true,
CustomPager = true,
PageSize = 3
})">
<CustomPager>
<button class="btn btn-primary" #onclick="PrevPage"> Prev </button>
<span> Page
<input type="number" min="1"#bind-value="#DataGrid.currentPageNumber"/>
of #DataGrid.MaxPageNumber </span>
<button class="btn btn-primary" #onclick="NextPage"> Next </button>
</CustomPager>
</DataGridComponent>
#code{
private DataGridComponent<Employee> DataGrid;
private List<Employee> listEmployee;
private List<ColumnDefinition> columnDefinitions;
protected override void OnInitialized()
{
base.OnInitialized();
Initialize();
}
private void PrevPage()
{
DataGrid.GoToPrevPage();
}
private void NextPage()
{
DataGrid.GoToNextPage();
}
this is my DataGrid class
<div class="level">
<div class="level-left"></div>
<div class="level-right">
<div class="level-item">
#if (Paging != null && Paging.Enabled)
{
#if (Paging.CustomPager)
{
#CustomPager
}
else
{
<span #onclick="GoToPrevPage"><b><</b>Prev</span>
<span> #currentPageNumber of #Paging.MaxPageNumber(DataItems.Count)
</span>
<span #onclick="GoToNextPage"><b>Next></b></span>
}
}
</div>
</div>
</div>
#code {
[Parameter]
public int currentPageNumber { get; set; } = 1;
[Parameter]
public List<TItem> DataItems { get; set; }
[Parameter]
public List<ColumnDefinition> Columns { get; set; }
[Parameter]
public PagingConfig Paging { get; set; } = new PagingConfig();
[Parameter]
public RenderFragment CustomPager { get; set; }
public void GoToPrevPage()
{
currentPageNumber = Paging.PrevPageNumber(currentPageNumber);
}
public void GoToNextPage()
{
currentPageNumber = Paging.NextPageNumber(currentPageNumber, DataItems.Count);
}
public int MaxPageNumber { get => Paging.MaxPageNumber(DataItems.Count); }
}
and this is my my pagingconfig
public class PagingConfig
{
public bool Enabled { get; set; }
public int PageSize { get; set; }
public bool CustomPager { get; set; }
public int NumOfItemsToSkip(int pageNumber)
{
if (Enabled)
{
return (pageNumber - 1) * PageSize;
}
else
return 0;
}
public int NumOfItemsToTake(int totalItemCount)
{
if (Enabled)
{
return PageSize;
}
return totalItemCount;
}
public int PrevPageNumber(int currentPageNumber)
{
if (currentPageNumber > 1)
return currentPageNumber - 1;
else
return 1;
}
public int NextPageNumber(int currentPageNumber, int totalItemsCount)
{
if (currentPageNumber < MaxPageNumber(totalItemsCount))
{
return currentPageNumber + 1;
}
else
{
return currentPageNumber;
}
}
public int MaxPageNumber(int totalItemcount)
{
int maxPageNumber;
double numberOfPage = (double)totalItemcount / (double)PageSize;
if (numberOfPage == Math.Floor(numberOfPage))
{
maxPageNumber = (int)numberOfPage;
}
else
{
maxPageNumber = (int)numberOfPage + 1;
}
return maxPageNumber;
}
}
}
the problem is, when i tried to set enabled into true, the pagging config should get the value of true from index. But it's said not set the instance object yet. i tried to put in, the new Pagging instance into grid component but still no clue :(
DataGrid needs to be assigned. I think you want this:
<DataGridComponent #ref="DataGrid" TItem="Employee" ...>
...
</DataGridComponent>
the reference is to the variable in this line:
private DataGridComponent<Employee> DataGrid;
It's just the #ref="DataGrid" missing in the markup of your DataGridComponent. Therefore, your private DataGrid variable is null.
In Index.razor you set up a DataGridComponent instance in Razor and then declare another one in the code section private DataGridComponent<Employee> DataGrid. These are two unlinked instances of DataGridComponent. To reference DataGrid to your razor declared version you need to use #ref as below.
<DataGridComponent TItem="Employee"
DataItems="listEmployee"
Columns="columnDefinitions"
#ref = "this.DataGrid"
Paging="#(new PagingConfig {
Enabled = true,
CustomPager = true,
PageSize = 3
})">
I've got a blazor project and created some tab components. I want these tabs to be dynamic so I can add and take away from them when new values get added to the view model.
Adding works fine but once the page has rendered and I try to remove one of the tabs it doesn't update the tab header and I can't work out why as the view model has changed so surely the page should re-render?
The count updates correctly but the page is not re-rendering to remove the tab. I'm sure I'm fundamentally missing something.
I've created a fiddle to show this better than I can explain: https://blazorfiddle.com/s/fduse7v2
Thanks in advance.
EDIT - Added code from fiddle as requested.
//INDEX
#page "/"
<h1>Tab Issue</h1>
Data Count: #View.Data.Count
<button #onclick='(() => Add($"Tab {(View.Data.Count + 1)}"))'>Add Tab</button>
<TabControl>
#foreach (var data in View.Data)
{
<TabPage Text='#data'>
#data <button #onclick="(() => Remove(data))">Remove</button>
</TabPage>
}
</TabControl>
#code{
public class ViewModel{
public List<string> Data = new List<string>{
"Tab 1","Tab 2","Tab 3"
};
}
public ViewModel View { get; set; }
protected override async Task OnInitializedAsync()
{
View = new ViewModel();
}
public void Add(string data)
{
View.Data.Add(data);
StateHasChanged();
}
public void Remove(string data)
{
View.Data.Remove(data);
StateHasChanged();
}
}
//TabPage
#if (Parent.ActivePage == this)
{
<div class='tab-page #($"tab-{this.Text.Replace(" ","-").ToLower()}")'>
#ChildContent
</div>
}
#code {
[CascadingParameter] public TabControl Parent { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public string Id { get; set; }
[Parameter] public string Text { get; set; }
protected override void OnInitialized()
{
if (Parent == null)
throw new ArgumentNullException(nameof(Parent), "TabPage must exist within a TabControl");
Parent.AddPage(this);
base.OnInitialized();
}
}
//Tab Control
<div style="display:flex;">
<ul style="display:flex;">
#foreach (TabPage tabPage in Pages)
{
<li style="margin-right:20px; #GetActiveClass(tabPage); list-style:none; " #onclick=#(() => ActivatePage(tabPage))>#tabPage.Text</li>
}
</ul>
</div>
<CascadingValue Value="this">
#ChildContent
</CascadingValue>
#code {
[Parameter]
public RenderFragment ChildContent { get; set; }
public TabPage ActivePage { get; set; }
public List<TabPage> Pages { get; } = new List<TabPage>();
public void AddPage(TabPage tabPage)
{
Pages.Add(tabPage);
if (Pages.Count == 1)
ActivePage = tabPage;
StateHasChanged();
}
public string GetActiveClass(TabPage page)
{
return page == ActivePage ? "border-bottom: 2px solid #263238;" : "";
}
public void ActivatePage(TabPage page)
{
System.Console.WriteLine($"Activating Page: {page.Text} - {page.Id}.");
ActivePage = page;
}
}
I have two models as shown below:
One:
[Validator(typeof(BlogPostValidator))]
public partial class BlogPostModel : BaseNopEntityModel
{
public BlogPostModel()
{
Tags = new List<string>();
Comments = new List<BlogCommentModel>();
AddNewComment = new AddBlogCommentModel();
}
public string SeName { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public bool AllowComments { get; set; }
public int NumberOfComments { get; set; }
public DateTime CreatedOn { get; set; }
public IList<string> Tags { get; set; }
public IList<BlogCommentModel> Comments { get; set; }
public AddBlogCommentModel AddNewComment { get; set; }
public BlogCommentModel blogcommentmodel { get; set; }
}
two:
public partial class BlogCommentModel : BaseNopEntityModel
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public string CustomerAvatarUrl { get; set; }
public string CommentText { get; set; }
public DateTime CreatedOn { get; set; }
public bool AllowViewingProfiles { get; set; }
public int CommentParentID { get; set; }
public IList<BlogComment> ChildCommentList { get; set; }//netra
}
I want to make use of ChildCommentList from BlogCommentModel to show nested comments.
My View:
#model BlogPostModel
#using Nop.Web.Models.Blogs;
#if (Model.AllowComments)
{
<div class="clear">
</div>
<fieldset class="new-comment" id="addcomment">
<legend class="title">#T("Blog.Comments.LeaveYourComment")</legend>
#using (Html.BeginForm())
{
<div>
<div class="message-error">#Html.ValidationSummary(true)</div>
#{
string result = TempData["nop.blog.addcomment.result"] as string;
}
#if (!String.IsNullOrEmpty(result))
{
<div class="result">#result</div>
}
<div class="forms-box">
<div class="inputs">
#Html.LabelFor(model => model.AddNewComment.CommentText)
<div class="input-box">
#Html.TextAreaFor(model => model.AddNewComment.CommentText, new { #class = "comment-text" })
</div>
#Html.ValidationMessageFor(model => model.AddNewComment.CommentText)
</div>
#if (Model.AddNewComment.DisplayCaptcha)
{
<div class="captcha-box">
#Html.Raw(Html.GenerateCaptcha())
</div>
<div class="clear">
</div>
}
</div>
<div class="clear">
</div>
<div class="buttons">
<input type="submit" name="add-comment" class="button-1 blog-post-add-comment-button" value="#T("Blog.Comments.SubmitButton")" />
</div>
</div>
}
</fieldset>
if (Model.Comments.Count > 0)
{
<div class="clear">
</div>
<div class="comment-list">
<div class="title">
#T("Blog.Comments")
</div>
<div class="clear">
</div>
#foreach (var comment in Model.Comments)
{
<div class="blog-comment">
<div class="comment-info">
<div class="user-info">
#if (comment.AllowViewingProfiles)
{
#(comment.CustomerName)
}
else
{
<span class="username">#(comment.CustomerName)</span>
}
<div class="avatar">
#if (!String.IsNullOrEmpty(comment.CustomerAvatarUrl))
{
<img src="#(comment.CustomerAvatarUrl)" class="avatar-img" title="avatar" alt="avatar" />
}
</div>
</div>
</div>
<div class="comment-content">
<div class="comment-time">
#T("Blog.Comments.CreatedOn"): <span class="stat-value">#comment.CreatedOn.ToString("g")</span>
</div>
<div class="comment-body">
#Html.Raw(Nop.Core.Html.HtmlHelper.FormatText(comment.CommentText, false, true, false, false, false, false))
</div>
</div>
#Html.Widget("blogpost_page_inside_comment")
</div>
<div class="clear">
</div>
<div>
#foreach(var childcomments in Model.blogcommentmodel.ChildCommentList)
{
#Html.Raw(Nop.Core.Html.HtmlHelper.FormatText(childcomments.CommentText, false, true, false, false, false, false))
}
</div>
}
<div class="buttons">
<input type="submit" id="replyto" name="reply-comment" class="button-1 blog-post-add-comment-button" value="#T("Blog.Comments.ReplyButton")" />
</div>
</div>
}
}
Error occurred on :
#foreach(var childcomments in Model.blogcommentmodel.ChildCommentList)
{
#Html.Raw(Nop.Core.Html.HtmlHelper.FormatText(childcomments.CommentText, false, true, false, false, false, false))
}
</div>
}
NullReferenceException was unhandled by usercode -{"Object reference not set to an instance of an object."}
My controller code for ActionResult:
public ActionResult BlogPost(int blogPostId)
{
if (!_blogSettings.Enabled)
return RedirectToRoute("HomePage");
var blogPost = _blogService.GetBlogPostById(blogPostId);
if (blogPost == null ||
(blogPost.StartDateUtc.HasValue && blogPost.StartDateUtc.Value >= DateTime.UtcNow) ||
(blogPost.EndDateUtc.HasValue && blogPost.EndDateUtc.Value <= DateTime.UtcNow))
return RedirectToRoute("HomePage");
var model = new BlogPostModel();
**PrepareBlogPostModel(model, blogPost, true);**
return View(model);
}
Below method called in above ActionResult:
[NonAction]
protected void PrepareBlogPostModel(BlogPostModel model, BlogPost blogPost, bool prepareComments)
{
if (blogPost == null)
throw new ArgumentNullException("blogPost");
if (model == null)
throw new ArgumentNullException("model");
model.Id = blogPost.Id;
model.SeName = blogPost.GetSeName();
model.Title = blogPost.Title;
model.Body = blogPost.Body;
model.AllowComments = blogPost.AllowComments;
model.CreatedOn = _dateTimeHelper.ConvertToUserTime(blogPost.CreatedOnUtc, DateTimeKind.Utc);
model.Tags = blogPost.ParseTags().ToList();
model.NumberOfComments = blogPost.ApprovedCommentCount;
model.AddNewComment.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnBlogCommentPage;
if (prepareComments)
{
// var blogchildcomment = _blogService.GetAllChildComments();
var blogComments = blogPost.BlogComments.Where(pr => pr.IsApproved).OrderBy(pr => pr.CreatedOnUtc);
foreach (var bc in blogComments)
{
var commentModel = new BlogCommentModel()
{
Id = bc.Id,
CustomerId = bc.CustomerId,
CustomerName = bc.Customer.FormatUserName(),
CommentText = bc.CommentText,
CreatedOn = _dateTimeHelper.ConvertToUserTime(bc.CreatedOnUtc, DateTimeKind.Utc),
AllowViewingProfiles = _customerSettings.AllowViewingProfiles && bc.Customer != null && !bc.Customer.IsGuest(),
CommentParentID = bc.CommentParentID,//Netra
};
//Netra
var comments = _blogService.GetBlogComments(bc.CommentParentID);
if (comments != null)
{
commentModel.ChildCommentList = new List<BlogComment>();
foreach (var cmnt in comments)
{
commentModel.ChildCommentList.Add(cmnt);
}
}
if (_customerSettings.AllowCustomersToUploadAvatars)
{
var customer = bc.Customer;
string avatarUrl = _pictureService.GetPictureUrl(customer.GetAttribute<int>(SystemCustomerAttributeNames.AvatarPictureId), _mediaSettings.AvatarPictureSize, false);
if (String.IsNullOrEmpty(avatarUrl) && _customerSettings.DefaultAvatarEnabled)
avatarUrl = _pictureService.GetDefaultPictureUrl(_mediaSettings.AvatarPictureSize, PictureType.Avatar);
commentModel.CustomerAvatarUrl = avatarUrl;
}
model.Comments.Add(commentModel);
}
}
}
BlogComment is class which describes the properties.
public partial class BlogComment : CustomerContent
{
/// <summary>
/// Gets or sets the comment text
/// </summary>
public virtual string CommentText { get; set; }
/// <summary>
/// Gets or sets the blog post identifier
/// </summary>
public virtual int BlogPostId { get; set; }
/// <summary>
/// Gets or sets the blog post
/// </summary>
public virtual BlogPost BlogPost { get; set; }
public virtual int CommentParentID { get; set; } //netra
}
So you have a null somewhere - you need to find out what's null and make it so it's not null; or change the Razor code to something like:
#if(Model.blogcommentmodel != null && Model.blogcommentmodel.ChildCommentList != null)
{
#* original #foreach statement here *#
}
My first guess would be that you need to create a default constructor for your model. In the constructor, set ChildCommentList = new List<BlogComment>();.
It looks like you didn't set that in your Controller, which the way that you have your Model set up, would cause ChildCommentList to still be null. With that default constructor, you won't have to worry about your code breaking like this if you miss setting it somewhere.
To answer wnetra's comment, the constructor is on the BlogcommentModel class. So you'll need something like this:
public class BlogcommentModel {
/* All of the property declarations */
public IList<BlogComment> ChildCommentList { get; set; }
/* Constructor for the BlogcommentModel class */
public BlogcommentModel() {
ChildcommentList = new List<BlogComment>();
}
}
Any reference objects should always be instantiated inside a default constructor to ensure they won't be null when trying to reference them in code.
Also see Andras' answer with regard to always doing != null when trying to reference reference objects in your code.
I have removed following code from BlogCommenmodel :
//public int CommentParentID { get; set; }
//public IList<BlogComment> ChildCommentList { get; set; }//netra
//public BlogCommentModel()
//{
// ChildCommentList = new List<BlogComment>();
//}
and introduced in BlogPostModel
[Validator(typeof(BlogPostValidator))]
public partial class BlogPostModel : BaseNopEntityModel
{
public BlogPostModel()
{
Tags = new List<string>();
Comments = new List<BlogCommentModel>();
AddNewComment = new AddBlogCommentModel();
ChildCommentList = new List<BlogComment>();
}
public string SeName { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public bool AllowComments { get; set; }
public int NumberOfComments { get; set; }
public DateTime CreatedOn { get; set; }
public IList<string> Tags { get; set; }
public IList<BlogCommentModel> Comments { get; set; }
public AddBlogCommentModel AddNewComment { get; set; }
//Netra
public int CommentParentID { get; set; }
public IList<BlogComment> ChildCommentList { get; set; }//netra
// public BlogCommentModel blogcommentmodel { get; set; }
}
IN View:
<div>
#if (Model.ChildCommentList != null)
{
foreach (var childcomments in Model.ChildCommentList)
{
#Html.Raw(Nop.Core.Html.HtmlHelper.FormatText(childcomments.CommentText, false, true, false, false, false, false))
}
}
</div>