Passing complex object from Angularjs controller to MVC controller is not working - javascript

On Ajax call from Angular controller, i am passing a complex object as data. On MVC controller object has all null values.
I have MVC view as given below, which will be the boiler plate for Register customer View.
<div data-ng-app="customer" id="customer" data-ng-controller="rootViewModel">
<h2>{{ pageHeading }}</h2>
<hr />
<form id="formElement">
<div ng-view></div>
</form>
Using AngularJS, I will be loading the register customer view, mark of register customer view given below. I have register customer function tied to button using ng-click directive.
<fieldset class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-3">Company Name</label>
<div class="col-sm-4">
<input class="form-control inputfieldValidation" ng-model="customer.Name" type="text" placeholder="Full company name" required autofocus />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">PAN</label>
<div class="col-sm-4">
<input class="form-control" ng-model="customer.Pan" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">TIN</label>
<div class="col-sm-4">
<input class="form-control inputfieldValidation" ng-model="customer.Tin" type="text" required />
</div>
</div>
<button class="btn btn-primary proceedNext" id="registerCompany" ng-click="registerCompany(customer)">Register Customer</button>
</fieldset>
I have angular controller, which has function called registerCustomer() that will be called on click of register customer. I have an ajax call inside that function as given below.
customerModule.controller("CustomerRegistration", function ($scope) {
var initialize = function () {
}
$scope.registerCompany = function (customer) {
$.ajax({
url: 'Home/RegisterCompany',//make sure url exist
data: JSON.stringify({company: customer}),//pass data to action
type:'POST',
success: function (data) {
alert(JSON.stringify(data));
//window.location.href = '#Url.Action("Order")'; //redirect
}
});
}
initialize();
});
On MVC, i have a model called Company as given below.
public class Company
{
public string Name;
public string Pan;
public string Tin;
}
and my MVC controller look as
[HttpPost]
public JsonResult RegisterCompany(Company company)
{
//Do something
return null;
}
Always I have null object on MVC controller, please help me if am missing anything. Thanks in advance

EDIT: It looks like you need a view model in mvc or a modification to your post:
public class CompanyViewModel {
public Company company { get; set; }
}
Or use data: JSON.stringify(customer) instead of data: JSON.stringify({ company: customer })
Here is a working example from a website we are developing. It uses Riot.js instead of angular, but the concepts will be similar.
See also http://www.abeautifulsite.net/postjson-for-jquery/
$.getJSON(self.baseUrl + "/SaveApplicant", $('form.#applicant').serialize(), function (response) {
if (response.errorMessage) {
RiotControl.trigger('error_message', response.errorMessage);
return;
} else {
self.packageQuote.applicant = response;
}
RiotControl.trigger("continue","applicant");
});
Or using post, per the link above
$.post(self.baseUrl + "/SaveApplicant", $('form.#applicant').serialize(), function (response) {
if (response.errorMessage) {
RiotControl.trigger('error_message', response.errorMessage);
return;
} else {
self.packageQuote.census = response;
}
RiotControl.trigger("continue","applicant");
},'json');
There is a bit more involved on the MVC side of things, to send back a json response with lower case property name prefixes:
public ActionResult SaveApplicant(Applicant model)
{
if (ModelState.IsValid)
{
var applicant = DbContext.Applicants.FirstOrDefault(row => row.Id == model.Id);
if (applicant == null) {
DbContext.Applicants.Add(model);
} else {
applicant.Clone(model); // implement as needed or use entity state modified.
}
DbContext.SaveChanges();
return FormattedJsonResult(applicant);
}
return ModelErrors();
}
public ActionResult FormattedJsonResult(object model)
{
var camelCaseFormatter = new JsonSerializerSettings();
camelCaseFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();
var result = JsonConvert.SerializeObject(model, camelCaseFormatter);
return Content(result, "application/json");
}
public ActionResult ModelErrors()
{
return FormattedJsonResult(
new
{
errorMessage =
String.Join("\n",
ModelState.Values.SelectMany(value => value.Errors).Select(error => error.ErrorMessage))
});
return View();
}

Related

When one form is updated (using Ajax) reloading the forms make them have the same values as the previously updated one

I'm trying to make a simple Todo app to learn asp net core mvc.
I did the CRUD to manage the todos and it worked fine. For the next step i wanted to try adding Ajax to it (avoiding to reload the entire page), delete worked fine, create too, but when i want to edit one todo (which is basically a form) the response of the Ajax request sets all the inputs of all the todos at the same value.
If I update "Buy chocolat" by "Buy chocolate" as the title of one todo, all other todos will have a title "Buy chocolate".
If I refresh the page (or just the section containing todos) everything goes back to normal, which means the database updated just the todo I wanted to.
It's really weird and it probably comes from the fact that the inputs have the same name value (todo 1 title => todo.Title, todo 2 title => todo.Title, etc...) even though it works fine for all the rest.
Here's the page with the container of todos :
#model IEnumerable<TodoApp.Models.Todo>
#section Css{
<link href="/css/todos.css" rel="stylesheet" />
<link href="~/lib/fontawesome/css/all.css" rel="stylesheet" />
}
#{
ViewData["Title"] = "List of todos";
}
<h1>My list of Todos</h1>
<span class="error-span" style="color:red"></span>
<div id="main_container">
<i onclick="createTodo()" id="create-button" class="fas fa-plus-circle" title="Add new todo"></i>
<div id="todos_container">
#await Html.PartialAsync("_TodoList", Model)
</div>
</div>
<partial name="_DeleteModal">
#section Scripts{
<script src="~/js/todos.js"></script>
}
Here's the foreach that displays all todos which also is the partial view "_TodoList" :
#model IEnumerable<TodoApp.Models.Todo>
#foreach (Todo todo in Model)
{
<form class="todo" asp-action="Edit" asp-controller="Todos" data-id="#todo.Id">
<input type="hidden" asp-for="#todo.Id" id="id_#todo.Id" />
<div class="todo-up todo-row">
<textarea autocomplete="off" placeholder="Put the title here..." class="todo-header" asp-for="#todo.Title" id="title_#todo.Id" ></textarea>
<textarea autocomplete="off" placeholder="Put the description here..." class="todo-description" asp-for="#todo.Description" id="decription_#todo.Id" ></textarea>
</div>
<div class="todo-down todo-row">
<div class="todo-validation-row">
<span></span>
<i class="fa-regular fa-check todo-edit" alt="Saved"></i>
<span class="tooltip-text">Saved</span> #*Tooltip for edition*#
</div>
<div class="todo-footer">
<div class="todo-updated"><img src="~/assets/img/update.svg" alt="Updated at" /><span>#todo.UpdatedDate</span></div>
<a onclick="showDeleteModal(#todo.Id)" title="Delete todo">
<i class="fas fa-trash"></i>
</a>
</div>
</div>
</form>
}
The beginning of the controller method :
[HttpPatch]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit([Bind("Id", "Title", "Description")] Todo todo)
{
if (ModelState.IsValid)
{
var matchingTodo = await _context.Todos.FindAsync(todo.Id);
if (matchingTodo != null)
{
if (GetConnectedUserId() == matchingTodo.UserId)
{
matchingTodo.Title = todo.Title;
matchingTodo.Description = todo.Description;
matchingTodo.UpdatedDate = DateTime.Now;
_context.Update(matchingTodo);
await _context.SaveChangesAsync();
var todos = GetTodosOfConnectedUser();
var partialView = PartialView("_TodoList", todos);
return partialView;
The GetTodosOfConnectedUser method (which return an Enumerable object of Todos that belongs to the user currently connected) :
private IEnumerable<Todo> GetTodosOfConnectedUser()
{
return _context.Todos.Where(t => t.UserId == Convert.ToInt32(HttpContext.User.FindFirst("user_id").Value)).OrderByDescending(t => t.UpdatedDate);
}
And the JS for the Ajax request :
${'.todo'}.on("change", function (ev) {
let form = ev.currentTarget;
editTodo(form);
});
function editTodo(form) {
try {
$.ajax({
type: 'PATCH',
url: form.action,
data: new FormData(form),
processData: false,
contentType: false,
success: function (res) {
$(".error-span").html("");
$('#todos_container').html(res);
},
error: function (err) {
console.log(err);
$(".error-span").html("An error occured please try again.");
}
});
return false;
}
catch (ex) {
console.log(ex);
}
}
Thank you for your time
So, the problem is weird. Like, really weird.
I have followed what's happening step-by-step and everything is going smoothly and then... All the forms get the same inputs/textareas for no apparent reasons.
I believe it comes from the fact that I create one form for each todo, which is a really bad practice, probably never meant to be done in the first place. If you ever encounter this problem, just change the way you do it.
I show my code and hope it can help you :
model
public class Todo
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string UpdatedDate { get; set; }
}
view
#model IEnumerable<TodoApp.Models.Todo>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<div id="todos_container">
#foreach (Todo todo in Model)
{
<form class="todo" asp-action="Edit" asp-controller="Todos" data-id="#todo.Id">
<input type="hidden" asp-for="#todo.Id" id="id_#todo.Id" />
<div class="todo-up todo-row">
<textarea autocomplete="off" placeholder="Put the title here..." class="todo-header" asp-for="#todo.Title" id="title_#todo.Id" ></textarea>
<textarea autocomplete="off" placeholder="Put the description here..." class="todo-description" asp-for="#todo.Description" id="decription_#todo.Id" ></textarea>
</div>
<div class="todo-down todo-row">
<div class="todo-validation-row">
<span></span>
<i class="fa-regular fa-check todo-edit" alt="Saved"></i>
<span class="tooltip-text">Saved</span> #*Tooltip for edition*#
</div>
<div class="todo-footer">
<div class="todo-updated"><img src="~/assets/img/update.svg" alt="Updated at" /><span>#todo.UpdatedDate</span></div>
<a onclick="showDeleteModal(#todo.Id)" title="Delete todo">
<i class="fas fa-trash"></i>
</a>
</div>
</div>
</form>
}
</div>
#*#section Scripts {*#
<script>
$('.todo').on("change", function (ev) {
let form = ev.currentTarget;
editTodo(form);
});
function editTodo(form) {
try {
$.ajax({
type: 'PATCH',
url: '/todoes/Edit',
data: new FormData(form),
processData: false,
contentType: false,
success: function (res) {
$(".error-span").html("");
$('#todos_container').html(res);
},
error: function (err) {
console.log(err);
$(".error-span").html("An error occured please try again.");
}
});
return false;
}
catch (ex) {
console.log(ex);
}
}
</script>
#*}*#
action
[HttpPatch]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit([Bind("Id", "Title", "Description")] Todo todo)
{
if (ModelState.IsValid)
{
var matchingTodo = await _context.Todos.FindAsync(todo.Id);
if (matchingTodo != null)
{
matchingTodo.Title = todo.Title;
matchingTodo.Description = todo.Description;
matchingTodo.UpdatedDate = DateTime.Now.ToString();
_context.Update(matchingTodo);
await _context.SaveChangesAsync();
var todos = GetTodosOfConnectedUser();
var partialView = PartialView("_TodoList", todos);
return partialView;
}
return BadRequest();
}
return View(todo);
}

laravel/JS typeahead for two different input fields

I have two fields in my form, customer name, card name, i am trying to implement autocomplete to fetch details from the database to auto fill the rest of the fields.
I have got typeahead working on the first input field, however, when using the same method for the second field which is the card number, autofill is not coming up at all. I am not sure what i am doing wrong, a little guidance is appreciated here.
here is my create.blade.php
<div class="form-group">
<strong>Customer Name:</strong>
<input class="typeahead form-control" type="text" id='cust' onkeypress="myFunction1()" placeholder="Customer Name">
</div>
<div class="form-group">
<strong>Card Number:</strong>
<input class="typeahead form-control" type="text" id='card' autocomplete="off" onkeypress="myFunction()" placeholder="Card Number">
</div>
<script>
function myFunction() {
var path = "{{ route('autocompletecard') }}";
$('#card').typeahead({
source: function (query, process) {
return $.get(path, { query: query }, function (data) {
return process(data);
});
}
});
}
function myFunction1()
{
var path = "{{ route('autocomplete') }}";
$('#cust').typeahead({
source: function (query, process) {
return $.get(path, { query: query }, function (data) {
return process(data);
});
}
});
}
</script>
here is my Assignee controller:
public function autocomplete(Request $request)
{
$data = Customer::select("name")
->where("name","LIKE","%{$request->input('query')}%")
->get();
return response()->json($data);
}
public function autocompletecard(Request $request)
{
$data = Card::select("number")
->where("number","LIKE","%{$request->input('query')}%")
->get();
return response()->json($data);
}
after checking the console and network tab on the browser autocompletecard is being executed, it is returning a card number as response. but it is not showing as autocomplete.

ajax mvc html: jquery val not preventing call to api

When clicking a button I am calling a web api with ajax. My form is using JqueryVal, to make form validations, according to my viewmodel data annotations.
My problem is that when I click the button "Listo" in my form, it calls my API, inspite of jqueryval is marking an error( selecting a file is required)
This is my code:
My viewmodel that contains data annotations(the dataannotations are used along with the jquery.validate.js and jquery.validate.unobtrusive.js. As you can see, it is working, but is not preventing the API from being called):
public class CursoViewModel
{
public Guid Id { get; set; }
[MaxLength(125)]
public string Titulo { get; set; }
[Required]
public string Descripcion { get; set; }
[Required(ErrorMessage ="selecciona una imagen para tu curso")]
[DataType(DataType.Upload)]
public HttpPostedFileBase Imagen { get; set; }
}
The class posted to my api
public class person
{
public string name { get; set; }
public string surname { get; set; }
}
The Api code
[HttpPut]
[Route("api/ProfesorCurso/test")]
public string Put(person p)
{
return p.name + p.surname;
}
My View
#model project.ViewModels.CourseViewModel
<form id="Editcurso" method="post" action="#">
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "Please fix the following errors.")
<div class="container">
<div class="form-group">
#Html.LabelFor(c=>c.Titulo)
#Html.TextBoxFor(c => c.Titulo, new {id="titulo", #class="form-control"})
#Html.ValidationMessageFor(m => m.Titulo)
</div>
<div class="form-group">
#Html.LabelFor(c => c.Descripcion)
#Html.TextAreaFor(c => c.Descripcion, new {id="descripcion", #class = "form-control" })
#Html.ValidationMessageFor(m => m.Descripcion)
</div>
<div class="thumbnail" id="imagencurso"></div>
<div class="form-group">
#Html.LabelFor(m => m.Imagen)
#Html.TextBoxFor(m => m.Imagen, new {id="imagen" ,type = "file", data_rule_validCustomer = "true" })
#Html.ValidationMessageFor(m => m.Imagen)
</div>
<button id="submiter" type="submit" class="btn btn-primary">Listo!</button>
</div>
</form>
The scripts in the view
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
<script>
$(document).ready(function () {
$("#submiter").click(function () {
jQuery.support.cors = true;
var person = new Object();
person.name = "Sourav";
person.surname = "Kayal";
$.ajax({
url: '/api/ProfesorCurso/test',
type: 'PUT',
dataType: 'json',
data: person,
success: function (data) {
console.log(data);
return false;
},
error: function (x, y, z) {
alert('error al postear');
return false;
}
});
});
});
</script>
}
What can I do to prevent ajax to call my api when clicking my form button, if there are Jquery validation errors?
thanks
You should be handling the .submit() event of the form, and then your can check .valid(), and if not cancel the ajax call. Note you should also be cancelling the default submit.
$('#Editcurso').submit(e) {
e.preventDefault(); // prevent the default submit
if (!$(this).valid()) {
return; // exit the function and display the errors
}
....
$.ajax({
....
});
}
As a side note, there is no point adding new { id="titulo" } etc - the HtmlHelper methods that generate form controls already add an id attribute based on the property name

how to post data to server using angularJS, web api

i am new to angularJS and trying to figure out, how to post data to my server, problem is that my post to server is successful and it create new record but all is empty(null).
Thanks for any advice
JS:
$scope.addChannel = function () {
var channels = [];
//var newChannel = {
// Name: $scope.Name
//};
//clearing error message
$scope.errorMessage = "";
$http.post("api/Channel/channels", newChannel)
.success(function (newChannel) {
//on success
$scope.channels.push({ "Name": $scope.Name });
console.log("data added");
// newChannel.push(response.data);
newChannel = {};
}, function () {
//on failure
$scope.errorMessage = "Failed to save data";
})
}
HTML:
<div ng-controller="ChannelController">
<div class="col-md-4 col-lg-4 col-sm-4">
<form novalidate name="newUser"ng-submit="addChannel()">
<div class="form-group">
<label for="name">Channel</label>
<input class="form-control" type="text" id="Name" name="Name" ng-model="newChannel.Name" />
</div>
<div class="form-group">
<input type="submit" value="Add" class="btn btn-success" />
</div>
</form>
<a ng-href="/Dashboard#/channels">Return to dashboard</a>
</div>
<div class="has-error" ng-show="errorMessage">
{{errorMessage}}
</div>
Channel Controller
[HttpPost("channels")]
public async System.Threading.Tasks.Task Create(Channel channel)
{
await _channelRepository.CreateChannel(channel);
}
Repository
public async System.Threading.Tasks.Task CreateChannel(Channel channel)
{
_context.Channel.Add(channel);
await _context.SaveChangesAsync();
}
Check if you name properties correctly in object that you send to server, property names are case sensitive. Looks like you have in js
var customer = {name: 'foo', lastName: 'bar'};
and on server you try to deserialize your JSON to this entity
class Customer {
public Name {get;set;} //note prop names are capitalized
public LastName {get;set;}
}

SilverStripe submit HTML form through Ajax

I want to pass data from a simple HTML form to a controller through Ajax, then process the data and return a response back.
At the moment I have the following:
HomePage.ss
<form method="POST" class="form-horizontal submit-form" onsubmit="return checkform(this);">
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="name">Name</label>
<div class="col-md-8">
<input id="name" name="name" type="text" placeholder="insert full Name" class="form-control input-md" required="" />
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="send-btn"></label>
<div class="col-md-8">
<button id="send-btn" name="send-btn" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
JavaScript
$('form.submit-form').submit(function() {
$.ajax({
type: 'POST',
url: 'processForm',
data: $(this).serialize(),
success: function(data) {
alert('data received');
}
});
});
HomePage.php
class HomePage_Controller extends Page_Controller {
public function events() {
$events = CalendarEvent::get();
return $events;
}
public function processForm() {
if (Director::is_ajax()) {
echo 'ajax received';
} else {
//return $this->httpError(404);
return 'not ajax';
}
}
}
In developer tools I can see that I got the xhr processForm with a 404 not found error.
How do I get this Ajax form working correctly with the SilverStripe controller?
Spider,
I've done something similar to below. This is a quick and dirty demo and hasn't been tested, but it may get you going in the right path. If you're unfamiliar with how forms work within SilverStripe there is a lesson for front end forms in SilverStripe. I've found the lessons useful personally and provide the code for the lesson as well: http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms?ref=hub
Page.php
<?php
class Page extends SiteTree
{
}
class Page_Controller extends Content_Controller
{
private static $allowed_actions = array(
'MyForm',
);
public function MyForm()
{
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.min.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/jquery.validate.min.js');
Requirements::javascript('/path/to/your/validation/script.js');
$fields = FieldList::create(
TextField::create('name')
->setTitle('Name')
);
$actions = FieldList::create(
FormAction::create('doSubmit')
->setTitle('Submit')
);
$requiredFields = RequiredFields::create(
'name'
);
$form = Form::create($this, 'MyForm', $fields, $actions, $requiredFields);
return $form;
}
public function doSubmit($data, $form)
{
//process $data or create your new object and simpley $form->saveInto($yourObject); then $yourObject->write()
//then deal with ajax stuff
if ($this->request->isAjax()) {
return $this->customise(array(
'YourTemplateVar' => 'Your Value'
))->renderWith('YourIncludeFile');
} else {
//this would be if it wasn't an ajax request, generally a redirect to success/failure page
}
}
}
YourValidationScript.js
(function ($) {
$(function () {
$('#MyForm_Form').validate({
submitHandler: function (form) {
$.ajax({
type: $(form).attr('method'),
url: $(form).attr('action') + "?isAjax=1",
data: $(form).serialize()
})
.done(function (response) {
$('.content').html(response);
})
.fail(function (xhr) {
alert('Error: ' + xhr.responseText);
});
},
rules: {
name: "required"
}
});
})
})(jQuery);
You need to understand how HTTP request routing is handled in SilverStripe.
When you send request POST /processForm, it is treated as page and managed by ModelAsController. That is why you get 404 error - there is no SiteTree record with URLSegment = processForm.
Solution 1
Use Form object. It creates all routing configuration automatically during runtime. Read more
https://docs.silverstripe.org/en/3.3/tutorials/forms/
https://docs.silverstripe.org/en/3.3/developer_guides/forms/
Solution 2
Use this approach, when you really want to go down to the simple one method request handler. Register custom controller and routing.
You specify your route in mysite/_config/routing.yml
---
Name: siteroutes
---
Director:
rules:
processCustomForm: CustomFormController
Handle your request
class CustomFormController extends Controller
{
public function handleRequest( SS_HTTPRequest $request, DataModel $model ) {
if (!$request->isPost()) {
// handle invalid request
}
$name = $request->postVar('name')
// process your form
}
}

Categories

Resources