binding new DOM elements to viewmodel after AJAX call - ajax

I'm having troubles binding new DOM elements to my viewmodel. This elements are in a partial view loaded using an AJAX call (see the customizeQuote function below).
$(function () {
var mvcModel = ko.mapping.fromJS(initialData);
function QuoteViewModel() {
var self = this;
self.customizeQuote = function (quote) {
self.selectedQuote = quote;
//remove the disable attribute on all form controls before serializing data
$(".step").each(function () {
$(this).find('input, select').removeAttr('disabled');
});
//convert form data to an object
var formData = $('#etape').toObject();
$.ajax("getSelectedQuote", {
data: ko.toJSON({ model: self.selectedQuote, model1: formData }),
type: "post", contentType: "application/json",
success: function (result) {
$("#custom").html(result);
$("#etape").formwizard("show", "customize");
ko.applyBindings(self.selectedQuote, $("#covers"));
}
});
}
}
var myViewModel = new QuoteViewModel();
var g = ko.mapping.fromJS(myViewModel, mvcModel);
ko.applyBindings(g);
});
Here's the partial view html:
#model QuoteViewModel
<table id="covers">
<thead>
<tr>
<th>
ProductName
</th>
</tr>
</thead>
<tbody data-bind="foreach: CoverQuotesViewModel">
<tr>
<td>
<input data-bind="value: ProductName" />
</td>
</tr>
</tbody>
</table>
the line:
ko.applyBindings(self.selectedQuote, $("#covers"));
triggers an error:
"ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node"
I'm fairly new to knockout and I don't see what I'm doing wrong. Any idea ?

$("#covers") is not a DOM node though, it is an jQuery object. Perhaps try using this instead:
ko.applyBindings(self.selectedQuote, $("#covers")[0]);
The [0] will get the first matched element of the selector in the jquery object.

Related

Cannot read property replace of null method parameter

I started messing around with Vue.js earlier this week.
So far I created a list of MTG(a TCG) cards. The data comes from the database through an Ajax request. This all works like a charm.
What i want to do next is replace the string that contains the costs of a card e.g. something like '{1}{U}{G}' with images for the corresponding tag.
HTML:
<div v-for="(cards, key) in mainBoard" class="">
<table class="table">
<tr>
<th colspan="5">#{{ key }}</th>
</tr>
<tr>
<th>#</th>
<th>Name</th>
<th>ManaCost</th>
#if($deck->enableCommander())
<th>Commander</th>
#else
<th></th>
#endif
<th>Actions</th>
</tr>
<tr v-for="card in cards">
<td>#{{card.pivot.quantity}}</td>
<td>#{{card.name}}</td>s
<td v-html="replaceManaSymbols(card)"></td>
#if($deck->enableCommander())
<td>
<span v-if="card.pivot.commander" #click="preformMethod(card, 'removeCommander', $event)"><i class="fa fa-flag"></i></span>
<span v-else #click="preformMethod(card,'assignCommander', $event)"><i class="far fa-flag"></i></span>
</td>
#else
<td> </td>
#endif
<td>
<button #click="preformMethod(card,'removeCardFromDeck', $event)"><i class="fa fa-times-circle"></i></button>
<button #click="preformMethod(card,'plusCardInDeck', $event)"><i class="fa fa-plus-circle"></i></button>
<button #click="preformMethod(card,'minusCardInDeck', $event)"><i class="fa fa-minus-circle"></i></button>
</td>
</tr>
</table>
</div>
Vue.js
new Vue({
el: '#Itemlist',
data: {
mainBoard: [],
sideBoard: [],
},
methods:{
preformMethod(card, url){
var self = this;
var varData = {
slug: '{{ $deck->slug }}',
card: card.id,
board: card.pivot.mainboard
};
$.ajax({
url: '/vue/'+url,
data: varData,
method: 'GET',
success: function (data) {
self.mainBoard = data.mainBoard;
self.sideBoard = data.sideBoard;
},
error: function (error) {
console.log(error);
}
});
},
replaceManaSymbols(card){
var mc = card.manaCost;
var dump = mc.replace(/([}])/g, '},').split(',');
var html = '';
/**
* replace each tag with an image
*/
return html;
}
},
mounted(){
var self = this;
var varData = {
slug: '{{ $deck->slug }}'
};
$.ajax({
url: '/vue/getDeckList',
data: varData,
method: 'GET',
success: function (data) {
self.mainBoard = data.mainBoard;
self.sideBoard = data.sideBoard;
},
error: function (error) {
console.log(error);
}
});
}
})
I pass the card as a parameter to the replaceManaSymbols method. I can console.log the contents of mana without any issue. But as soon as a want to modify the string Vue throws the error TypeError: Cannot read property 'toLowerCase/split/replace' of null. I'm not really sure what's going wrong. Any idea's?
As a rule of thumb, you shouldn't use methods on the display side. Keep them for the update side - processing changes back into a store and such. Methods aren't reactive - they need to be called. You want your display to automatically reflect some underlying data, so you should use computed.
You should also avoid using v-html, because you end up with markup outside your templates, and that markup is static. It's not totally clear to me what will be in v-html, but you should try and keep markup in your template, and inject values using data, props or computed. Hope this helps!
If you want a div with some text that magically turns into an image tag, this could be a good use for <component :is>.

Populate Multi-select list from Ajax in Knockout

I have an editable table based off of the Knockoutjs example. I've been trying to populate a multi-select dropdown list from an AJAX call to ASP MVC controller. I've looked through this example, this one, and this one but am still having issues getting it to work.
Here's the html:
<tbody data-bind='foreach: users' class="table-striped">
<tr>
<td><input class='required number form-control' maxlength="9" stringlength="9" data-bind='value: id, uniqueName: true' /></td>
<td><input class='required form-control' data-bind='value: firstName' /></td>
<td><input class='required form-control' data-bind='value: lastName' /></td>
<td>
<select data-bind="options: MAMUserGroupsListOptions, selectedOptions: MAMSelectedGroup">
<!--dropdown list goes here-->
</select>
</td>
<td><a href='#' data-bind='click: $root.removeUser'>Delete</a></td>
</tr>
</tbody>
Here's the javascript for the knockout portion:
var UserModel = function (users) {
var self = this;
self.users = ko.observableArray(users);
//add user
self.addUser = function () {
self.users.push({
id: "",
firstName: "",
lastName: "",
MAMUserGroupsListOptions: "",
MAMUserGroups: ""
});
};
//remove user
self.removeUser = function (user) {
self.users.remove(user);
};
var viewModel = new UserModel([
{
id: ko.observable(""), firstName: ko.observable(""), lastName: ko.observable(""),
MAMUserGroupsListOptions: ko.observable(),//this is where I need to populate
MAMSelectedGroups: ko.observable(),//this is the result of the multiselect
}
]);
ko.applyBindings(viewModel);
Here's the controller:
[HttpGet]
public ActionResult MAMUserGroupsList()
{
var MAMUserGroupsListOptions = db.MAMUserGroupsListModels.Select(x => new {
MAMUserGroupName = x.MAMUserGroupName
}).ToList();
return Json(MAMUserGroupsListOptions, JsonRequestBehavior.AllowGet);
}
When the controller is called via AJAX, this is what returns:
{MAMUserGroupName: "MAMGroup1"}, {MAMUserGroupName: "MAMGroup2"}, etc...
I'm trying to get the multi-select dropdown to have "MAMGroup1", MAMGroup2, etc.
I've been banging my head against the wall on this- any help would be much appreciated.
Looks like you just want to map the array of objects to extract the one value of interest. If you start with your result in a variable named ajaxResult,
MAMUserGroupsListOptions(ajaxResult.map(obj => obj.MAMUserGroupName));
will assign the values to your observable array. Or if the value is available to you when you're creating your viewmodel,
MAMUserGroupsListOptions: ko.observable(ajaxResult.map(obj => obj.MAMUserGroupName))

AJAX Post to Update List in View

I have a List in my model. The view is bound to the model. I am trying to populate the List through ajax. And then I have a 'submit' button on the form which should submit the entire model. While submitting the entire model, the list value is 'null'??
I am getting the response data from the controller through Json. The response data is not bound to the model.
<table id="tblTable" style="border-width:1px;border-style:solid" >
<tr style="border-width:1px;border-style:solid">
<th>
#Html.Label("Col1")
</th>
<th>
#Html.Label("Col2")
</th>
</tr>
<tbody>
#if (Model != null && Model.Operations!= null && Model.Operations.Count > 0)
{
foreach (var item in Model.Operations)
{
<tr style="border-width:1px;border-style:solid">
<td style="border-width:1px;border-style:solid">
#Html.DisplayFor(modelItem => item.Col1)
</td>
<td style="border-width:1px;border-style:solid">
#Html.DisplayFor(modelItem => item.Col2)
</td>
</tr>
}
}
</tbody>
</table>
My controller that responds to teh ajax request:
[HttpPost]
public JsonResult UploadFile(HttpPostedFileBase file, Model model)
{
//extract file contents into the operations list
model.Operations = new List<Operations>();
model.Operations.Add(blah..blah)
return Json(model);
}
Issue is, I have to populate the list via ajax, but post the complete model along with the ajax-retrieved data.
Update: JS CODE:
$(document).ready(function () {
$('#fileupload').fileupload({
dataType: 'json',
url: '/Summary/UploadFile',
autoUpload: true,
done: function (e, data) {
var operations = data.result.Operations;
var tbl = $('#tblTable');
$.each(operations, function (key, val) {
$('<tr><td>' + val.col1 + '</td>' + '<td>' +
val.col2 + '</td><tr>').appendTo(tbl);
});
}
}).on('fileuploadprogressall', function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('.progress .progress-bar').css('width', progress + '%');
});
});

ASP.NET MVC3: Ajax postback doesnot post complete data from the view

Hi Greetings for the day!
(1) The view model (MyViewModel.cs) which is bound to the view is as below...
public class MyViewModel
{
public int ParentId { get; set; } //property1
List<Item> ItemList {get; set;} //property2
public MyViewModel() //Constructor
{
ItemList=new List<Item>(); //creating an empty list of items
}
}
(2) I am calling an action method through ajax postback (from MyView.cshtml view) as below..
function AddItem() {
var form = $('#MyForm');
var serializedform = form.serialize();
$.ajax({
type: 'POST',
url: '#Url.Content("~/MyArea/MyController/AddItem")',
cache: false,
data: serializedform,
success: function (html) {$('#MyForm').html(html);}
});
}
The below button click will call the ajax postback...
<input type="button" value="Add" class="Previousbtn" onclick="AddItem()" />
(3) I have an action method in the (MyController.cs controller) as below...
public ActionResult AddItem(MyViewModel ViewModel)
{
ViewModel.ItemList.Add(new Item());
return View("MyView", ViewModel);
}
Now the issue is, after returning from the action, there is no data in the viewmodel. But i am able to get the data on third postback !! Can you pls suggest the solution..
The complete form code in the view is below...
#model MyViewModel
<script type="text/javascript" language="javascript">
function AddItem() {
var form = $('#MyForm');
var serializedform = form.serialize();
$.ajax({
type: 'POST',
url: '#Url.Content("~/MyArea/MyController/AddItem")',
cache: false,
data: serializedform,
success: function (html) {
$('#MyForm').html(html);
}
});
}
function RemoveItem() {
var form = $('#MyForm');
var serializedform = form.serialize();
$.ajax({
type: 'POST',
url: '#Url.Content("~/MyArea/MyController/RemoveItem")',
cache: false,
data: serializedform,
success: function (html) {
$('#MyForm').html(html);
}
});
}
function SaveItems() {
var form = $('#MyForm');
var serializedform = forModel.serialize();
$.ajax({
type: 'POST',
url: '#Url.Content("~/MyArea/MyController/SaveItems")',
cache: false,
data: serializedform,
success: function (html) {
$('#MyForm').html(html);
}
});
}
</script>
#using (Html.BeginForm("SaveItems", "MyController", FormMethod.Post, new { id = "MyForm" }))
{
#Html.HiddenFor(m => Model.ParentId)
<div>
<input type="button" value="Save" onclick="SaveItems()" />
</div>
<div>
<table>
<tr>
<td style="width: 48%;">
<div style="height: 500px; width: 100%; overflow: auto">
<table>
<thead>
<tr>
<th style="width: 80%;">
Item
</th>
<th style="width: 10%">
Select
</th>
</tr>
</thead>
#for (int i = 0; i < Model.ItemList.Count; i++)
{
#Html.HiddenFor(m => Model.ItemList[i].ItemId)
#Html.HiddenFor(m => Model.ItemList[i].ItemName)
<tr>
#if (Model.ItemList[i].ItemId > 0)
{
<td style="width: 80%; background-color:gray;">
#Html.DisplayFor(m => Model.ItemList[i].ItemName)
</td>
<td style="width: 10%; background-color:gray;">
<img src="#Url.Content("~/Images/tick.png")" alt="Added"/>
#Html.HiddenFor(m => Model.ItemList[i].IsSelected)
</td>
}
else
{
<td style="width: 80%;">
#Html.DisplayFor(m => Model.ItemList[i].ItemName)
</td>
<td style="width: 10%">
#if ((Model.ItemList[i].IsSelected != null) && (Model.ItemList[i].IsSelected != false))
{
<img src="#Url.Content("~/Images/tick.png")" alt="Added"/>
}
else
{
#Html.CheckBoxFor(m => Model.ItemList[i].IsSelected, new { #style = "cursor:pointer" })
}
</td>
}
</tr>
}
</table>
</div>
</td>
<td style="width: 4%; vertical-align: middle">
<input type="button" value="Add" onclick="AddItem()" />
<input type="button" value="Remove" onclick="RemoveItem()" />
</td>
</tr>
</table>
</div>
}
You must return PartialViewResult and then you can do something like
$.post('/controller/GetMyPartial',function(html){
$('elem').html(html);});
[HttpPost]
public PartialViewResult GetMyPartial(string id
{
return PartialView('view.cshtml',Model);
}
In my project i get state data with country id using json like this
in my view
<script type="text/javascript">
function cascadingdropdown() {
var countryID = $('#countryID').val();
$.ajax({
url: "/City/State",
dataType: 'json',
data: { countryId: countryID },
success: function (data) {
alert(data);
$("#stateID").empty();
$("#stateID").append("<option value='0'>--Select State--</option>");
$.each(data, function (index, optiondata) {
alert(optiondata.StateName);
$("#stateID").append("<option value='" + optiondata.ID + "'>" + optiondata.StateName + "</option>");
});
},
error: function () {
alert('Faild To Retrieve states.');
}
});
}
</script>
in my controller return data in json format
public JsonResult State(int countryId)
{
var stateList = CityRepository.GetList(countryId);
return Json(stateList, JsonRequestBehavior.AllowGet);
}
i think this will help you ....
I resolved the issue as below...
Issue:
The form code i have shown here is actually part of another view page
which also contains a form. So, when i saw the page source at
run-time, there are two form tags: one inside the other, and the
browser has ignored the inner form tag.
Solution:
In the parent view page, earlier i had used Html.Partial to render
this view by passing the model to it.
#using(Html.BeginForm())
{
---
---
#Html.Partial('/MyArea/Views/MyView',MyViewModel)
---
---
}
But now, i added a div with no content. On click of a button, i'm
calling an action method (through ajax postback) which then renders
the above shown view page (MyView.cshmtl) into this empty div.
#using(Html.BeginForm())
{
---
---
<div id="divMyView" style="display:none"></div>
---
---
}
That action returns a separate view which is loaded into the above
div. Since it is a separate view with its own form tag, i'm able to
send and receive data on each postback.
Thank you all for your suggestions on this :)

Passing Id from javascript to Controller in mvc3

How to pass Id from javascript to Controller action in mvc3 on ajax unobtrusive form submit
My script
<script type="text/javascript">
$(document).ready(function () {
$("#tblclick td[id]").each(function (i, elem) {
$(elem).click(function (e) {
var ID = this.id;
alert(ID);
// var url = '#Url.Action("Listpage2", "Home")';
var data = { Id: ID };
// $.post(url,data, function (result) {
// });
e.preventDefault();
$('form#myAjaxForm').submit();
});
});
});
</script>
the how to pass Id on using $('form#myAjaxForm').submit(); to controller
instead of
$.post(url,data, function (result) {
// });
My View
#using (Ajax.BeginForm("Listpage2", "", new AjaxOptions
{
UpdateTargetId = "showpage"
}, new { #id = "myAjaxForm" }))
{
<table id="tblclick">
#for (int i = 0; i < Model.names.Count; i++)
{
<tr>
<td id='#Model.names[i].Id'>
#Html.LabelFor(model => model.names[i].Name, Model.names[i].Name, new { #id = Model.names[i].Id })
<br />
</td>
</tr>
}
</table>
}
</td>
<td id="showpage">
</td>
I would avoid using the Ajax Beginform helper method and do some pure handwritten and Clean javascript like this
<table id="tblclick">
#foreach(var name in Model.names)
{
<tr>
<td id="#name.Id">
#Html.ActionLink(name.Name,"listpage","yourControllerName",
new { #id = name.Id },new { #class="ajaxShow"})
</td>
</tr>
}
</table>
<script>
$(function(){
$(".ajaxShow")click(function(e){
e.preventDefault();
$("#showpage").load($(this).attr("href"));
});
});
</script>
This will generate the markup of anchor tag in your for each loop like this.
<a href="/yourControllerName/listpage/12" class="ajaxShow" >John</a>
<a href="/yourControllerName/listpage/35" class="ajaxShow" >Mark</a>
And when user clicks on the link, it uses jQuery load function to load the response from thae listpage action method to the div with id showPage.
Assuming your listpage action method accepts an id parameter and returns something
I am not sure for $.post but I know window.location works great for me.
Use this instead and hopefully you have good results :)
window.location = "#(Url.Action("Listpage2", "Home"))" + "Id=" + ID;
replace $('form#myAjaxForm').submit(); with this code and nothing looks blatantly wrong with your jscript.
Just use a text box helper with html attribute ID.
#Html.TextBox("ID")
You can do this too:
var form = $('form#myAjaxForm');
$.ajax({
type: "post",
async: false,
url: form.attr("action"),
data: form.serialize(),
success: function (data) {
// do something if successful
}
});

Resources