Servicestack + model binding on json post using AngularJs - rest

I'm calling a REST service developed in serviceStack, I'm using angularJs as a client but my problem is that for some reason I can't bind my json model with my requestDTO.
my angularJs code:
I have a AngularJs Service
angular.module('mseApp.services', ['ngResource']).
factory('loginService',
function ($resource) {
return $resource('/Api/User/login', '',
{
query: {
method: 'POST',
params:{ }
}
});
});
controller:
function loginCtrl($scope, loginService) {
$scope.submit = function () {
var user = loginService.query({loginRequest:$scope.loginRequest});
};
}
View:
<div>
<div ng-model="loginRequest">
<form ng-submit="submit()" ng-controller="loginCtrl">
<input id="username" ng-model="loginRequest.Username" type="text" name="text" />
<input id="password" ng-model="loginRequest.Password" type="text" name="text" />
<input type="submit" id="submit" value="Submit" />
<span ng-model="Username"></span>
</form>
</div>
</div>
on the dot net side
my DTO
public class LoginRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
public UserResponse Post(LoginRequest loginRequest)
{
}
everything if fine the only problem is that LoginRequest is null on my service method.
Any ideas???

Just send
query($scope.loginRequest)
instead of
query({loginRequest:$scope.loginRequest})
that will solve your issue.

You can also utilize the $promise object of your query to defer processing of your binding to $scope
function loginCtrl($scope, loginService) {
$scope.submit = function () {
var user = loginService.query.$promise.then(function (promise) {
$scope.userResponse = promise.d;
});
};
}
then bind to (assuming your Username is at UserResponse.Username) like this
<span ng-model="UserResponse.Username"></span>

Related

How can I bind form fields to a nested model on post?

I am coding a solution where the user will submit a form, posting the values back to my ASP.NET MVC controller. My model is complex and the form fields are contained in a nested object (I'm using CQRS via MediatR). When I submit the form, the values come across as null. How can I get the complex model to recognize the form fields?
Here is my code:
Controller:
[HttpPost]
[Route("edit")]
public async Task<IActionResult> Edit(UpdateApplicationCommand command)
{
await _mediator.Send(command)
.ConfigureAwait(false);
return RedirectToAction("Index");
}
Models:
public class UpdateApplicationCommand : IRequest<Unit>
{
public ApplicationEditGeneralViewModel ApplicationEditGeneralViewModel { get; set; } = null!;
}
public class ApplicationEditGeneralViewModel
{
[Required]
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
View:
#model ApplicationEditGeneralViewModel
<form method="post" asp-action="Edit" asp-controller="Applications">
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control", placeholder = "Application Name"})
<label for="Name">Application Name</label>
</div>
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.Description, new { #class = "form-control", placeholder = "Application Description"})
<label for="Description">Application Description</label>
</div>
<div class="d-flex flex-row-reverse bd-highlight">
<input type="submit" value="Submit" class="btn btn-primary mt-2" />
</div>
</form>
I've tried to reduce the complex model to its fields, by placing the contents of the ApplicationEditGeneralViewModel directly into the UpdateApplicationCommand class. This worked, but I'd really like to keep the nested structure so that I can reuse the ApplicationEditGeneralViewModel object.
I saw this solution here:
How to bind nested model in partial view
But I'd rather avoid adding the name as a route object (if possible) for every form field. Is there another, more simple way that I can do this?
The first way, you can custom model binding like below:
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var model = new UpdateApplicationCommand()
{
ApplicationEditGeneralViewModel = new ApplicationEditGeneralViewModel()
{
Description = bindingContext.ValueProvider.GetValue("Description").ToString(),
Name = bindingContext.ValueProvider.GetValue("Name").ToString()
}
};
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Apply the custom model binding like below:
[HttpPost]
public async Task<IActionResult> Edit([ModelBinder(typeof(CustomModelBinder))]UpdateApplicationCommand model)
{
//.....
}
The second way, just change your razor view like below:
#model UpdateApplicationCommand
<form method="post">
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.ApplicationEditGeneralViewModel.Name, new { #class = "form-control", placeholder = "Application Name"})
<label for="Name">Application Name</label>
</div>
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.ApplicationEditGeneralViewModel.Description, new { #class = "form-control", placeholder = "Application Description"})
<label for="Description">Application Description</label>
</div>
<div class="d-flex flex-row-reverse bd-highlight">
<input type="submit" value="Submit" class="btn btn-primary mt-2" />
</div>
</form>

How to update a MVC Form before Submit

So i have 2 Viewbags, they each have a list of values from a database table, the first Viewbag have all possible values of a database column, while the other have only the values corresponding to the selected value in the first Viewbag.
I have the logic for the search.
but i need to have the form update after selecting one value, since they both need to be in the same form, it is not searching for the second value.
OBS:i am only using the controllers, and cshtml views, not razor pages.
Here is a simple demo how to create cascading selectlist in asp.net core mvc:
Model:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubCategory
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string SubCategoryName { get; set; }
}
View(Index.cshtml):
<div>
<div style="float:left;width:40%">
<form id="form">
<div class="form-group row">
<label>Category Name</label>
<div class="col-12">
<select id="CategoryId" class="custom-select mr-sm-2"
asp-items="#(new SelectList( #ViewBag.Category,"Id","Name"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div class="form-group row">
<label>SubCategory Name</label>
<div class="col-12">
<select id="SubCategoryId" class="custom-select mr-sm-2"
asp-items="#(new SelectList(string.Empty,"Id","SubCategoryName"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div>
<input type="button" value="Search" />
</div>
</form>
</div>
</div>
#section Scripts
{
<script>
$(function () {
$('#CategoryId').change(function () {
var data = $("#CategoryId").val();
$.ajax({
url: '/Home/GetSubCategory?CategoryId=' + data,
type: 'Get',
success: function (data) {
var items = "";
$.each(data, function (i, item) {
items += "<option value='" + item.value + "'>" + item.text + "</option>";
});
$('#SubCategoryId').html(items);
}
})
});
})
</script>
}
Controller:
public class HomeController : Controller
{
private readonly MvcProj3Context _context;
public HomeController(MvcProj3Context context)
{
_context = context;
}
public IActionResult Index()
{
ViewBag.Category = _context.Category.ToList();
return View();
}
public JsonResult GetSubCategory(int CategoryId)
{
ViewBag.SubCategory = (from m in _context.SubCategory
where m.CategoryId == CategoryId
select m).ToList();
return Json(new SelectList(ViewBag.SubCategory, "Id", "SubCategoryName"));
}
}
Result:
The problem is, that the .cshtml file is completely rendered before its send to your browser. Therefore you cannot change it with C# code after its sent to the browser.
You could use blazor if you want to do it with c#, or you could do it with javascript.

Dynamically updating html based on viewmodel

I'm currently trying to update some HTML based on two properties in my viewmodel which are hooked to two form groups. This is ASP.NET Core.
The two form groups:
<div class="form-group">
<label asp-for="ParticipantAmount">Antal deltagere</label>
<input asp-for="ParticipantAmount" onchange="Group()" class="form-control" type="number" max="1000" min="1" />
</div>
<div class="form-group">
<label asp-for="GroupAmount">Antal grupper</label>
<input asp-for="GroupAmount" class="form-control" onchange="Group()" type="number" max="20" min="1" />
<p id="GroupSize">0</p>
</div>
The properties in the viewmodel:
public int ParticipantAmount { get; set; }
public int GroupAmount { get; set; }
The javascript method in the script section:
function Group() {
var groups = Number('#Model.GroupAmount');
var participants = Number('#Model.ParticipantAmount');
var size = participants / groups;
document.getElementById('GroupSize').innerHTML = size;
}
When I try to log stuff to the console, it seems like the properties are not being updated in the viewmodel when the form inputs change.
I think I'm missing some basic knowledge about an important aspect of what I'm trying to do, but I can't seem to google the correct keywords. Hope you can lead me in the right direction.
Just to illustrate how AJAX calls work I made a minimalistic web application using jQuery which should be used for learning only, the code is not production-ready.
Razor view. I've added default values and IDs to your the input fields to identify them easier in JavaScript code.
<div class="form-group">
<label asp-for="ParticipantAmount">Antal deltagere</label>
<input asp-for="ParticipantAmount" onchange="Group()" class="form-control" type="number" max="1000" min="1" value="#Model.ParticipantAmount" id="ParticipantAmount" />
</div>
<div class="form-group">
<label asp-for="GroupAmount">Antal grupper</label>
<input asp-for="GroupAmount" class="form-control" onchange="Group()" type="number" max="20" min="1" value="#Model.GroupAmount" id="GroupAmount" />
<p id="GroupSize">0</p>`enter code here`
</div>
JavaScript code using jQuery to get/update values and make AJAX request.
<script>
function Group() {
$.ajax({
method: "POST",
url: "/Home/UpdateAmounts",
data: { ParticipantAmount: $('#ParticipantAmount').val(), GroupAmount: $('#GroupAmount').val() }
}).done(function (response) {
$('#GroupSize').html(response);
});
}
</script>
Controller and view model. Added a method for AJAX calls.
public class HomeController : Controller
{
public IActionResult Index()
{
return View(new ParticipantViewModel());
}
// You can call this method via AJAX to update your view model
[HttpPost]
public IActionResult UpdateAmounts(int participantAmount, int groupAmount)
{
// Validation for negative numbers, and groupAmount cannot be zero
if (participantAmount < 0 || groupAmount <= 0)
{
return Ok(0);
}
return Ok(participantAmount / groupAmount);
}
}
public class ParticipantViewModel
{
public int ParticipantAmount { get; set; }
public int GroupAmount { get; set; }
}
To make things nicer and more modern you could look into following example of a simple web application using database to store data, and using Knockout for UI.

How to change input name and db name?

I build a registration form with CodeIgniter. I have a question.
As an example, one of the field from my form
<input type="text" name="username">
But in database, I will use another name instead of username such as user. How do I do it?
So, on the form it will be username, but in the database its name will be user.
Will clear the whole flow (comments may help to understand better)
View:
<form action="/user/register" method="post">
<input name="username" type="text" />
<input name="pass" type="password" />
<input name="age" type="text" />
<input type="submit" />
</form>
Controller:
<?php
class User extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('user_model');
}
public function register()
{
if($this->input->server('REQUEST_METHOD') == 'POST')
{
// if validation success
if($validation)
{
/*
Mapping form fields with DB fields
Assuming below as fields in DB
user, password, age
*/
$data['user'] = $this->input->post('username');
$data['password'] = $this->input->post('pass');
$data['age'] = $this->input->post('age');
// Add user
$this->user_mode->add_user($data);
}
}
}
}
Model:
<?php
class User_model extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->db = $this->load->database('default',true);
}
public function add_user($data)
{
$this->db->insert('users', $data);
}
}
To understand please read the Active Record section of CI User manual.

Post complex model to controller MVC using Facebook application template C#

I have following model:
class HomePageModel
{
public User user { get; set; }
public ResultHistory resultHistory { get; set; }
public Option option { get; set; }
public HomePageModel()
{
}
}
Code from controller when passing it into view:
[FacebookAuthorize]
public async Task<ActionResult> Index(Context context)
{
ViewBag.AppUrl = GlobalFacebookConfiguration.Configuration.AppUrl;
if (ModelState.IsValid)
{
var user = await context.Client.GetCurrentUserAsync<MyAppUser>();
var option = new Option();
//CODE for reading user from db, simpified
Users dbUser = db.Get(context);
var resultHistory = new ResultHistory(dbUser.NSPGW, dbUser.NSPGL,
dbUser.NMPGW,dbUser.NMPGL,dbUser.NDPGW, dbUser.NDPGL);
HomepageModel homepageModel = new HomepageModel();
homepageModel.option = option;
homepageModel.resultHistory = resultHistory;
homepageModel.user = user;
return View(homepageModel);
}
}
Code from controller
#using (Html.BeginForm("Start", "Home", FormMethod.Post, new { id="opcijeForm"}))
{
<div class="row">
<div class="col-md-4">
<div class="radio">
<label>
<input type="radio" name="radio" value="sp" />SP
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio" value="mp" />MP
</label>
</div>
<div>
<input type="submit" value="start" />
</div>
</div>
<div class="col-md-8">
<div id="spoption">
#Html.DropDownListFor(m => m.option.sl, Model.option.slSelectList)
<label style="padding-left:10px">
<input type="checkbox" id="bb" /> BB
</label>
#Html.DropDownListFor(m => m.option.swt, Model.option.swtSelectList, new {id="bbdd" })
<label style="padding-left:10px">
<input type="checkbox" id="ziv" /> ziv
</label>
#* #Html.DropDownListFor(m => m.gameOption.sln, Model.option.slnSelectList, new { id="zivdd"})*#
</div>
<div id="mpoption">
<label>Numer of oppointment</label>
#Html.DropDownListFor(m=>m.option.spn, Model.option.spnSelectList)
#Html.DropDownListFor(m=>m.option.smv, Model.option.smvSelectList)
</div>
</div>
</div>
}
and receiving model on post
[FacebookAuthorize]
[HttpPost]
public ActionResult Start(HomepageModel model)
{
//Some code
//here is for example model.option null
}
When I submit form with above model, in my controller action where I receive same model, all it's properties are null.Each class that is within HomePage has only string and int as properties. If I move something, for example from class Option and put it in HomePageModel direct, then properties I have moved are not null. Is there a way to post model as above, or should I just copy properties from these three class into HomePageModel class?
[EDIT]
It is because of meta-data FacebookAuthorize, when I remove it everything is working
MVC app facebook passing object from view to controller at submit