I am working on a project which includes MVC and KnockoutJS. As this is the first time i'm working with KnockoutJS i'm running into a lot of issues.
I want to put my MVC Viewmodel on the webpage in a tabbed interface. Then from that interface i want to call functions depending on which button i press.
Currently i am getting the model to load and show itself fine, but i cannot call any functions i am trying to create.
Also i have no clue that the way i am creating this at the moment is the correct one.
I have the following model:
public partial class SSoSearchModel
{
public string SearchParameter { get; set; }
public string SearchValue { get; set; }
public string SearchLabel { get; set; }
public IQueryable<SsoRecordResultViewModel> searchResults { get; set; }
public List<SelectListItem> ParameterList { get; set; }
public List<SelectListItem> LabelList { get; set; }
}
The SearchResults look like this
public partial class SsoRecordResultViewModel
{
[JsonProperty(PropertyName = "first_name")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "last_name")]
public string LastName { get; set; }
[JsonProperty(PropertyName = "email_address"), DefaultValue(PropertyName = "email_addresses")]
public string EmailAddress { get; set; }
[JsonProperty(PropertyName = "phone_number"), DefaultValue(PropertyName = "phone_numbers")]
public string PhoneNumber { get; set; }
[JsonProperty(PropertyName = "Klantnummer"), CustomAttribute(PropertyName = "Klantnummer")]
public string Klantnummer { get; set; }
[JsonProperty(PropertyName = "verzekerdenummer"), CustomAttribute(PropertyName = "Verzekerdennummer")]
public string Verzekerdenummer { get; set; }
[JsonProperty(PropertyName = "person_id")]
public string SSOID { get; set; }
[JsonProperty(PropertyName = "initials")]
public string Initialen { get; set; }
}
And this is all loaded in the Index function of the controller with dummy data
public ActionResult Index()
{
List<SsoRecordResultViewModel> temp = new List<SsoRecordResultViewModel>();
temp.Add(new SsoRecordResultViewModel { SSOID = "ea373d27-142d-48f6-86c9-dcdb15e316d2", EmailAddress = "A", FirstName = "a", Klantnummer = "1", Verzekerdenummer = "1", PhoneNumber = "1", Initialen = "A", LastName = "a" });
temp.Add(new SsoRecordResultViewModel { SSOID = "2d613aba-3f89-43b0-919a-1aa615086ff3", EmailAddress = "b", FirstName = "b", Klantnummer = "2", Verzekerdenummer = "2", PhoneNumber = "2", Initialen = "b", LastName = "b" });
temp.Add(new SsoRecordResultViewModel { SSOID = "c142f22e-7664-4a9c-9303-293b48acbf65", EmailAddress = "c", FirstName = "c", Klantnummer = "3", Verzekerdenummer = "3", PhoneNumber = "3", Initialen = "c", LastName = "c" });
SSoSearchModel model = new SSoSearchModel();
model.searchResults = temp.AsQueryable(); //Enumerable.Empty<SsoRecordResultViewModel>().AsQueryable();
//zoekopties
model.ParameterList = new List<SelectListItem>();
model.ParameterList.Add(new SelectListItem { Text = "Zoek op Klantnummer", Value = "Klantnummer" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op Verzekerdenummer", Value = "Verzekerdenummer" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op E-mail adres", Value = "Email" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op SSO ID", Value = "SSOID" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op Telefoonnummer", Value = "Telefoonnummer" });
//Labels
model.LabelList = new List<SelectListItem>();
model.LabelList.Add(new SelectListItem { Text = "Rel1", Value = "Rel1" });
model.LabelList.Add(new SelectListItem { Text = "Rel2", Value = "Rel2" });
return View(model);
}
Now on the Index.cshtml i offcourse have my markup which shows the data great at the moment, but the KnockoutJS is the issue.
The javascript looks the following:
<script>
function ViewModel() {
var self = this;
self.Search = ko.observableArray(searchResults);
self.save = function(ssoid){
//ajax hier
};
};
var rawData = #Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model));
var Data = ko.mapping.fromJS(rawData,ViewModel);
ko.applyBindings(Data);
</script>
This currently shows me the data, but i do not know how i can call the save function. I have this but this does not work:
<button type="button" class="btn btn-default" aria-label="Bewaren" data-toggle="tooltip" data-placement="top" title="Bewaar de gegevens" data-bind="click: function() { $data.save(SSOID()); }"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span></button>
Also when i swap the values in ko.mapping.fromJS(rawData,ViewModel); the data does not get shown anymore.
Anyone here who can help me with this as it is driving me crazy.
Related
I have 2 class models, one is called WorkCategory and the second one is called ServiceCategory. Based on each category selected from the dropdown(WorkCategory) it will populate the second dropdown with data from the second class(ServiceCategory) based on the foreign key.
The first dropdown is populated from the database without javascript, in the second dropdown I populate the value with javascript based on the first dropdown with the method on change.
This is my model view which has the attributes that I want to capture.
public class PostJobViewModel
{
[Required]
public WorkCategory Category { get; set; }
[Required]
public string Headline { get; set; }
[Required]
public string JobAddress { get; set; }
[Required]
public string AboutJob { get; set; }
[Required]
public string JobCity { get; set; }
[Required]
public string JobPostCode { get; set; }
public ServiceCategory JobService { get; set; }
}
This is the first dropdown which populates well from DB and also when I post the form has the ID of the selected option.
#Html.DropDownListFor(m => m.Category.CategoryName, ViewBag.whatWorkToBeDone as SelectList, "-- Select Category --", new { #class = "form-control", #onchange="FillService()" })
#Html.ValidationMessageFor(m => m.Category.CategoryName, "", new { #class = "text-danger" })
and this is the second dropdown list which is populated on runtime with Ajax, based on the first selected option.
#Html.DropDownListFor(m => m.JobService.Category, new SelectList(Enumerable.Empty<SelectListItem>(), "ServiceId", "ServiceName"), "-- Select Service --", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.JobService.Category, "", new { #class = "text-danger" })
The problem that I have is when I debug the controller the "JobService" is always null even though the option has a value of ids. Despite this, the modelstate is not valid and it throws an error on the UI saying "The value '14' is invalid.(where 14 is the number(id) but for some reason is presented like string).
And here below is the JavaScript/ajax call which I populate the dropdown.
function FillService() {
var serviceId = $('#Category_CategoryName').val();
$.ajax({
url: '#Url.Action("FillServ", "PostJob")',
type: "GET",
contentType: "JSON",
data: { ServiceCategory: serviceId },
success: function (services) {
if (services == '') {
// $('#step-wizard').load(' .step-anchor');
document.getElementById('JobService_Category').disabled = true;
$("#Title-Category").html(""); // clear before appending new list
$("#JobService_Category").html(""); // clear before appending new list
$("#Title-Category").append('Please skip this step!');
$("#secondStep").html("");
$("#secondStep").append('Skip this step!');
} else {
document.getElementById('JobService_Category').disabled = false;
$("#JobService_Category").html(""); // clear before appending new list
$.each(services, function (i, service) {
$("#JobService_Category").append($('<option></option>').val(service.ServiceId).html(service.ServiceName));
});
}
}
});
what is wrong with this?
---UPDATE---
ServiceCategory Class
public class ServiceCategory
{
[Key]
public int ServiceId { get; set; }
public string ServiceName { get; set; }
public WhatToHaveDone Category { get; set; }
public string ServiceDescription { get; set; }
}
public class WhatToHaveDone
{
[Key]
public int WhatToDoneId { get; set; }
public string WhatToDoneItem { get; set; }
public string ItemDescription { get; set; }
public ICollection<ServiceCategory> Services { get; set; }
}
And this is the Method that returns data from the Api call in ajax
[HttpGet]
public ActionResult FillServ(int ServiceCategory)
{
var services = (from s in _context.JobService where s.Category.WhatToDoneId == ServiceCategory select s).ToList();
return Json(services, JsonRequestBehavior.AllowGet);
}
controller debug
[HttpPost]
public ActionResult Index(PostJobViewModel JobModel, List<HttpPostedFileBase> jobImages)
{
if (User.Identity.Name == "")
{
return RedirectToAction("Login","Account");
}
else
{
var imgList = new List<JobImage>();
var user = (from u in _context.Users where u.Email == User.Identity.Name select u.Id).First();
foreach (var item in jobImages)
{
var newJobimage = new JobImage
{
JobFileName = item.FileName,
JobImageContentBytes = new byte[item.ContentLength],
};
imgList.Add(newJobimage);
}
if (ModelState.IsValid)
{
var newJob = new JobPost{
Category = JobModel.Category,
JobAddress=JobModel.JobAddress,
AboutJob=JobModel.AboutJob,
JobCity=JobModel.JobCity,
JobPostCode=JobModel.JobPostCode,
JobImages=imgList,
UserId = user,
JobService=JobModel.JobService
};
_context.jobPosts.Add(newJob);
_context.SaveChanges();
}
else
{
}
}
var workCategories = new SelectList(_context.Categories, "CategoryId", "CategoryName");
ViewBag.WorkCategories = workCategories;
var whatWorkToBeDone = new SelectList(_context.JobWizardCategories, "WhatToDoneId", "WhatToDoneItem");
ViewBag.whatWorkToBeDone = whatWorkToBeDone;
return View();
}
I am using the following query for use in google charts. It works well but it doesnt show distinct months. Is there a way to sum all the HoursTaken per MonthOfHoliday to avoid not having distinct values?
var querythpmpro = (from r in db.HolidayRequestForms
where r.EmployeeID == id
select new { Count = r.HoursTaken, Value = r.MonthOfHoliday.ToString() }).OrderBy(e => e.Value);
Error message from the console Uncaught (in promise) Error: Type mismatch. Value 1 does not match type string in column index 0
EDIT:
#region Total Hours Per Month
var querythpmpro = (from r in db.HolidayRequestForms
where r.EmployeeID == id
group r by r.MonthOfHoliday into g
select new { Value = g.Key, Sum = g.Sum(h => h.HoursTaken) }
).OrderBy(e => e.Value);
var resultthpmpro = querythpmpro.ToList();
var datachartthpmpro = new object[resultthpmpro.Count];
int Q = 0;
foreach (var i in resultthpmpro)
{
datachartthpmpro[Q] = new object[] { i.Value, i.Sum};
Q++;
}
string datathpmpro = JsonConvert.SerializeObject(datachartthpmpro, Formatting.None);
ViewBag.datajthpmpro = new HtmlString(datathpmpro);
#endregion
Model:
public partial class HolidayRequestForm
{
public int RequestID { get; set; }
public int EmployeeID { get; set; }
public System.DateTime StartDate { get; set; }
public System.DateTime FinishDate { get; set; }
public decimal HoursTaken { get; set; }
public string Comments { get; set; }
public int YearCreated { get; set; }
public int MonthCreated { get; set; }
public int DayCreated { get; set; }
public Nullable<int> YearOfHoliday { get; set; }
public Nullable<bool> Approved { get; set; }
public string SubmittedBy { get; set; }
public string ApprovedBy { get; set; }
public Nullable<int> WorkWeek { get; set; }
public Nullable<int> MonthOfHoliday { get; set; }
public virtual Employee Employee { get; set; }
}
Script:
function drawChartA() {
// Create the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Value');
data.addColumn('number', 'Count');
data.addRows(datassthpmpro);
// Set chart options
var options = {
'title': 'Holiday Hours Taken Per Month',
'width': 600,
'height': 350,
'hAxis': { title: 'Month Number' },
'vAxis': { title: 'Holiday Hours Taken' },
'is3D': true,
'legend': 'none'
};
How about grouping by MonthOfHoliday and doing the Sum on HoursTaken:
var querythpmpro = (from r in db.HolidayRequestForms
where r.EmployeeID == id
group r by r.MonthOfHoliday into g
select new { Value = g.Key, Sum = g.Sum(h => h.HoursTaken) }
).OrderBy(e => e.Value);
I have created two dropdownlists, of which one acts as a filter for the other. So, with dropdown Customer a customer is selected and only a limited set of ClientUsers is presented in in the dropdown ClientUser. I use a Jquery function to make this happen.
The selection works excellent, but when I POST the form the ClientUser is set to 0, instead of the choice.
The View is as follows (simplified for readability purposes):
#model DropDownTester2.Models.CaseViewModel
<form asp-action="Maak">
<label asp-for="CaseTitle" class="control-label"></label>
<input asp-for="CaseTitle" class="form-control" />
<select id="customerId" asp-for="CustomerId" asp-items='#(new SelectList(ViewBag.customers, "CustomerId", "CompanyName"))'>
<option>Select Customer Name</option>
</select>
<select id="clientUserId" asp-for="ClientUserId" asp-items='#(new SelectList(string.Empty, "ClientUserId", "LastName"))'>
</select>
<input type="submit" value="Maak" class="btn btn-default" />
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/lib/jquery/dist/jquery.js"></script>
<script type="text/javascript">
$(function () {
$("#customerId").change(function () {
var url = '#Url.Content("~/")' + "Case/getClientUserById";
var ddlsource = "#customerId";
$.getJSON(url, { id: $(ddlsource).val() }, function (data) {
//$.getJSON("#Url.Action("getClientUserById","Case")", { id: $(ddlsource).val() }, function (data) {
var items = '';
$("#clientUserId").empty();
$.each(data, function (i, row) {
items += "<option value='" + row.value + "'>" + row.text + "</option>";
});
$("#clientUserId").html(items);
})
});
});
</script>
}
The CaseViewModel is:
public class CaseViewModel
{
public int CaseId { get; set; }
public string CaseTitle { get; set; }
public int CustomerId { get; set; }
public int ClientUserId { get; set; }
public IEnumerable<Customer> CustomerList { get; set; }
public IEnumerable<ClientUser> ClientUserList {get; set;}
My Model for Case is:
public class Case
{
public int CaseId { get; set; }
public string CaseTitle { get; set; }
public string CaseRoleDescription { get; set; }
public int CustomerId { get; set; }
public int ClientUserId { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<ClientUser> ClientUsers { get; set; }
}
Finally my controllers are :
// GET: Case/Maak
public IActionResult Maak()
{
ViewBag.customers = _context.Customer.ToList();
return View();
}
public JsonResult getClientUserById(int id)
{
List<ClientUser> list = new List<ClientUser>();
list = _context.ClientUser.Where(c => c.Customer.CustomerId == id).ToList();
list.Insert(0, new ClientUser { ClientUserId = 0, LastName = "Please select a clientuser" });
return Json(new SelectList(list, "ClientUserId", "LastName"));
}
// POST: Case/Maak
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Maak(CaseViewModel model)
{
if (ModelState.IsValid)
{
var newCase = new Case
{
CaseId = model.CaseId,
CaseTitle = model.CaseTitle,
CustomerId = model.CustomerId,
ClientUserId = model.ClientUserId
};
_context.Add(newCase);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["CustomerId"] = new SelectList(_context.Set<Customer>(), "CustomerId", "CustomerId", model.CustomerId);
return View(model);
}
I can't reproduce your issue :
The difference part of codes is that i create the Customer/ClientUser manually but that shouldn't be the issue cause :
public class Customer {
public int CustomerId { get; set; }
public string CompanyName { get; set; }
}
public class ClientUser
{
public int ClientUserId { get; set; }
public string LastName { get; set; }
}
You may search the "ClientUserId " in your pages to confirm whether other function reset the value .
As a workaround, you can also :
Create a hidden filed in your page and bind to your model property , create the select change event javascript function to set the value based on the selection .
Use Jquery to get the selected opinion , use Ajax to call action function and pass the value as parameter .
I have a form that has two sections. 3 input fields and another section with 10 checkboxes.
public class Customerproductdto
{
public string CustomerNumber { get; set; }
public string CustomerName { get; set; }
public string CustomerPhone { get; set; }
List<ProductDetails> GetAllChecked {get;set;}
}
public class ProductDetails
{
public string ProductName{ get; set; }
}
Here is jquery code I am using to get all the values of the checkboxes that were
checked on my form. They are about 10 and users could check everything.
var yourArray[]
$("input:checkbox[name=type]:checked").each(function(){
yourArray.push($(this).val());
});
Here is javascript that I use to collect the data and pass to my controller.
How can I pass in my array here all in one shot?
var objdata =
({
CustomerNumber: txtcustnumber,
CustomerName: txtcustname,
CustomerPhone: txtphone
//How do I pass the yourArray here?
});
var url = "#Url.Action("WriteToDb", "Home")";
var completeData = JSON.stringify({ 'Information': objdata });
$.get(url, { 'objdata': completeData }, function (data) {
$('#mainListContent').html(data);
});
Please note that I will like to deserialize this once I get to the controller.
Here is the method.
public ActionResult WriteToDb(string objdata)
{
Customerproductdto getAllTaskSaved = null;
try
{
var stripOffObjectName = JObject.Parse(objdata)["Information"];
var cleanedData = JsonConvert.DeserializeObject<Customerproductdto>(stripOffObjectName.ToString());
getAllTaskSaved = _dtcDataService.WriteTaskToDb(cleanedData, "Add");
}
catch (Exception ex)
{
logger.Error(ex);
}
return PartialView("_Taskdisplay", getAllTaskSaved);
}
Sorry if this is too simple, I am new to JSON and Angular.
So, I have a server response in JSON that I don't quite understand and I need to read using AngularJS. This is the JSON.
{
"$id": "1",
"$values":
[
{
"$id": "2",
"ID": 2,
"FiscalYear": "",
"Month": 1,
"Day": 1,
"Surname": "test",
"FirstName": "test",
"Program": "smart",
"PoliceFileNumber": "123",
"CourtFileNumber": 123,
"Service": "1",
"VictimOfIncident": "yes",
"FamilyVoilenceFile": "123",
"Gender": "M",
"Ethnicity": "1",
"Age": "22",
"AssignedWorker": 1,
"StatusOfFile": 1
}
]
}
This represent a query from a table in my database.
1) What I don't understand is why it's included the id and values at the beginning?
2) How do I access the variables inside values? For example, how do read the Month value?
In the server I have this:
public class ClientDTOesController : ApiController
{
private LookupContext db = new LookupContext();
// GET: api/ClientDTOes
public IQueryable<ClientDTO> GetClientsDTO()
{
return db.ClientsDTO;
}
And this is my model:
public partial class ClientDTO
{
public int ID { get; set; }
public String FiscalYear { get; set; }
public int Month { get; set; }
public int Day { get; set; }
public string Surname { get; set; }
public string FirstName { get; set; }
public String Program { get; set; }
public string PoliceFileNumber { get; set; }
public int CourtFileNumber { get; set; }
public String Service { get; set; }
public String VictimOfIncident { get; set; }
public String FamilyVoilenceFile { get; set; }
public string Gender { get; set; }
public String Ethnicity { get; set; }
public String Age { get; set; }
public int AssignedWorker { get; set; }
public int StatusOfFile { get; set; }
}
My Client code:
(function () {
// creates a module and register to the app
var app = angular.module('ReportViewer', []);
// controller declaration
var MainController = function ($scope, ReportService) {
// object model
var _Client = {
Age: "",
CourtFileNumber: "",
Day: "",
Ethnicity: "",
FamilyVoilenceFile: "",
FirstName: "",
FiscalYear: "",
Gender: "",
Month: "",
PoliceFileNumber: "",
Program: "",
Service: "",
Surname: "",
VictimOfIncident: ""
};
// GET ALL
var onGetAllComplete = function (data) {
$scope.clients = data;
};
var onGetAllError = function (reason) {
$scope.error = "Could not get all clients.";
};
// accessed from the view
// calls the service function
$scope.getClient = function () {
ReportService.getAllClients()
.then(onGetAllComplete, onGetAllError);
};
// exposes the 'object'
$scope.client = _Client;
};
//register the controller to the app
app.controller("MainController", MainController);
}());
And the service:
// ReportService.js
(function () {
var ReportService = function ($http) {
var baseUrl = 'http://localhost:16188/api/Report/ReportClient/1/1';
var _getAllClients = function () {
return $http.get(baseUrl)
.then(function (response) {
return response.data
});
};
return {
getAllClients: _getAllClients
};
};
var module = angular.module("ReportViewer");
module.factory("ReportService", ReportService);
}());
I am trying to display the values here:
<!DOCTYPE html>
<html data-ng-app="ReportViewer">
<head>
<title>Report</title>
<script src="../Scripts/AngularJS/angular.js"></script>
<script src="../Scripts/App/MainController.js"></script>
<script src="../Scripts/App/ReportService.js"></script>
</head>
<body data-ng-controller="MainController">
<div id="wrapper" class="container">
<div class="summary">
<h1>Worker summary & detail report</h1>
<button ng-click="getClient()">Get All Clients</button>
<div id="test" ng-show="clients" >
<p>{{client.courtFileNumber}}</p>
<p>{{client.Day}}</p>
<p>{{client.Ethnicity}}</p>
<p>{{client.FamilyVoilenceFile}}</p>
<p>{{client.FirstName}}</p>
<p>{{client.FiscalYear}}</p>
<p>{{client.Gender}}</p>
<p>{{client.Month}}</p>
<p>{{client.PoliceFileNumber}}</p>
<p>{{client.Program}}</p>
<p>{{client.Service}}</p>
<p>{{client.Surname}}</p>
<p>{{client.VictimOfIncident}}</p>
</div>
Thank you very much for any suggestions.
Your code looks perfect, basically problem is with you HTML, you could use ng-repeat which act as like for loop which will loop through all clients and show it.
Markup
<div id="test" ng-repeat="client in clients.$values">
<p>{{client.courtFileNumber}}</p>
<p>{{client.Day}}</p>
<p>{{client.Ethnicity}}</p>
<p>{{client.FamilyVoilenceFile}}</p>
<p>{{client.FirstName}}</p>
<p>{{client.FiscalYear}}</p>
<p>{{client.Gender}}</p>
<p>{{client.Month}}</p>
<p>{{client.PoliceFileNumber}}</p>
<p>{{client.Program}}</p>
<p>{{client.Service}}</p>
<p>{{client.Surname}}</p>
<p>{{client.VictimOfIncident}}</p>
</div>