How can I implement a dynamic form in TYPO3? - ajax

I have created a extension to manage some courses and now I want to add some dynamic to one of my forms. This form contains two select fields. The first select field specify a course category and the second one a course. Currently I get always all categories and all courses no matter which category is selected.
Now I would like that a user can only select a course which belongs to a selected category, but I don't know how I can implement this.
I have tried to use a typeNum and some ajax, but I don't know how I can change the form model with the ajax call and without a page reload.
My template for this form looks like the following:
<f:layout name="Default" />
<f:section name="main">
<div class="sidebar-view">
<a id="form"></a>
<f:form action="list" name="form" object="{form}" section="form">
<fieldset>
<f:render partial="FormErrors" arguments="{field: 'form.category'}" />
<f:form.select class="category" property="category" options="{categoryOptions}" optionValueField="key" optionLabelField="value" prependOptionLabel="{f:translate(key: 'LLL:EXT:courses/Resources/Private/Language/locallang_db.xlf:tx_courses_domain_model_form.category')}" />
<f:render partial="FormErrors" arguments="{field: 'form.course'}" />
<f:form.select property="course" options="{courseOptions}" optionValueField="key" optionLabelField="value" prependOptionLabel="{f:translate(key: 'LLL:EXT:courses/Resources/Private/Language/locallang_db.xlf:tx_courses_domain_model_form.course')}" />
<f:form.hidden class="id" property="pageId" />
<f:form.submit name="send" value="{f:translate(key: 'LLL:EXT:courses/Resources/Private/Language/locallang_db.xlf:tx_courses_domain_model_form.submit')}" />
</fieldset>
</f:form>
</div>
</f:section>
As you can see it is a simple form with two select fields, a submit button and a hidden field which stores the page id. I don't want to submit the form when the category is changed so I think there is no way to implement this dynamic without ajax.
For this reason I have implemented the following typenum:
obj.ajaxPrototype = PAGE
obj.ajaxPrototype {
typeNum = 0
config {
disableAllHeaderCode = 1
additionalHeaders = Content-type: text/plain
xhtml_cleaning = 0
debug = 0
no_cache = 1
admPanel = 0
}
}
ajaxCall < obj.ajaxPrototype
ajaxCall {
typeNum = 1001
10 < tt_content.list.20.courses_p1
}
After that I have added some javascript (jQuery) to execute a controller action via ajax when the select field is changed:
$(document).ready(function() {
$('.tx-courses select:first-of-type').on('change', function() {
$.ajax({
method: "POST",
url: "index.php?id=" + $('.id').val(),
data: "type=1001&tx_courses_p1[action]=check&tx_courses_p1[form][category]="+$('.category').val(),
success: function(msg) {
alert(msg);
}
});
});
});
Simple action:
/**
* check action
*
* #return void
*/
public function checkAction() {
return 'ajax call fired';
}
If I change the select field I get the message "ajax call fired". So my controller action will be executed and I might implement some "get courses by requested category id" logic and so on now, but how can I update the form from above which will be rendered by my sidebarAction with the ajax call and without a page reload?

Ok, I found a way to get it work.
At first I changed the ontent type of my typeNum from above to json, because I want to work with json.
additionalHeaders = Content-type: application/json
Then I changed my ajax call:
$(document).ready(function() {
var arr;
var ajaxCall = function() {
$.ajax({
method: "POST",
url: "index.php?id=" + $('.id').val(),
data: "type=1001&tx_courses_p1[action]=check&tx_courses_p1[form][category]="+$('.category').val(),
success: function(msg) {
$('.tx-courses .course option:not(:first-child)').remove();
arr = $.map(msg, function(el) { return el });
arr.sort();
$.each(arr, function(i, val) {
$('.tx-courses .course').append('<option value="'+val+'">'+val+'</option>');
});
}
});
};
// init (if someone submitted the form, but forgot to select a category)
ajaxCall();
// someone changed the category
$('.tx-courses .category').on('change', function() {
ajaxCall();
});
});
And my controller action for this ajax request looks as follows:
/**
* check action
*
* #return string
*/
public function checkAction() {
// get arguments
$args = $this->request->getArguments();
// create container for courses which are depended on selected category
$coursesBySelectedCategory = array();
// check if category was posted and category exists
if (isset($args['form']['category']) && $this->courseRepository->categoryExists($args['form']['category'])) {
// get all courses which belongs to this category
$courses = $this->courseRepository->findByCategoryId($args['form']['category']);
} else {
// get all courses
$courses = $this->courseRepository->findAllNotExpiredCourses();
}
// store course names
foreach($courses as $course) {
$coursesBySelectedCategory[] = $course->getName();
}
// remove double entries
$coursesBySelectedCategory = array_unique($coursesBySelectedCategory);
return json_encode($coursesBySelectedCategory);
}
Your feedback is welcome :).

Related

What to return after Ajax call asp.net

After ajax call is completed and the model is posted successfully to the controller, what should the controller return?
In my case, I just want to add an item to the wishlist and that's it, no redirect.
Controller can return a message or somethingelse for sure that your action did successful
This question you need to know two points, 1.What type can asp.net core return? 2.What type can ajax can receive.
First, Asp.net core can return the following types: Specific type, IActionResult, ActionResult<T>, Learn more details in this document.
Second, Ajax can send and receive information in various formats, including JSON, XML, HTML, and text files.
From your question, I think you want to recive the model from controller and add it to the wishlist in the view. So, In my opinion, You can directly return the specified model, Asp.net core will serialize models to Json Automatically. Then you can use it in your ajax success method.
simple demo:
<div class="text-center" id="Test">
<h1 class="display-4">Welcome</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
<button onclick="GetDetails(2)">submit</button>
#section Scripts{
<script>
function GetDetails(id){
var postData = {
'ProductId': id,
};
$.ajax({
type: "Post",
url: "/Home/privacy",
data: postData,
success: function (res) {
document.getElementById("Test").innerHTML = res["name"];
}
});
}
</script>
}
Controller
List<Student> students = new List<Student>()
{
new Student()
{
Id="1",
Name="Jack",
Country="USA"
},
new Student()
{
Id="2",
Name="Nacy",
Country="Uk"
},
new Student()
{
Id="3",
Name="Lily",
Country="Cn"
}
};
[HttpPost]
public Student Privacy(string ProductId)
{
var result = students.Where(x => x.Id == ProductId).FirstOrDefault();
return result;
}

on check box check send value to partial view and update it mvc 3

i have two partial view one is having a 5 checkbox (for filtering) and another will display the filtered data.
<input type="checkbox" data-toggle="checkbox" id="2000-5000"/>Rs.2000-Rs.5000
<input type="checkbox" data-toggle="checkbox" id="2000-5000"/>Rs.2000-Rs.5000
and with help of jquery am sending the request to controller..
public PartialViewResult PhonesPartail(int? id,string where)
{
var list = paginateRsult(id, "", where).ToList();
ViewData["totalpages"] = totalPages;
return PartialView("_phonelist",list);
}
and the jquery
$(document).on("change", ".price-checkbox input[type=checkbox]", function () {
if ($(this).is(":checked")) {
var prange = $(this).attr("id");
var parray = prange.split('-');
var whereclause = "price>=" + parray[0] + " and price <=" + parray[1];
$.ajax({
url: '../../Phones/PhonesPartail',
data: { where: whereclause },
type: 'POST',
success: function (data) {
$("#phone-list").append(data);
}
});
}
});
now the list which is returned , is getting added to the div with entire html page.. means again the html page is getting added in the div..
any solution..
Thanks in advance...
use html() function of jquery
It will paste data returned by partial view in to a div rather then appending it to div
$("#phone-list").html(data);

Calling multiple action methods (using ajax) and showing the result of last in a new tab

I have a form in which I need to call two action methods, one after the other. This is how the flow goes.
First I check if the prerequisite data is entered by the user. If not then I show a message that user needs to enter the data first.
If all the prerequisite data is entered, I call an action method which return data. If there is no data returned then I show a message "No data found" on the same page.
If data is returned then I call another action method present in a different controller, which returns a view with all the data, in a new tab.
The View:
#using (Ajax.BeginForm("Index", "OrderListItems", null, new AjaxOptions { OnBegin = "verifyRequiredData"}, new { #id = "formCreateOrderListReport", #target = "_blank" }))
{
//Contains controls and a button
}
The Script in this View:
function verifyRequiredData() {
if ($("#dtScheduledDate").val() == "") {
$('#dvValidationSummary').html("");
var errorMessage = "";
errorMessage = "<span>Please correct the following errors:</span><ul>";
errorMessage += "<li>Please enter Scheduled date</li>";
$('#dvValidationSummary').append(errorMessage);
$('#dvValidationSummary').removeClass('validation-summary-valid').addClass('validation-summary-errors');
return false;
}
else {
$('#dvValidationSummary').addClass('validation-summary-valid').removeClass('validation-summary-errors');
$('#dvValidationSummary').html("");
$.ajax({
type: "GET",
url: '#Url.Action("GetOrderListReport", "OrderList")',
data: {
ScheduledDate: $("#dtScheduledDate").val(),
Crews: $('#selAddCrewMembers').val(),
Priorities: $('#selPriority').val(),
ServiceTypes: $('#selServiceTypes').val(),
IsMeterInfoRequired: $('#chkPrintMeterInfo').val()
},
cache: false,
success: function (data) {
debugger;
if (data !== "No data found") {
//var newUrl = '#Url.Action("Index", "OrderListItems")';
//window.open(newUrl, '_blank');
return true;
} else {
//Show message "No data found"
return false;
}
}
});
return false;
}
}
The "GetOrderListReport" Action method in "OrderList" Controller:
public ActionResult GetOrderListReport(OrderListModel model)
{
var contract = new OrderReportDrilldownParamDataContract
{
ScheduledDate = model.ScheduledDate
//Setting other properties as well
};
var result = OrderDataModel.GetOrderList(contract);
if (string.IsNullOrWhiteSpace(result) || string.IsNullOrEmpty(result))
{
return Json("No data found", JsonRequestBehavior.AllowGet);
}
var deserializedData = SO.Core.ExtensionMethods.DeserializeObjectFromJson<OrderReportDrilldownDataContract>(result);
// send it to index method for list
TempData["DataContract"] = deserializedData;
return Json(deserializedData, JsonRequestBehavior.AllowGet);
}
The last action method present in OrderListItems Controller, the result of which needs to be shown in a new tab:
public ActionResult Index()
{
var deserializedData = TempData["DataContract"] as OrderReportDrilldownDataContract;
var model = new OrderListItemViewModel(deserializedData);
return View(model);
}
The problem is that I am not seeing this data in a new tab, although I have used #target = "_blank" in the Ajax.BeginForm. I have also tried to use window.open(newUrl, '_blank') as can be seen above. But still the result is not shown in a new tab.
Please assist as to where I am going wrong?
If you are using the Ajax.BeginForm you shouldn't also be doing an ajax post, as the unobtrusive ajax library will automatically perform an ajax post when submitting the form.
Also, if you use a view model with data annotation validations and client unobtrusive validations, then there would be no need for you to manually validate the data in the begin ajax callback as the form won't be submitted if any validation errors are found.
The only javascript code you need to add in this scenario is a piece of code for the ajax success callback. That will look as the one you currently have, but you need to take into account that opening in new tabs depends on the browser and user settings. It may even be considered as a pop-up by the browser and blocked, requiring the user intervention to allow them as in IE8. You can give it a try on this fiddle.
So this would be your model:
public class OrderListModel
{
[Required]
public DateTime ScheduledDate { get; set; }
//the other properties of the OrderListModel
}
The form will be posted using unobtrusive Ajax to the GetOrderListReport of the OrderList controller. On the sucess callback you will check for the response and when it is different from "No data found", you will then manually open the OrderListItems page on a new tab.
This would be your view:
#model someNamespace.OrderListModel
<script type="text/javascript">
function ViewOrderListItems(data){
debugger;
if (data !== "No data found") {
var newUrl = '#Url.Action("Index", "OrderListItems")';
//this will work or not depending on browser and user settings.
//passing _newtab may work in Firefox too.
window.open(newUrl, '_blank');
} else {
//Show message "No data found" somewhere in the current page
}
}
</script>
#using (Ajax.BeginForm("GetOrderListReport", "OrderList", null,
new AjaxOptions { OnSucces= "ViewOrderListItems"},
new { #id = "formCreateOrderListReport" }))
{
#Html.ValidationSummary(false)
//input and submit buttons
//for inputs, make sure to use the helpers like #Html.TextBoxFor(), #Html.CheckBoxFor(), etc
//so the unobtrusive validation attributes are added to your input elements.
//You may consider using #Html.ValidationMessageFor() so error messages are displayed next to the inputs instead in the validation summary
//Example:
<div>
#Html.LabelFor(m => m.ScheduledDate)
</div>
<div>
#Html.TextBoxFor(m => m.ScheduledDate, new {id = "dtScheduledDate"})
#Html.ValidationMessageFor(m => m.ScheduledDate)
</div>
<input type="submit" value="Get Report" />
}
With this in place, you should be able to post the data in the initial page using ajax. Then based on the response received you will open another window\tab (as mentioned, depending on browser and user settings this may be opened in a new window or even be blocked) with the second page content (OrderListItems).
Here's a skeleton of what I think you are trying to do. Note that window.open is a popup though and most user will have popups blocked.
<form id="formCreateOrderListReport">
<input type="text" vaule="testing" name="id" id="id"/>
<input type="submit" value="submit" />
</form>
<script type="text/javascript">
$('#formCreateOrderListReport').on('submit', function (event) {
$.ajax({
type: "POST",
url: '/home/test',
data: { id: $('#id').val()},
cache: false
}).done(function () {
debugger;
alert("success");
var newUrl = '/home/contact';
window.open(newUrl, '_blank');
}).fail(function () {
debugger;
alert("error");
});
return false;
});
</script>
Scale down the app to get the UI flow that you want then work with data.

Can I Use PartialViewResult with PagedList and show it in a PartialView

Hi everyone I have a question, Can I use PagedList in a PartialViewResult Action and show the result in a PartialView?
Here is some code
Controller Code:
public PartialViewResult CargosPorProyecto(string id, int? page)
{
var cargos = db.Cargo.Include(i => i.Proyectos).Where(i => i.NumProyecto.Equals(id)).OrderByDescending(i => i.Fecha);
if (Request.HttpMethod != "GET")
{
page = 1;
}
var pageSize = 10;
var pageNumber = (page ?? 1);
var onePage = cargos.ToPagedList(pageNumber, pageSize);
return PartialView("ListaCargosParcial", ViewBag.OnePage = onePage);
}
In my PartialView i put this code to show the pagination
<div class="pagination-right">
<div class="span12">
<%: Html.PagedListPager((IPagedList)ViewBag.OnePage, page => Url.Action("CargosPorProyecto", new { page = page }), new PagedListRenderOptions { LinkToFirstPageFormat = "<< Primera", LinkToPreviousPageFormat = "< Anterior", LinkToNextPageFormat = "Siguiente >", LinkToLastPageFormat = "Ăšltima >>" })%>
</div>
</div>
And when i load the page that contains the partialview everything looks good, but when i click in Next ("Siguiente") doesn't load in my partial view.
I hope I explained clearly and thanks for the time.
Regards
You could use AJAX if you want to stay on the same page. For example if you are using jQuery you could subscribe to the click event of the pagination links and then trigger an AJAX request to the corresponding controller action and refresh the partial with the results returned:
$(function() {
$('.pagination-right a').click(function() {
$.ajax({
url: this.href,
type: 'GET',
cache: false,
success: function(result) {
// refresh the contents of some container div for the partial
// make sure you use the correct selector here
$('#some_container_for_the_partial').html(result);
}
});
// cancel the default action which is a redirect
return false;
});
});

call to controller to populate text box based on dropdownlistfor selection using Ajax

I have a dropdown and when I select an item from it, I want to pass on the selected value to a function in a controller, query the db and auto load a text box with query results.
How do I use Ajax to make that call to the controller when there is onclick() event on the dropdown?
My dropdown and textbox in my aspx page is:
<%: Html.DropDownListFor(model => model.ApplicationSegmentGuid, Model.ApplicationSegment)%>
<%: Html.TextAreaFor(model => model.EmailsSentTo, false, new { style = "width:500px; height:50px;" })%>
My function in controller is
public ActionResult AsyncFocalPoint(Nullable<Guid> ApplicationSegmentGuid)
{
string tempEmail = UnityHelper.Resolve<IUserDirectory>().EmailOf();
tempEmail = "subbulakshmi.kailasam#lyondellbasell.com" + tempEmail;
IList<string> EmailAddresses = new List<String>();
using (TSADRequestEntities context = UnityHelper.Resolve<TSADRequestEntities>())
{
EmailAddresses = context.FOCALPOINTs.Where(T => T.APPLICATIONSEGMENT.ItemGuid == ApplicationSegmentGuid && T.FlagActive)
.Select(T => T.Email).ToList();
}
foreach (string emailAddress in EmailAddresses)
tempEmail = tempEmail + ";" + emailAddress;
return Json(tempEmail, JsonRequestBehavior.AllowGet);
}
You could give your dropdown an id and url:
<%= Html.DropDownListFor(
model => model.ApplicationSegmentGuid,
Model.ApplicationSegment,
new { id = "myddl", data_url = Url.Action("AsyncFocalPoint") }
) %>
and then subscribe to the .change() event of the dropdown list unobtrusively and trigger the AJAX request:
$(function() {
$('#myddl').change(function() {
// get the selected value of the ddl
var value = $(this).val();
// get the url that the data-url attribute of the ddl
// is pointing to and which will be used to send the AJAX request to
var url = $(this).data('url');
$.ajax({
url: url,
type: 'POST',
data: { applicationSegmentGuid: value },
success: function(result) {
// TODO: do something with the result returned by the server here
// for example if you wanted to show the results in your textarea
// you could do this (it might be a good idea to override the id
// of the textarea as well the same way we did with the ddl):
$('#EmailsSentTo').val(result);
}
});
});
});

Resources