knockoutjs with mvc collection model binding - asp.net-mvc-3

I'm using knockoutjs to render a collection of items. After allowing the user to do some inline editing I need to post the collection back to the server. However, the collection isn't being populated on the server because I'm not using the name="[0].Blah" naming convention. Does anyone know how to either render name attributes like this using knockoutjs OR how to create a model binder that will allow me to extract the values from the ValueProvider?
You can see a screenshot of the ValueProvider during debugging below.
http://i.imgur.com/zSU5Z.png
Here is my managed ViewModel:
public class FundLevelInvestmentUploadResult
{
public string FileName { get; set; }
public IList<FundLevelInvestmentViewModel> Items { get; set; }
public int NumberOfErrors { get; set; }
public bool ShowErrorsOnly { get; set; }
public FundLevelInvestmentUploadResult()
{
Items = new List<FundLevelInvestmentViewModel>();
}
}
Here is the managed class for "Items":
public class FundLevelInvestmentViewModel
{
private string _fund;
private string _fundType;
private string _date;
private string _netOfWaivedFees;
private string _waivedFees;
private string _bcip;
private string _fxRate;
public uint RowIndex { get; set; }
public int? DealCode { get; set; }
public bool DealCodeIsValid { get; set; }
public string Fund
{
get { return _fund; }
set { _fund = GetString(value); }
}
public bool FundIsValid { get; set; }
public string FundType
{
get { return _fundType; }
set { _fundType = GetString(value); }
}
public bool FundTypeIsValid { get; set; }
public string DateOfInvestment
{
get { return _date; }
set { _date = GetString(value); }
}
public bool DateOfInvestmentIsValid { get; set; }
public string NetOfWaivedFees
{
get { return _netOfWaivedFees; }
set { _netOfWaivedFees = GetString(value); }
}
public bool NetOfWaivedFeesIsValid { get; set; }
public string WaivedFee
{
get { return _waivedFees; }
set { _waivedFees = GetString(value); }
}
public bool WaivedFeeIsValid { get; set; }
public string BCIP
{
get { return _bcip; }
set { _bcip = GetString(value); }
}
public bool BCIPIsValid { get; set; }
public string ExchangeRateToUSD
{
get { return _fxRate; }
set { _fxRate = GetString(value); }
}
public bool ExchangeRateToUSDIsValid { get; set; }
public string FileName { get; set; }
private IList<string> _errors;
public IList<string> Errors
{
get { return _errors ?? (_errors = new List<string>());}
set { _errors = value; }
}
public bool Show { get; set; }
public FundLevelInvestmentViewModel()
{
Errors = new List<string>();
Show = true;
}
// knockoutjs is returning "null" instead of "" for a null object when calling ko.mapping.fromJS
private string GetString(string value)
{
if (value == "null")
return string.Empty;
return value;
}
}
Here is my knockout viewModel:
var viewModel = {
FileData: ko.observableArray([]),
validateFile: function (file, event) {
$.ajax({
type: 'post',
url: newUrl,
data: ko.mapping.toJS(file)
}).done(function (data) {
var newFile = ko.mapping.fromJS(data);
var index = file.Items.indexOf(file);
viewModel.FileData.replace(file, newFile);
});
}
};

If you are using version 2.1.0.0 or later of knockout you can render the name attribute as follows from an observable array.
<input data-bind='attr: { name: "Items["+$index()+"].DealCode"}' />

Related

Bar Chart of ChartJS does not Render

I am having trouble displaying bar chart from my ASP.NET-MVC-5 application. I witnessed the JSON comes out correctly (sample is applied below), and as per documentation I had included all, yet the output comes as below:
HTML:
<div class="jumbotron">
<h1>Another Chart</h1>
<canvas id="barChartLoc" width="600" height="400"></canvas>
</div>
This is the Script which calls the Controller, which returns a JSON:
<script type="text/javascript">
function chartFYRevenue() {
$.ajax({
url: '#Url.Action("GetLast5FYRevenueAnalysis", "Utility")',
cache: true
})
.done(function (data) {
var mybarChartLoc = new Chart(document.getElementById("barChartLoc").getContext("2d")).Bar(data);
})
.fail(function () {
alert("Ajax failed to fetch data");
});
}
$(document).ready(function () {
//auto load on page load
chartFYRevenue();
});
</script>
This is the Controller which returns a JSON. I have tested this and things are fine here as well:
public JsonResult GetLast5FYRevenueAnalysis()
{
Models.Chart.BarChartDBContext chartDB = new Models.Chart.BarChartDBContext();
return Json(chartDB.Test, JsonRequestBehavior.AllowGet);
}
This is the Modeler class where I build the Chart Data dynamically:
public class ChartDataSets
{
public string label { get; set; }
public string fillColor { get; set; }
public string highlightFill { get; set; }
public string highlightStroke { get; set; }
public string strokeColor { get; set; }
public string data { get; set; }
}
public class BarChartModel
{
public string labels { get; set; }
public List<ChartDataSets> datasets { get; set; }
}
public class BarChartDBContext : Models.DBHelper.DBHelperClass
{
public BarChartModel GetLast5FInancialYearRevenue
{
get { return getLast5FinancialYearRevenue(); }
}
public BarChartModel Test
{
get { return test(); }
}
private BarChartModel test()
{
List<ChartDataSets> _datasets = new List<ChartDataSets>();
BarChartModel _barChartModel = null;
_datasets.Add(new ChartDataSets()
{
data = string.Format("[{0}]", "10,5,25,35"),
fillColor = "rgba(220,220,220,0.5)",
highlightFill = "rgba(220,220,220,0.75)",
highlightStroke = "rgba(220,220,220,1)",
strokeColor = "rgba(220,220,220,0.8)",
label = "s1"
});
_barChartModel = new BarChartModel();
_barChartModel.labels = string.Format("[{0}]", "c1,c2,c3,c4");
_barChartModel.datasets = _datasets;
return _barChartModel;
}
}
JSON Data Sample:
{
"labels": "[c1,c2,c3,c4]",
"datasets": [
{
"label": "s1",
"fillColor": "rgba(220,220,220,0.5)",
"highlightFill": "rgba(220,220,220,0.75)",
"highlightStroke": "rgba(220,220,220,1)",
"strokeColor": "rgba(220,220,220,0.8)",
"data": "[10,5,25,35]"
}
]
}
Update:
I modified my ChartDataSet and BarChartModel Class to the following:
public class ChartDataSets
{
public string label { get; set; }
public string fillColor { get; set; }
public string highlightFill { get; set; }
public string highlightStroke { get; set; }
public string strokeColor { get; set; }
public string[] data { get; set; }
}
public class BarChartModel
{
public string[] labels { get; set; }
public List<ChartDataSets> datasets { get; set; }
}
Your JSON data generated is incorrect. The right output should be
"{
"labels": ["c1","c2","c3","c4"],
"datasets": [
{
"label":"s1",
"fillColor":"rgba(220,220,220,0.5)",
"highlightFill":"rgba(220,220,220,0.75)",
"highlightStroke":"rgba(220,220,220,1)",
"strokeColor":"rgba(220,220,220,0.8)",
"data":[10,5,25,35]
}
]
}"

insert data in two class(table) in Entity Framwork by rerlation many to many

i create two class in my model and create relation many to many by Entity
in sql my classes is created tables Properly
when i try to insert data in this table get show error "Object reference not set to an instance of an object." my cod is:
public class News
{
public int ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Date { get; set; }
public virtual Picture Picture { get; set; }
public virtual NewsType NewsType { get; set; }
public ICollection<Tag> Tag { get; set; }
public News(int id, string title, string content, DateTime date)
{
this.ID = id;
this.Title = title;
this.Content = content;
this.Date = date;
}
public News()
{
}
}
public class Tag
{
public int ID { get; set; }
public string Title { get; set; }
public ICollection<News> News { get; set; }
public Tag()
{
}
}
public class DatabaseContext : DbContext
{
public DatabaseContext()
: base("News")
{
}
static DatabaseContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DatabaseContext>());
}
public DbSet<News> newsInfo { get; set; }
public DbSet<Picture> pictures { get; set; }
public DbSet<NewsType> Types { get; set; }
public DbSet<Tag> Tags { get; set; }
}
[HttpPost]
public ActionResult AddNews(NewsViewModel newsInfo)
{
using (Models.DatabaseContext dbContext = new DatabaseContext())
{
ViewData["Type"] = new SelectList(dbContext.Types.ToList(), "Id", "Title");
}
if (!ModelState.IsValid)
{
return View();
}
else
{
Models.DatabaseContext dbContext = new Models.DatabaseContext();
Models.News news = new Models.News();
news.Title = newsInfo.Title;
news.Content = newsInfo.Content;
news.Date = DateTime.Now;
string newsinput = newsInfo.Tag.cleanTag();
string[] tags = new string[] { };
if (newsinput != null)
{
tags = newsinput.Split(',');
}
foreach (string item in tags)
{
Tag findTag = dbContext.Tags.Where(x => x.Title == item).FirstOrDefault();
if (findTag != null)
{
news.Tag.Add(findTag)
////////////////////////show error in this line
}
}
news.NewsType = dbContext.Types.Find(Convert.ToInt32(Request.Form["rdb"]));
dbContext.newsInfo.Add(news);
dbContext.SaveChanges();
return View();
}

How do I custom validate collections withing MVC5

I am building an MVC5 application and I have the following viewmodels:
public class UserPartyViewModel
{
public UserPartyViewModel()
{
Entitlements = new Collection<AssignedClaims>();
}
public Guid PartyID { get; set; }
public string PartyName { get; set; }
public ICollection<AssignedClaim> AssignedClaims{ get; set; }
}
public class AssignedClaims
{
public AssignedClaims()
{
ClaimValues = new Collection<AssignedClaimValue>();
}
public string Name { get; set; }
public int Max { get; set; }
public int Min { get; set; }
public ICollection<AssignedClaimValue> ClaimValues { get; set; }
}
public class AssignedClaimValue
{
public Guid ClaimValueID { get; set; }
public string ClaimValue { get; set; }
public bool Assigned { get; set; }
}
Contained in the UserPartyViewModel will always be an assignedclaim with a name of "Security" and the assignedclaimvalue with a claimvalue of "User"
If the ClaimValue of user is Assigned then I need to validate the rest of the model. If it is not then no further validation should take place.
Within AssignedClaims there is a min and max, these are the minimum and maximum number of assignedclaimvalues that should be Assigned.
I have tried to use AttributeValidate cannot stop it validating the rest of the model.
I have also looked at the IValidatableObject interface but also can't work out how to control the validation of the child collections depending on the User claim.
What's the best way to achieve this?
Found a solution which appears to do what I want:
public class UserPartyViewModel : IValidatableObject
{
public UserPartyViewModel()
{
Entitlements = new Collection<AssignedClaims>();
}
public string AccessLevel { get; set; }
public Guid PartyID { get; set; }
public string PartyName { get; set; }
public ICollection<AssignedClaims> Entitlements { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var isUser = Entitlements.Any(c => c.Name == "Security" && c.ClaimValues.Any(v => v.Assigned == true && v.ClaimValue == "User"));
if (isUser)
{
int i = 0;
foreach (var result in Entitlements)
{
yield return result.Validate(i++);
}
}
else
{
yield return ValidationResult.Success;
}
}
}
public class AssignedClaims
{
public AssignedClaims()
{
ClaimValues = new Collection<AssignedClaimValue>();
}
public string Name { get; set; }
public string Description { get; set; }
public int Max { get; set; }
public int Min { get; set; }
public ICollection<AssignedClaimValue> ClaimValues { get; set; }
public ValidationResult Validate(int item)
{
int min = Min;
int max = (ClaimValues.Count() < Max) ? ClaimValues.Count() : Max;
int assignedCount = ClaimValues.Where(i => i.Assigned == true).Count();
if (!(min <= assignedCount && assignedCount <= max))
{
string errMessage = String.Format("{2} should have between {0} and {1} Security Claims checked.", min, max, Name);
return new ValidationResult(errMessage, new[] { string.Format("Entitlements[{0}]", item) });
}
else
{
return ValidationResult.Success;
}
}
}
The only issue I had was trying to get the error messages appearing in the correct place. In my view for assignedclaims I added:
#Html.ValidationMessageFor(model => model, "", new { #class = "text-danger" })
and passed the iteration through to the validate function on assignedclaim to ensure it was added to the correct member.

Resolving LINQ Error: Missing Query Pattern Implementation

I am receiving a strange LINQ error when trying to filter a collection in my view.
This is my model:
public class adminEditProductsPricelistProductsVM
{
public Product Product { get; set; }
public PricelistProduct pricelistProduct { get; set; }
}
This is my view:
#using Pp.Lib.Models;
#using System.Linq;
#model PpLib.viewModels.admin.adminEditProductsPricelistProductsVM
#{
//get corresponding PricelistProduct
PricelistProduct thisPP = new ProofPixLib.Models.PricelistProduct();
thisPP = (from x in Model.pricelistProduct where x.ProductId == Model.Product.ProductId select x).FirstOrDefault();
}
the Model.pricelistProduct line is underlined in VS and it says the following error:
Could not find an implementation of the query pattern for source type Pp.Lib.Models.PricelistProduct. 'Where' not found.
Thanks in advance for your help!
UPDATE
As requested - here is the code for the PricelistProduct model.
public partial class PricelistProduct
{
public PricelistProduct()
{
this.PricelistProductOptions = new List<PricelistProductOption>();
}
[ReadOnly(true)]
[Display(Name = "Pricelist Product ID")]
public int PricelistProductId { get; set; }
[ReadOnly(true)]
[Display(Name = "Pricelist ID")]
public int PricelistId { get; set; } //foregin key
public virtual Pricelist Pricelist { get; set; }
[ReadOnly(true)]
[Display(Name = "Product ID")]
public int ProductId { get; set; } //foreign key
public virtual Product Product { get; set; }
[HiddenInput]
public int ProductCategoryId { get; set; } // not a FK but data only
public virtual ProductCategory ProductCategory { get; set; }
[Display(Name = "Use formula")]
private bool _UsesFormula = true;
public bool UsesFormula { get { return _UsesFormula; } set { _UsesFormula = value; } }
private decimal _Price = 0;
public decimal Price { get { return _Price; } set { _Price = value; } }
[Display(Name = "Use discount pricing")]
private bool _HasDiscountPricing = false;
public bool HasDiscountPricing { get { return _HasDiscountPricing; } set { _HasDiscountPricing = value; } }
[Display(Name = "Local shipping price")]
private decimal _LocalShipPrice = 0;
public decimal LocalShipPrice { get { return _LocalShipPrice; } set { _LocalShipPrice = value; } }
[Display(Name = "Intl. shipping price")]
private decimal _IntlShipPrice = 0;
public decimal IntlShipPrice { get { return _IntlShipPrice; } set { _IntlShipPrice = value; } }
[Display(Name = "Item is taxable")]
private bool _isTaxable = true;
public bool isTaxable { get { return _isTaxable; } set { _isTaxable = value; } }
public virtual List<PricelistProductOption> PricelistProductOptions { get; set; }
}
Your PricelistProduct class doesn't implement IEnumerable or IQuerable interfaces, other words it's not a collection to query over it.
Maybe it should be Model.pricelistProduct.PricelistProductOptions in your query or your view model should be a collection?
Consecutive assignments don't make sense as well:
#{
//get corresponding PricelistProduct
PricelistProduct thisPP = new ProofPixLib.Models.PricelistProduct();
thisPP = ..;
}

Troubleshoot object reference error when filling mvc models with datareader?

I am using MVC 3 and I am using a datareader to create a list of items that have subItems. When I add the subitem I get "Object reference not set to an instance of an object." With the following code:
QuestionaireLine question = new QuestionaireLine();
question.Question_ID = Convert.ToInt32(reader["Question_ID"]);
question.Question_Answer = reader["Question_Answer"].ToString();
...etc..
currentGroup.Lines.Add(question); //exception thrown here
The models:
public class Questionaire
{
public int Question_Group_Id { get; set; }
public string Question_Group_Name { get; set; }
public int Question_Group_Indent { get; set; }
public int Question_Group_Order { get; set; }
public List<QuestionaireLine> Lines { get; set; }
}
public class QuestionaireLine
{
public int Question_ID { get; set; }
public string Question_Label { get; set; }
public string Question_Answer { get; set; }
public int Data_Type { get; set; }
public int Control_Type { get; set; }
public string Data_Choices { get; set; }
public int Data_Max_Length { get; set; }
public bool Issue_Tagged { get; set; }
public int Question_Order { get; set; }
public string NumberedQuestion
{
get { return String.Format("{0}. {1}", Question_Order, Question_Label); }
}
}
The whole code:
// what am I missing??
using (var conn = new SqlConnection(_connectionString))
{
List<Questionaire> groups = new List<Questionaire>();
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.Parameters.Add(new SqlParameter
{
ParameterName = "#Review_ID",
Value = reviewID
});
com.CommandText = "Review_Get_Question_Groups_Answers";
conn.Open();
// Get the reader
SqlDataReader reader = com.ExecuteReader();
// Process each result in the result set
int currQuestionGroupId = 0;
Questionaire currentGroup = null;
while (reader.Read())
{
var questionGroupId = Convert.ToInt32(reader["Question_Group_Id"]);
if (questionGroupId != currQuestionGroupId)
{
currQuestionGroupId = questionGroupId;
if (currentGroup != null)
{
groups.Add(currentGroup);
}
currentGroup = new Questionaire();
currentGroup.Question_Group_Id = Convert.ToInt32(reader["Question_Group_Id"]);
currentGroup.Question_Group_Indent = Convert.ToInt32(reader["Question_Group_Indent"]);
currentGroup.Question_Group_Name = reader["Question_Group_Name"].ToString();
currentGroup.Question_Group_Order = Convert.ToInt32(reader["Question_Group_Order"]);
}
if (reader["Question_ID"] != DBNull.Value)
{
QuestionaireLine question = new QuestionaireLine();
question.Question_ID = Convert.ToInt32(reader["Question_ID"]);
question.Question_Answer = reader["Question_Answer"].ToString();
question.Issue_Tagged = Convert.ToBoolean(reader["Issue_Tagged"]);
question.Data_Type = Convert.ToInt32(reader["Data_Type"]);
question.Data_Max_Length = Convert.ToInt32(reader["Data_Max_Length"]);
question.Data_Choices = reader["Data_Choices"].ToString();
question.Question_Label = reader["Question_Label"].ToString();
question.Question_Order = Convert.ToInt32(reader["Question_Order"]);
question.Control_Type = Convert.ToInt32(reader["Control_Type"]);
currentGroup.Lines.Add(question);
}
if (currentGroup != null)
{
groups.Add(currentGroup);
}
}
reader.Close();
com.Dispose();
return groups;
}
Your Lines property on your Questionaire instance is Null. Change to:
public class Questionaire
{
public int Question_Group_Id { get; set; }
public string Question_Group_Name { get; set; }
public int Question_Group_Indent { get; set; }
public int Question_Group_Order { get; set; }
public List<QuestionaireLine> Lines { get; set; }
public Questionaire() {
Lines = new List<QuestionaireLine>();
}
b.t.w. stepping through your code would have shown you that.

Resources