knockout computed not updating while change in view model being made from ajax - ajax

How much I know about knockout Js is that a computed gets updated anyhow depending on the viewmodel, but in my case its not happening. So basically I have a radio button which turns off and on and changes the date in the database, and the ajax calls returns and pushes the new date in the viewmodel so that the data changes.
So thats the summary. But the thing I want is that while the radio button is being updated I want a part of the html to change to active or disabled based on the radio button.
Firstly here is the HTML code.
<div class="col-sm-4">
<p>
<span data-bind="text : $data.basketStatusValue"></span>
</p>
</div>
<div class="col-sm-4">
<div class="on_off">
<input type="checkbox" data-bind="bootstrapSwitchOn: {
tocall: $root.changeActiveBasketStatus
}" />
</div>
</div>
Here is the JS code.
function MoneyInvestedViewModel(root /* root not needed */, money) {
var self = this;
self.ID = money.ID;
self.ORIG_ID = money.ORIG_ID;
self.Available = money.Available;
self.basketStatusValue = ko.computed (function () {
if (self.Available == '9999-12-01') {
return "Active";
} else {
return "Disabled";
}
});
};
And next is the code which is updating the view model moneyInvested . So the checkbox can show on or off.
self.changeActiveBasketStatus = function (bindingContext) {
console.log(bindingContext);
var Id = bindingContext.$data.ORIG_ID;
var Available = bindingContext.$data.Available;
if (Available == '9999-12-01') {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 1,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
})
.always(function (data){
});
} else {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 0,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
})
.always(function (data) {
});
}
};
So basically the PROBLEM is that when all this update is done, the computed self.basketStatusValue does not get updated. So when I click the checkbox on, it doesnt show Active, or off for disabled, the checkbox is working perfectly, only the html $data.basketStatusValue is not updating through the computed function.
Just incase if necessary here is the code for the checkbox.
(function ($) {
ko.bindingHandlers.bootstrapSwitchOn = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor());
var tocall = ko.utils.unwrapObservable(options.tocall);
$elem = $(element);
$(element).bootstrapSwitch();
$(element).bootstrapSwitch('setState', bindingContext.$data.Available === '9999-12-01' ? true : false); // Set intial state
$elem.on('switch-change', function (e, data) {
tocall(bindingContext);
// valueAccessor()(data.value);
});
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
}
};
})(jQuery);
To summarize, all I want to do is have the $data.basketStatusValue have "active" or "disabled" when the checkbox is on or off.

A dirty trick you can use to pull this off is empty the whole observable and push it with the new data. But honestly its not the right way to use it. I am assuming right now thats its a array, but you can remove observables too. Just put the observable name instead of YourArray().
self.refresh = function(){
var data = YourArray().slice(0);
YourArray.removeAll();
self.YourArray(data);
};
And place this function right after the done function
.done(function(newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
// here self.refresh();
})

Your binding handler is wrong, let's start with that.
It should:
Set up Bootstrap switch on the element in init()
React to change in update()
Bind to an observabe (don't use a callback function). In our case the observable should contain the checkbox state, i.e. true or false.
Properly dispose of the Bootstrap widget when the time comes.
So, this would work better:
(function ($) {
ko.bindingHandlers.bootstrapSwitch = {
init: function (element, valueAccessor) {
var options = valueAccessor();
// set up bootstrap in init()
$(element).bootstrapSwitch().on('switch-change', function (e, data) {
options.value(data.value);
});
// see http://knockoutjs.com/documentation/custom-bindings-disposal.html
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).bootstrapSwitch("destroy");
});
},
update: function(element, valueAccessor) {
var options = valueAccessor();
// react to change in update()
$(element).bootstrapSwitch('setState', options.value());
}
};
})(jQuery);
Next we need to set up the viewmodel accordingly.
Available needs to be observable. View changes depend on it.
we need an observable that returns true or false, depending on Available
we need an observable that returns "Active" or "Disabled", depending on that
we need a function that updates the server on change (through a subscription)
like this:
function MoneyInvestedViewModel(money) {
var self = this;
self.ID = money.ID;
self.ORIG_ID = money.ORIG_ID;
self.Available = ko.observable(money.Available);
self.errorMessage = ko.observable();
self.BasketStatus = ko.computed(function () {
return self.Available() == '9999-12-01';
});
self.BasketStatusText = ko.computed(function () {
return self.basketStatus() ? "Active" : "Disabled";
});
// BEWARE: this is not actually correct (cicular dependency)
self.BasketStatus.subscribe(function () {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + self.ID + "/" + 1,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
self.Available(newAvailableDate);
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
});
};
}
Note: Subscribe to the correct observable to update the server with the proper value. It was not clear from your question what value the server update depends on.
And now it's straightforward to bind a view to that:
<div class="col-sm-4">
<p><span data-bind="text: BasketStatusText"></span></p>
</div>
<div class="col-sm-4">
<div class="on_off">
<input type="checkbox" data-bind="bootstrapSwitch: {
value: BasketStatus
}" />
</div>
</div>

if(self.Available == '9999-12-01'){ return "Active"; }else{ return "Disabled";}
The trouble with this line is that it does not look up the value of any observable or computed, and thus it does not cause the computed to ever be updated.
You need to make self.Available an observable and then do self.Available()
This is how computeds work, they are recomputed when any of their observable/computed dependencies change. Knockout will not see a simple property update like you are doing.

Related

how to load a partial view inside an anchor tag which has been generated via Ajax

I have a form with a dropdownlist. When selecting an option, I make an ajax call to dynamically add a list of links in the view. When I click on one of the links, I want to update the existing page with a partial view returned by the PostListSubCategory() method.
Currently, clicking on one of the links does a redirect and shows the partial view in a new page. How can I update the the existing page?
<script language="javascript" type="text/javascript">
function GetSubCategory(_categoryId) {
var procemessage = "<a='0'> Please wait...</a>";
$("#SubCategoryID").html(procemessage).show();
var url = "/Posts/GetSubCategoryById/";
$.ajax({
url: url,
data: { categoryid: _categoryId },
cache: false,
type: "POST",
success: function (data) {
var markup = "";
for (var x = 0; x < data.length; x++) {
var num = data[x].Text;
markup += "<a href='/posts/postlistsubcategory?subcategoryid=" + data[x].Text + "'>" + data[x].Text + "</a><br />";
// markup += "<a href=" + Url.Action("postlistsubcategory", new { subcategoryid = num });
}
$("#SubCategoryID").html(markup).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
$.ajax({
url: "/Posts/PostListCategory",
data: { categoryid: _categoryId },
cache: false,
type: "POST",
success: function (data) {
$("#postList").html(data).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
}
</script>
#using (Html.BeginForm())
{
#Html.ListBoxFor(m => m.CategoryModel, new SelectList(Model.CategoryModel, "CategoryId", "Name"), new { #id = "ddlcategory", #style = "width:200px;", #onchange = "javascript:GetSubCategory(this.value);" })
<br />
<br />
<div id="SubCategoryID" name="SubCategoryID" style="width: 200px"></div>
<br /><br />
}
In the controller
public PartialViewResult PostListSubCategory(string subcategoryid)
{
if (subcategoryid == null)
{
return PartialView(db.Posts.ToList());
}
return PartialView("PostList", db.Posts.Include(i => i.SubCategory).Where(p => p.SubCategory.Name == subcategoryid));
}
You currently dyamically generating links with an href attribute so clicking on them will do a redirect. You need to handle the click event of those links using event delegation and then use ajax to update the existing DOM. There a some other bad practices in your code and I suggest you use the following
#using (Html.BeginForm())
{
// no need to override the id attribute and use Unobtrusive Javascript (don't pollute markup with behavior)
#Html.ListBoxFor(m => m.CategoryModel, new SelectList(Model.CategoryModel,"CategoryId", "Name"))
}
<div id="SubCategoryID"></div> // no point adding a name attribute
<div id="postList"></div>
var subcategories = $('#SubCategoryID');
$('#CategoryModel').change(function() {
var url = '#Url.Action("GetSubCategoryById", "Posts")'; // don't hard code url's
var category = $(this).val();
subcategories.empty(); // clear any existing links
$.post(url, { categoryid: category }, function(data) { // this could be a GET?
$.each(data, function(index, item) {
subcategories.append($('<a></a>').text(item).attr('href','#').addClass('subcategory')); // see note below
});
});
});
Note: Since your ajax only needs one property to generate the links (the value to display in the link), then your GetSubCategoryById() should be returning IEnumerable<string> not a collection of complex objects (you current code suggest your returning other data which you never use). If you do need to return a collection of objects, then change the above to use .text(item.Text). The above code will generate
.....
for each item you return. Then add an additional script to handle the .click() event of the links (since the links are dynamically added, you need event delegation using the .on() method)
var posts = $('#postList');
$('#SubCategoryID').on('click', '.subcategory', function() {
var url = '#Url.Action("postlistsubcategory", "posts")';
var subcategory = $(this).text();
posts.load(url, { subcategoryid: subcategory });
});

knockoutjs data bind hidden field value

I'm having a hidden field in a knockout template that its value gets updated with jquery. The problem is when trying to pass this value to the server with ajax, I get null value in the controller. But the html source code shows that the value of the hidden field is updated. If I replaced the hidden field with a textbox, it would work fine only when I enter text manually.
jQuery
function getFileDetail(fileID, fileName) {
$('#hdnFileName' + fileID).val(fileName);
$('#lblFileName' + fileID).text(fileName);
}
Here is the html knockout template:
<script type="text/html" id="fileTemplate">
<div data-role="fieldcontain">
<label data-bind="text: 'File Upload ' + ID, attr: { id: 'lblFileName' + ID }"></label><input type="button" value="Remove" data-bind="click: removeFile" />
</div>
<input type="hidden" name="hdnFileName" data-bind="attr: { id: 'hdnFileName' + ID, value: fileName }" />
</script>
ViewModel
function FileViewModel() {
var self = this;
self.ID = ko.observable();
self.fileName = ko.observable();
self.removeFile = function (file) { };
self.Files = ko.observableArray([{ ID: 1, fileName: "", removeFile: function (file) { self.Files.remove(file); }}]);
self.addNewFile = function () {
var newFile = new FileViewModel();
newFile.ID = self.Files().length + 1;
newFile.fileName = "";
newFile.removeFile = function (file) { self.Files.remove(file); };
self.Files.push(newFile);
//$("input[name='hdnFileName'").trigger("change");
}
}
function ViewModel() {
var self = this;
self.fileViewModel = new FileViewModel();
self.submitForm = function () {
$.ajax({
type: "POST",
url: "<%= Url.Action("MeetingPresenter")%>",
data: "{Files:" + ko.utils.stringifyJson(self.fileViewModel.Files) + "}",
contentType: "application/json",
success: function (data) {},
});
};
}
Your model property ID is an observable, so you need to 'unwrap' to get the value from it when you are concatenating, like this:
<input type="hidden" name="hdnFileName" data-bind="attr: { id: 'hdnFileName' + ID(), value: fileName }" />
and this:
<label data-bind="text: 'File Upload ' + ID(), attr: { id: 'lblFileName' + ID() }"></label>
If you are using knockout.js you don't neede to modify the DOM, you can just update the ViewModel and the DOM will be updated according
function getFileDetail(fileID, fileName) {
viewModel.fileViewModel.update(fileID, fileName);
}
Add the update function in FileViewModel
function FileViewModel() {
// rest of the code
self.update = function(fileID, fileName) {
var file = ko.utils.arrayFirst(self.Files(), function(file) {
return file.ID == fileID;
});
file.fileName(fileName); // this will change and the UI will be updated according
};
}
Note: Please notice that you have a default item in Files that will not be changed with update function because properties are not observable
self.Files = ko.observableArray([{ ID: 1, fileName: "", removeFile: function (file) { self.Files.remove(file); }}]);
You can solve this by making them observable (i.e. ID: observable(1)) or you can create a new FileViewModel().
Note: The viewModel must be accesible in the function (i.e. global instance), otherwise will be undefined.
It looks to me that setting a field's value via the DOM does not interact with knockout. If you are setting its value using .value, the observable will not be updated. You should be updating the observable.
I wrote a little Fiddle to demonstrate. Every 2 seconds, it sets the input's value via the DOM, but the bound observable only changes when you type something.
http://jsfiddle.net/qcv01h2e/
var viewModel = (function () {
return {
fv: ko.observable().extend({notify:'always'})
};
}());
ko.applyBindings(viewModel);
setInterval(function () {
console.debug("Set it");
var f = document.getElementById('field');
f.value = "Hi";
console.debug("fv is", viewModel.fv());
}, 2000);
I came across a similar issue where I need to set a value without user input.
Before doing the click update function I do the required model update. If you have mode operations better to introduce a function in the model.
<input data-bind="click: function(){ isEnabled(true); update() }" />
What I actually did was,
<input data-bind="click: function(){ isEnabled(!isEnabled()); update() }" />
Keep in mind that asynchronous nature of javascript.

why can't my typo3 6.2 ajax action be found?

I am trying to hit an ajax action "ajaxfactsAction()" in my controller Factoids. But when the ajax call is made I get a 303 error and a redirect to another page.
This is what I have in my template that calls the ajax:
<f:section name="main">
<h1>I am</h1>
<f:form action="">
<f:form.select id="topics" name="topics" options="{categories}" optionValueField="uid" optionLabelField="title" additionalAttributes="{onchange: 'getFacts()'}"/>
<f:form.hidden id="extraids" name="extraids" value="3,4,5" />
<f:form.hidden id="number" name="number" value="3" />
</f:form>
<div>
<div class="factoid1"><f:render partial="Category/Factoid1" /></div>
<div class="factoid2"><f:render partial="Category/Factoid2" /></div>
<div class="factoid3"><f:render partial="Category/Factoid3" /></div>
</div>
<f:flashMessages renderMode="div" />
<script type="text/javascript">
var actionsPathFromViewHelperSetInTheView = '<f:uri.action action="ajaxfacts" controller="Factoid" />';
</script>
</f:section>
and this is the javascript:
function performAjaxCall(Topics, Extraids, Number) {
alert("dddd");
$.ajax({
url: actionsPathFromViewHelperSetInTheView,
data:{
"tx_factoids_interests[uid]":Topics,
"tx_factoids_interests[extraids]":Extraids,
"tx_factoids_interests[number]":Number
},
success:function (data) {
// do something with your json
alert('Load was performed.');
}
});
}
function getFacts(){
performAjaxCall($("#topics").val(), $("#extraids").val(), $("#number").val());
}
and this is my action function:
/**
* action ajaxfacts
*
* #return void
*/
public function ajaxfactsAction() {
echo __LINE__;
die;
}
and my plugin in ext_localconf:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Seethroughweb.' . $_EXTKEY,
'Interests',
array(
'Factoid' => 'interests, list, show, new, edit, create, update, ajaxfacts',
),
// non-cacheable actions
array(
'Factoid' => 'interests, list, show, new, edit, create, update, ajaxfacts',
)
);
What am I missing to make the action accessible?
The resulting uri looks like this :
xhttp://...xyz.com/index.php?id=111&tx_factoids_interests%5Baction%5D=ajaxfacts&tx_factoids_interests%5Bcontroller%5D=Factoid&cHash=0e805af8af888ebd7fec5e207f64b5f7&tx_factoids_interests%5Buid%5D=5&tx_factoids_interests%5Bextraids%5D=3%2C4%2C5&tx_factoids_interests%5Bnumber%5D=3
the page is is called from is accessible with the first part ie :
xhttp://....xyz.com/index.php?id=111
I am using Typo3 6.2,
Thanks.
PS: javascript version two - made to work like the Arek's answer.
function performAjaxCall(Topics, Extraids, Number) {
alert("performAjaxCall");
$.ajax({
url: $('form').attr('action'), // now we're using the url generated by Extbase/Fluid
data: $('form').serialize(), // serializes the form
// data:{
// "tx_factoids_interests[uid]":Topics,
// "tx_factoids_interests[extraids]":Extraids,
// "tx_factoids_interests[number]":Number
// },
success:function (data) {
// do something with your json
alert('performAjaxCall Load was performed.');
}
});
return false;
}
PPS: the request uri from this method now looks like this:
http://...xyz.com/undefinedindex.php?id=111&tx_factoids_interests%5Baction%5D=ajaxfacts&tx_factoids_interests%5Bcontroller%5D=Factoid&cHash=0e805af8af888ebd7fec5e207f64b5f7
and current javascript:
function performAjaxCall( Topics, Extraids, Divid) {
//alert("performAjaxCall");
$.ajax({
type: "POST",
dataType: "json",
url: $('base').attr('href') + $('#form1').attr('action'), // now we're using the url generated by Extbase/Fluid
data: $('#form1').serialize(),
// url: actionsPathFromViewHelperSetInTheView,
// data:{
// "tx_factoids_interests[uid]":Topics,
// "tx_factoids_interests[extraids]":Extraids
// },
success:function (data) {
// do something with your json
$.each( data, function( key, val ) {
var items='';
var id = '';
$.each( val, function( ikey, ival ) {
if(ikey =='category') id = Divid +" #factoid"+ival;
items += "<span class="+ikey+">" + ival + "</span><br/>" ;
});
$(id).html(items);
});
// $(".factoid1").html();
// $(".factoid2").html();
// $(".factoid3").html();
//alert('performAjaxCall Load was performed.');
}
});
}
function getFacts(Divid){
performAjaxCall( $(Divid+"topics").val(), $(Divid+"extraids").val(), Divid );
return false;
}
and the current template:
<div id="interests">
<f:form action="ajaxfacts" controller="Factoid" id="form1">
<f:form.select id="intereststopics" name="topics" options="{categories}" optionValueField="uid" optionLabelField="content" additionalAttributes="{onchange: 'getFacts(\'#interests\')'}"/>
<f:form.hidden id="interestsextraids" name="extraids" value="2,4,5" />
</f:form>
<div>
<div id="factoid2" class="factoid1"></div>
<div id="factoid4" class="factoid2"></div>
<div id="factoid5" class="factoid3"></div>
</div>
</div>
PPPS: final code
function performAjaxCall( Topics, Extraids, Divid, Formid) {
$.ajax({
type: "POST",
dataType: "json",
url: $(Formid).attr('action'), // now we're using the url generated by Extbase/Fluid
data: $(Formid).serialize(),
success:function (data) {
$.each( data, function( key, val ) {
var items='';
var id = '';
$.each( val, function( ikey, ival ) {
if(ikey =='category') id = Divid +" #factoid"+ival;
$(id +" ."+ikey).html(ival);
});
});
}
});
}
function getFacts(Divid, Formid){
performAjaxCall( $(Divid+"topics").val(), $(Divid+"extraids").val(), Divid, Formid );
return false;
}
You didn't set the <f:form action="">.
The calculated cHash is in your case based on the URL generated by <f:uri> which does not contain any information about the other properties you added in your JavaScript. Hereby you're running into the cHash error.
You can prevent that the pageNotFoundHandler is called on a cHash error by disabling $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError'] (Local Configuration).
For more information abount the cHash:
The mysteries of &cHash
The &cHash parameter of frontend plugins might have puzzled quite a few developers but this article will explain how it works and what to avoid in order to make great, reliable plugins with TYPO3.
Solution
Instead of making a custom url you need to use the action from the form generated by Extbase/Fluid.
So your form should look like:
<f:form action="ajaxfacts" controller="Factoid">
And your JavaScript should look like:
$(function() {
$('form').submit(function(e) {
$.ajax({
type: "POST",
url: $('base').attr('href') + $('form').attr('action'), // now we're using the url generated by Extbase/Fluid
data: $('form').serialize(), // serializes the form
success:function (data) {
// do something with your json
alert('Load was performed.');
}
});
return false;
});
});
PPPS Final js code:
function performAjaxCall( Topics, Extraids, Divid, Formid) {
$.ajax({
type: "POST",
dataType: "json",
url: $(Formid).attr('action'), // now we're using the url generated by Extbase/Fluid
data: $(Formid).serialize(),
success:function (data) {
$.each( data, function( key, val ) {
var items='';
var id = '';
$.each( val, function( ikey, ival ) {
if(ikey =='category') id = Divid +" #factoid"+ival;
$(id +" ."+ikey).html(ival);
//items += "<span class="+ikey+">" + ival + "</span><br/>" ;
});
});
}
});
}
function getFacts(Divid, Formid){
performAjaxCall( $(Divid+"topics").val(), $(Divid+"extraids").val(), Divid, Formid );
return false;
}

Polling jQuery ajax for each. Check if element does not exist, if so prepend to div

I have a div #notification-data which on $(document).ready gets populated with multiple <li></li> from $.post.
The $.post then gets called setTimeout(poll_n,9000); so data is up-to-date.
So im not updating all the data every time, I would like to do check if the <li></li> already exists in #notification-data, if it does not exist then I would like to prepend() it to #notification-data.
The data comes in the form of:
<li id="new_notification_1" class="seen_0 li_notification">blah</li>
<li id="new_notification_2" class="seen_0 li_notification">bleh</li>
As an extra question, is this the correct way of long polling?
Here is my code:
function poll_n(){
$.post('<?php echo $siteUrl ?>notifications.php?x=' + (new Date()).getTime() +'', function(data) {
$(data).find(".li_notification").each(function () {
var li_id = $(this).attr('id');
if ($(li_id).closest('#notification-data').length) {
//do nothing
} else {
$('#notification-data').append(??not_sure_what_goes_here??); // process results here
}
});
setTimeout(poll_n,9000);
});
}
EDIT - After answer I have now got this but it does not work (I get nothing in the console).
success: function(data){
$(data).find(".li_notification").each(function() {
var id = $(this).attr('id'),
notification = $('#notification-data');
console.log(id);
console.log('hello');
if (notification.find('#' + id).length === 0) {
// notification doesn't exists yet then lets prepend it
notification.prepend('#' + id);
}
});
},
You can try this:
function poll_n() {
$.ajax({
type: 'POST',
url: 'your url',
success: function(data){
var notification = $('#notification-data');
$.each($(data), function(){
var id = this.id;
if (notification.find('#' + id).length === 0) {
// notification doesn't exists yet then lets prepend it
notification.prepend(this);
}
});
},
complete: function(jqXHR, status) {
if (status === 'success') {
setTimeout(poll_n, 9000);
}
}
});
}
You must call poll_n() again after the request has been completed.

$.submit form and replace div using ajax has strange jquery behaviour on the new partialview

I think the problem is with jQuery, i don't know for sure.
Let me explain the situation.
Screenshot 1
I fill in the partialView and click on submit.
The submit is a jQuery event handler with the following code:
_CreateOrEdit.cshtml
<script type="text/javascript">
$(document).ready(function () {
$('input[type=text], input[type=password], input[type=url], input[type=email], input[type=number], textarea', '.form').iTextClear();
$("input:checkbox,input:radio,select,input:file").uniform();
$("input[type=date]").dateinput();
});
$(window).bind('drilldown', function () {
$(".tabs > ul").tabs("section > section");
});
$("#CreateOrEditSubmit").submit(function () {
//get the form
var f = $("#CreateOrEditSubmit");
//get the action
var action = f.attr("action");
//get the serialized data
var serializedForm = f.serialize();
$.post(action, serializedForm, function (data) {
$("#main-content").html(data);
});
return false;
});
</script>
This all works fine on the first-run.
Then when i submit the form when it is invalid (Screenshot 1),
[HttpPost]
public ActionResult Create(Client client)
{
if (ModelState.IsValid)
{
context.Clients.Add(client);
context.SaveChanges();
return RedirectToAction("Index");
}
return PartialView(client);
}
Then it tries to redisplay the same form again (Controller Client, Action Create), but something isn't triggered right (Screenshot 2). The layout is wrong (buttons still hidden), the tabs aren't working (javascript), ...
Worst of all, i don't get any error in Firebug, Chrome Console, ...
Does anyone have an idea what could be the problem, because i really haven't got a clue what's happening. It seems to me that nothing has changed, but it did :s
Fyi, an equivalant for the post function is :
var request = $.ajax({
type: 'POST',
url: action,
data: serializedForm,
success: function (data) {
$("#main-content").html(data);
},
dataType: 'HTML'
});
request.done(function (msg) {
$("#log").html(msg);
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
Before submit, everything loads fine
After submit, same form is called. jQuery isn't working anymore and form is getting bricked (i think this is "side" behaviour from the jQuery breaking)
Edit: (on request)
Here is the partialView in full
_CreateOrEdit.cshtml doesn't contain any javascript for now, the result is the same, so i only posted Create.cshtml.
Create.shtml
#model BillingSoftwareOnline.Domain.Entities.Client
<div class="container_12 clearfix leading">
<div class="grid_12">
#using (Html.BeginForm("Create", "Client", FormMethod.Post, new { #class="form has-validation", id="CreateOrEditSubmit"}))
{
#Html.Partial("_CreateOrEdit", Model)
<div class="form-action clearfix">
<button class="button" type="submit">
OK</button>
<button class="button" type="reset">
Reset</button>
</div>
}
</div>
</div>
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.min.js")"></script>
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.itextclear.js")"> </script>
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.uniform.min.js")"></script>
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.tools.min.js")"> </script>
<script type="text/javascript">
$(document).ready(function () {
$('input[type=text], input[type=password], input[type=url], input[type=email], input[type=number], textarea', '.form').iTextClear();
$("input:checkbox,input:radio,select,input:file").uniform();
$("input[type=date]").dateinput();
});
$(window).bind('drilldown', function () {
$(".tabs > ul").tabs("section > section");
});
$("#CreateOrEditSubmit").submit(function () {
//get the form
var f = $("#CreateOrEditSubmit");
//get the action
var action = f.attr("action");
//get the serialized data
var serializedForm = f.serialize();
// $.post(action, serializedForm, function (data) {
// $("#main-content").html(data);
// });
var request = $.ajax({
type: 'POST',
url: action,
data: serializedForm,
success: function (data) {
$("#main-content").html(data);
},
dataType: 'HTML'
});
return false;
request.done(function (msg) {
alert(msg);
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
});
</script>
Since this markup is returned as a partial, you need to reinitialize your javascript.
This is hacky, but try putting your script in the partial view, instead of _CreateOrEdit.cshtml, and see if that works.
Update
After seeing the cshtml, it looks like it is not working because $(document).ready() has already executed, before the ajax load. Try this instead:
$(function () {
$('input[type=text], input[type=password], input[type=url], input[type=email], input[type=number], textarea', '.form').iTextClear();
$("input:checkbox,input:radio,select,input:file").uniform();
$("input[type=date]").dateinput();
$(window).bind('drilldown', function () {
$(".tabs > ul").tabs("section > section");
});
$("#CreateOrEditSubmit").submit(function () {
//get the form
var f = $("#CreateOrEditSubmit");
//get the action
var action = f.attr("action");
//get the serialized data
var serializedForm = f.serialize();
// $.post(action, serializedForm, function (data) {
// $("#main-content").html(data);
// });
var request = $.ajax({
type: 'POST',
url: action,
data: serializedForm,
success: function (data) {
$("#main-content").html(data);
},
dataType: 'HTML'
});
return false;
request.done(function (msg) {
alert(msg);
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
});
});
Add the following instructions to the end of your ajax callback, so that the styling is applied after the form has been injected to the DOM:
$('input[type=text], input[type=password], input[type=url], input[type=email], input[type=number], textarea', '.form').iTextClear();
$("input:checkbox,input:radio,select,input:file").uniform();
$("input[type=date]").dateinput();
$(".tabs > ul").tabs("section > section");

Resources