Validate title and user name unique combination with xVal - xval

I have a form for create/edit articles. Every article is associated with an user.
After article is publshed the link to each article is composed from user name and article title (and {userName}/{articleTitle} should be unique combination):
/articles/{userName}/{articleTitle}
Article class:
public class Article
{
public int ArticleId { get; set; }
[Required(ErrorMessage = "Please enter title")]
public string Title { get; set; }
[Required(ErrorMessage = "Please select a user")]
public int UserId { get; set; }
}
View model:
public class ArticleFormViewModel
{
public Article Article { get; set; }
public SelectList Users { get; set; }
public ArticleFormViewModel(Article article, Dictionary<int, string> allUsers)
{
Article = article;
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem() { Value = "", Text = "Please select a user" });
foreach (var user in allUsers)
{
list.Add(new SelectListItem() { Value = user.Key.ToString(), Text = user.Value });
}
Users = new SelectList(list, "Value", "Text", Article.UserId);
}
}
View:
<div id="validationSummary">
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
</div>
<% using (Html.BeginForm()) {%>
<%= Html.Hidden("ArticleId", Model.Article.ArticleId) %>
<fieldset>
<legend>Article</legend>
<ul>
<li>
<label for="UserId">User: <%= Html.ValidationMessage("UserId", "*")%></label>
<%= Html.DropDownList("UserId", Model.Users) %>
</li>
<li>
<label for="Title">Title: <%= Html.ValidationMessage("Title", "*") %></label>
<%= Html.TextBox("Title", Model.Article.Title) %>
</li>
</ul>
<input type="submit" value="Save" />
</fieldset>
<% } %>
<%= Html.ClientSideValidation(typeof(Article))
.AddRule("Title", new RemoteRule(Url.Action("ValidateTitle")))
.UseValidationSummary("validationSummary", "Please correct the errors and try again.")%>
I'm using xVal for validation.
ValidateTitle - is a controller action which validates that {userName}/{articleTitle} is unique. It works using Ajax.
Everything works fine when I'm editing title, but I have problems when I change the user in select list. If title was invalid for user1, and I choose user2, previous error message remains and I can't check that title for user2 is valid.
I can validate user name the same way I do it with title, but there will be cases when 2 errors saying that user name and title combination is invalid will be shown.
Title errors and user errors should be synchronized, but how?
Or maybe there is another way I should work with title and users list?

xVal generates validation rules (for jQuery validation plug-in):
<script type="text/javascript">xVal.AttachValidator(null,
{"Fields":[{"FieldName":"ArticleId","FieldRules":[{"RuleName":"DataType","RuleParameters":{"Type":"Integer"}}]},
{"FieldName":"Title","FieldRules":[{"RuleName":"Required","RuleParameters":{},"Message":"Please enter title"},
{"RuleName":"Remote","RuleParameters":{"url":"/articles/ValidateTitle"}}]},
{"FieldName":"UserId","FieldRules":[{"RuleName":"Required","RuleParameters":{},"Message":"Please select a user"}]}]},
{"ValidationSummary":{"ElementID":"validationSummary","HeaderMessage":"Please correct the errors and try again."}})</script>
Now there is only one condition: a user should be chosen from select list.
I don't know how to hide (reset) title error message when title + user1 is invalid and I chose user2 from list.
And opposite: hide user error message after I chose user and edit title.

Related

Display data from a SQL Server stored procedure in an ASP.NET MVC view

The stored procedure that I have carried out in SQL Server returns the following information:
I show what I have worked on so far.
Model
public class TarjetasInformativas
{
public string PrimerNombre { get; set; }
public decimal PrimerMonto { get; set; }
}
Class in which I make my connection to the database
public class DatosTarjetasInformativas
{
public List<TarjetasInformativas> RetornarNombres()
{
List<TarjetasInformativas> objTarjetas = new List<TarjetasInformativas>();
using(SqlConnection sqlConnection = new SqlConnection("Data Source=HN123; Initial Catalog=DBTEST; Integrated Security=True"))
{
string query = "SP_TARJETASINFORMATIVAS";
SqlCommand cmd = new SqlCommand(query, sqlConnection);
cmd.CommandType = CommandType.StoredProcedure;
sqlConnection.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
objTarjetas.Add(new TarjetasInformativas()
{
PrimerNombre = dr["ENTIDADES"].ToString(),
PrimerMonto = decimal.Parse(dr["MONTO"].ToString()),
});
}
}
}
return objTarjetas;
}
}
Controller
public ActionResult ObtenerNombres()
{
DatosTarjetasInformativas objDTTarjetas = new DatosTarjetasInformativas();
List<TarjetasInformativas> objTarjetas = objDTTarjetas.RetornarNombres();
return View(objTarjetas);
}
View
#model WebPlantillaOpexLTE.Models.TarjetasInformativas
<div class="col-lg-3 col-6">
<!-- small box -->
<div class="small-box bg-info">
<div class="inner">
<h3>#Html.LabelFor(m => m.PrimerNombre)</h3>
<p>#Html.LabelFor(m => m.PrimerMonto)</p>
</div>
<div class="icon">
<i class="ion ion-android-locate"></i>
</div>
</div>
</div>
Within my h3 and p tags of my view, I'm looking to get the information from my SQL Server stored procedure.
Through the Html.LabelFor helper I was able to get only the variables that I declared in my model, and I need to display the content of my SQL Server stored procedure inside the card.
In case of doubt, within my model where I relate to my stored procedure through a breakpoint, I have verified that it receives the information.
I am new to this platform, and I would like to know what I could do to solve my problem.
I thank you in advance for taking the time to pay attention to my question and for the help.
So, there is a couple things wrong in your Razor view.
#model should be of type List<WebPlantillaOpexLTE.Models.TarjetasInformativas> since this is the returning type of your SP function and the model returned to the view.
To show the contents of a list, you should use an foreach loop to iterate over your model, something like the following (may contain syntax error):
#model List<WebPlantillaOpexLTE.Models.TarjetasInformativas>
<div class="col-lg-3 col-6">
#foreach(var item in #Model){
<!-- small box -->
<div class="small-box bg-info">
<div class="inner">
<h3>#item.PrimerNombre</h3>
<p>#item.PrimerMonto</p>
</div>
<div class="icon">
<i class="ion ion-android-locate"></i>
</div>
</div>
}
</div>
Some reference: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/overview?view=aspnetcore-7.0
I achieved what was required, but I made some changes to my controller and deleted my class where I made the connection to my database. I did it with Entity Framework.
I show my controller, in my model and view nothing changes except what was corrected in the first answer.
public ActionResult ListarTarjeta()
{
List<TarjetasInformativas> listaTarjetas = new List<TarjetasInformativas>();
using (db)
{
var listTarjetasInformativas = db.SP_TARJETASINFORMATIVAS().ToList();
foreach(var item in listTarjetasInformativas)
{
var asignar = new TarjetasInformativas
{
PrimerNombre = item.NOMBRE,
PrimerMonto = (decimal)item.MONTO
};
listaTarjetas.Add(asignar);
}
}
return View(listaTarjetas);
}

Dynamic checkbox from list with Thymeleaf

I try to save the values from dynamically created checkboxes:
<div>
<ul>
<li th:each="item, stat : *{users}">
<input type="checkbox" th:field="*{users[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item}"></label>
</li>
</ul>
</div>
The controller provides the String items as follwing:
public List<String> getUsers() {
return users;
}
And the setter for the Strings is:
public void setUsers(final String[] users) {
for (final String string : users) {
System.out.println(string);
}
}
The values are correct shown in the html page. But when i click save button, and the setter is called, the values are empty. What can i do, where is the problem?
Any help would appreciate.
Please check out section about handlin multi-value checkboxes in Tutorial: Thymeleaf + Spring.
You should provide some model attribute (of type List<String>) containing all users possible to select. Let's call it selectableUsers.
Then it can collaborate with your form-backing bean (that one containing users) in a following manner:
<div>
<ul>
<li th:each="item : ${selectableUsers}">
<input type="checkbox" th:field="*{users}" th:value="${item}" />
<label th:for="${#ids.prev('users')}" th:text="${item}"></label>
</li>
</ul>
</div>
Note I think that getter and setter for a field should handle the same type, but they don't (getter returns List<String> however setter consumes String[])
What you are trying to do looks logical, but it does not work that way.
If you did not get it resolved you can do this instead:
In relevant method of your controller you can add list of titles for your checkboxes:
List<String> allUsers = Arrays.asList("abc","xyz"); // OR generate list dynamically
model.addAttribute("selectableUsers", allUsers);
Or add it to ModelAndView if that is what you are using.
Change your html to what was suggested by #Jakub Ch.
Change your getter and setter methods as follows:
private String users;
...
public String getUsers() {
return this.users;
}
public void setUsers(String users) {
this.users = users;
}
Then 'users' field will contain comma separated String values or their id numbers ( depending on how you set it up) indicating selected checkboxes. Then you can convert String values to array using code like below or if id numbers are stored get String values from your ArrayList.
public List<String> getStrings() {
return Arrays.asList(strings.split(","));
}
Hope it helps.

Why does my <a> taghelper route go dead after its route is used by a form post?

This is a ASP.Net Core MVC project.
In my layout file I have the following Menu link:
<li><a asp-controller="Employees" asp-action="Index">Employees</a></li>
The MVC controller it routes to looks like this:
public IActionResult Index()
{
return View();
}
When I click the link the Action is hit and the view is rendered with my Employee List View.
The Employee List View is bound to an Angular Controller which calls a corresponding Employees Web API GET and the view shows my employee list unfiltered.
Great.
Now we need an Employee quick search from a quick search panel.
So I modify my MVC Employees controller like this:
public IActionResult Index(EmployeeListPageViewModel empListPageVM)
{
return View(empListPageVM);
}
It takes in this Model:
public class EmployeeListPageViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
My Quick Search Form looks like this back in the layout file:
<form asp-controller="Employees" asp-action="Index">
<th>Employee Search:</th>
<td><input id="firstName" name="firstName" type="text" placeholder="FirstName" /></td>
<td><input id="lastName" name="lastName" type="text" placeholder="LastName" /></td>
<td>
<button class="btn btn-xs btn-primary glyphicon glyphicon-search">
</button>
</td>
</form>
Now the model is built from the form and sent to my MVC Employees Index action.
And of course I roll all the needed changes through.
Make my Employees Web API controller take in optional params.
FirstName = null, LastName = null.
The employee list view takes in the ViewModel:
#model EmployeeListPageViewModel
Binds to the Angular Controller:
ng-controller="employeesController
Calls getEmployees:
ng-init="getEmployees('#Model.FirstName', '#Model.LastName')
The Angular controller works out whether everything is null or filtering is needed:
/***** List Employees *****/
$scope.getEmployees = function (pfirstName, pLastName) {
var config = {
params: {
firstName: pfirstName,
lastName: pLastName
}
}
$http.get(employeeUrl, config)
.then(function (response) {
// Test front end exception message;
// throw "test exception";
$scope.data.employees = response.data;
})
.catch(function (error) {
$scope.data.employeeListError = error;
});
}
Hope all of this makes sense. Just laying the foundation here.
Now my problem:
Everything seems to work individually.
But, when I go in fresh and click the Employees Menu Link I get my full list.
And when I fill in FirstName And/or LastName in the quick search it works.
But now the Employees menu link is dead. It doesn't fire. It doesn't hit the Employees Index Controller action.
What is it about the form that is killing the Menu Link?
Update 1: After thinking about this I believe the anchor tag helper is looking at the controller and index and saying, "I am already there." So it is not going to the controller action. How do I force it to go even if it is already there?
Update 2: I tried changing the link to this:
<li>Employees</li>
The link works but it is still killed after the form post.
Apparently, no matter how you direct to the link, taghelper, ng-href, straight link, whatever, if you are already there the link will not go.
I had to replace the anchor link in the menu with this:
<li>
<form asp-controller="Employees" asp-action="Index">
<button type="submit" class="navbarLinks">
Employees
</button>
</form>

how to display first item from the model class using services and controller in angular js

I have model Class Doctor.This class is having one WebApi controller.I have Created 3 scripts namely module.js, service.js, Homecontroller.js.
The question is I want to display first item from the model class. I don't want to repeat the data, instead I want to display only one data(i.e FirstName).and also In the Next line I want to display the full name.
I have not written function for fullname.Please reply how to write function for fullname and displaying first data
// Model class : Doctor.cs
public class Doctor
{
public int Id { get; set; }
public string Reg_No { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
Web api controller for Doctor
public class DoctorsAPIController : ApiController
{
private DigitalHealthWebContext db = new DigitalHealthWebContext();
// GET: api/DoctorsAPI
public List<Doctor> GetDoctors()
{
return db.Doctors.ToList();
}
}
I have 3 scripts
Module.js
var app = angular.module("docModule", ["ngRoute"]);
Service.js
app.service('docService', function ($http) {
this.getDoctors = function () {
return $http.get("/api/DoctorsAPI");
}
})
homeController.js
app.controller('homeController', function ($scope, docService) {
getFormData();
function getFormData() {
var DoctorGet = docService.getDoctors();//The MEthod Call from service
DoctorGet.then(function (p1) { $scope.LoadDoctor = p1.data },
function (errorP1) {
$log.error('failure loading Doctor', errorP1);
});
}
});
if I use select controller with ng-Repeat I am able to get the data.
<select ng-model="Dept" class="dropdown form-control" required id="selectControl">
<option value="" disabled selected>Choose speaciality</option>
<option ng-repeat="Doctor in LoadDoctor" value="{{Doctor.Id}}">
{{Doctor.firstName}}
</option>
</select>
but If I write the following code I am not getting the data
<div class="doc-details-block" ng-controller="homeController">
<a href="#" ng-model="LoadDoctor">
<h2 class="doc-name">
{{firstName}}
</h2>
</a>
</div>
That's problem related to trying get firstName property on array with doctors.
You can try to improve it by follow script, but it will be much better to have dedicated property that store a doctor (and again better to have a dedicated service call, because it's more clear and faster)
<div class="doc-details-block" ng-controller="homeController">
<a ng-if="LoadDoctor.length > 0" href="#">
<h2 class="doc-name">
{{ LoadDpctor[0].firstName }}
</h2>
</a>
</div>

Retrieving Data From Controller Based On ListBox Value

I have a form with about 20 fields. I have a ListBox that is populated with Customers from the Model when the page loads. Once the user picks one of those Customers from the ListBox, I want to post to the Controller the selected Customer, get customer's info, return it to same view, and populate some of the fields with the Customer's info.
Here is what I am trying now, but it might not be the best way. Also, the onclick gets called on page load, which causes an infinite loop.
View - CreateUser
#Html.ListBoxFor(x => x.Id,
Model.Customers.Select(
x => (new SelectListItem {
Text = x.Name,
Value = x.Value.ToString(),
Selected = x.IsSelected})).OrderBy(x => x.Text),
htmlAttributes new {
onclick = #Html.Action("GetCustomerInfo", "Customer", Model)
})
Controller - Customer
[ChildActionOnly]
public ActionResult GetCustomerInfo(CustomerModel Model)
{
// populate model with customer info
return View("CreateUser", Model);
}
Also, if there is a better way for this solution, I would love to hear any ideas. I am trying to avoid loading all Customers and then just using Angular to change the text fields based on selected Customer, since there is going to be over 1,000 customers and it would be slow to initially load all of them.
#Html.Action() is razor code and is parsed on the server so GetCustomerInfo() is called before the page is sent to the client. The fact its associated with the onclick event of a control is irrelevant. The infinite loop is because the view returned by GetCustomerInfo is the same view your trying to render - it contains the same #Html.Action() so GetCustomerInfo is called again, which returns a view with the same #Html.Action() so GetCustomerInfo is called again and so on.
You can use ajax to update the DOM with the selected customers details.
View models
public class SelectCustomerVM
{
[Display(Name="Select customer to display details")]
public int? CustomerID { get; set; }
public SelectList CustomerList { get; set; }
}
public class CustomerVM
{
public int ID { get; set; }
public string Name { get; set; }
// other properties of customer
}
Controller
public ActionResult Index()
{
SelectCustomerVM model = new SelectCustomerVM();
model.CustomerList = new SelectList(db.Customers, "ID", "Name");
return View(model);
}
public ActionResult Details(int ID)
{
CustomerVM model = new CustomerVM();
// get customer from database and map properties to CustomerVM
return PartialView(model);
}
Index.cshtml
#model SelectCustomerVM
#Html.LabelFor(m => m.CustomerID)
#Html.DropDownListFor(m => m.CustomerID, Model.CustomerList, "--Please select--")
<div id=customerdetails></div>
<script type="text/javascript">
$('#CustomerID').change(function() {
var customerID = $(this).val();
if(customerID) {
$.get('#Url.Action("Details", "Customer")', { ID: customerID }, function(data) {
$('#customerdetails').html(data);
});
} else {
$('#customerdetails').empty();
}
});
</script>
GetCustomer.cshtml (partial view)
#model CustomerVM
#DisplayFor(m => m.ID)
#DisplayFor(m => m.Name)
....
Some best practices to note. Don't pollute your view with code to construct SelectList's - that's the responsibility of the controller; and use Unobtrusive javascript - don't mix content and behavior.

Resources