Creating an observable knockout array from a JSON response - javascript

C#/MVC/Knockout/JSON
I've got the following javascript:
function Feed(data) {
this.ID = ko.observable(data.ID);
this.RSSName = ko.observable(data.RSSName);
alert(data.RSSName + " " + data.ID);
}
function ViewModel() {
self = this;
self.CurrentFeeds = ko.observableArray([]);
self.isLoading = ko.observable(false);
self.StatusMessage = ko.observable("Loading");
$.ajax({
type: "GET",
url: '#Url.Action("RSSList", "RSS")',
success: function (data) {
var feeds = $.map(data, function (item) {
alert(item.RSSName + " " + item.ID + " 1");
return new Feed(item)
});
self.CurrentFeeds(feeds);
//self.CurrentFeeds(data);
},
error: function (err) {
alert(err.status + " : " + err.statusText);
}
});
self.save = function () {
self.deleteFeed = function (feed) {
};
};
}
The JSON response (as copied from fiddler) looks like this:
{"aaData":[{"ID":"0","RSSName":"Most Recent"},{"ID":"1","RSSName":"Website feed"}]}
Controller:
public JsonResult RSSList()
{
var query = (from t in db.tblRSSFeeds
select new ViewModels.RSSList()
{
ID = t.pkID.ToString(),
RSSName = t.szFeedName
}).OrderBy( t => t.RSSName).ToList();
var recent = new ViewModels.RSSList();
recent.ID = "0";
recent.RSSName = "Most Recent";
query.Insert(0, recent);
return Json( query, JsonRequestBehavior.AllowGet);
}
I'm thinking my issue has to do with the Feed(data) function in that it's only passing back one record. I tried setting the self.CurrentFeeds(data) as well with no luck. The "alerts" shown above show undefined but I can see the data coming down from fiddler...
For some reason the success function isn't seeing the data correctly to create the array. Why is this?

If it is the response:
{"aaData":[{"ID":"0","RSSName":"Most Recent"},{"ID":"1","RSSName":"Website feed"}]}
Change the success callback to:
$.ajax({
type: "GET",
url: '#Url.Action("RSSList", "RSS")',
success: function (data) {
var feeds = $.map(data.aaData, function (item) {
alert(item.RSSName + " " + item.ID + " 1");
return new Feed(item)
});
self.CurrentFeeds(feeds);
},
error: function (err) {
alert(err.status + " : " + err.statusText);
}
});
And i belive it works, because you are trying to map an object not an array, so you must to get the aaData that is the array to map.

Related

Conflict issue while updating list item with attachments

I created a custom form which has dynamic subform as well. for eg: the custom form consist of 3 sections:
Parent form.
Attachment control
Sub form with add/remove button for creating multiple subforms.
Here how my script works for adding the data: Parent form gets submitted and it returns item ID using jsom. And based on that item ID, Attachments are added to parent form and sub-form data gets added in another list. But sometimes, i am facing conflict issue while adding attachments and here is the code:
if (flag == true) {
oLoader = SP.UI.ModalDialog.showWaitScreenWithNoClose("Working on it", "Creating a new Request...");
var data = [];
var fileArray = [];
$("#attachFilesContainer input:file").each(function () {
if ($(this)[0].files[0]) {
fileArray.push({ "Attachment": $(this)[0].files[0] });
}
});
arraycount += fileArray.length;
data.push({
"Column_x0020_Name": $("#txtAccountNumber").val(),
"Warehouse_x0020_Code": $("#wareHousedrpdown option:selected").text(),
"Facility": $("#Facilitydrpdown option:selected").text(),
"Internal_x002f_External": $("#InternalExteralDrpdown :selected").text(),
"Requested_x0020_Completion_x0020": newReqDate,//$("#txtRequestedCompletionDate").datepicker('getDate').format('MM/dd/yyyy'), //$("#txtRequestedCompletionDate").val(),
"Account_x0020_Management_x0020_A": AccountName,
"Quote_x0020_Required_x003f_": $("#drpQuoteRequired :selected").text(),
"Files": fileArray
});
createItemWithAttachments("Parent", data).then(
function () {
oLoader.close();
window.location.replace(_spPageContextInfo.webAbsoluteUrl + "/Lists/Parent/AllItems.aspx");
//if (oLoader.close) setTimeout(function () { oLoader.close(); window.location.replace(_spPageContextInfo.webAbsoluteUrl + "/Lists/Test/AllItems.aspx"); }, 3000);
//alert('Item created with Multiple attachments');
},
function (sender, args) {
console.log('Error occured' + args.get_message());
}
)
//oLoader.close();
//window.location.replace(_spPageContextInfo.webAbsoluteUrl + "/Lists/Parent/AllItems.aspx");
}
function createSubformItem(listName,i) {
var listItem = {
__metadata: { "type": "SP.Data.SubFormListItem" },
"ParentID": id,
"Start_x0020_SKU":$("input[id='txtStartSKU" + i + "']").val(),
"Qty_x0020_Requested":$("input[id='txtQtyRequested" + i + "']").val(),
"UOM":$("#UOMdrpdown" + i + " option:selected").val(),
"SSRType":$("#SSRTypedrpdown" + i + " option:selected").val()!="null" ? { "__metadata": { "type": "Collection(Edm.String)" }, "results": $("#SSRTypedrpdown"+i+"").val() } : { "__metadata": { "type": "Collection(Edm.String)" }, "results": [""] },
"Hold_x0020_Type":$("#SSRHoldTypedrpdown" + i + " option:selected").val(),
"End_x0020_SKU":$("input[id='txtEndSKU" + i + "']").val(),
"Billing_x0020_UOM":$("#BillingUOMdrpdown" + i + " option:selected").val(),
"Price_x0020_per_x0020_UOM":$("input[id='txtPricePerUOM" + i + "']").val(),
"Instructions":$("textarea[title='Instructions" + i + "']").val(),
};
return $.ajax({
url:"http://devapp/app/_api/web/lists/getbytitle('SubForm')/items",
type: "POST",
contentType: "application/json;odata=verbose",
data: JSON.stringify(listItem),
headers: {
"Accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val()
}
});
}
var createItemWithAttachments = function (listName, listValues) {
var fileCountCheck = 0;
var fileNames;
var context = new SP.ClientContext.get_current();
var dfd = $.Deferred();
var targetList = context.get_web().get_lists().getByTitle(listName);
context.load(targetList);
var singleUser = listValues[0].Account_x0020_Management_x0020_A != "" ? SP.FieldUserValue.fromUser(listValues[0].Account_x0020_Management_x0020_A) : null;
var itemCreateInfo = new SP.ListItemCreationInformation();
var listItem = targetList.addItem(itemCreateInfo);
listItem.set_item("Account_x0020_Number", listValues[0].Account_x0020_Number);
listItem.set_item("Warehouse_x0020_Code", listValues[0].Warehouse_x0020_Code);
listItem.set_item("Facility", listValues[0].Facility);
listItem.set_item("Internal_x002f_External", listValues[0].Internal_x002f_External);
listItem.set_item("Requested_x0020_Completion_x0020", listValues[0].Requested_x0020_Completion_x0020);
listItem.set_item("Account_x0020_Management_x0020_A", singleUser);
listItem.set_item("Quote_x0020_Required_x003f_", listValues[0].Quote_x0020_Required_x003f_);
listItem.update();
for (i = 0; i <= count; i++)
{
createSubformItem("SubForm",i);
}
context.executeQueryAsync(
function () {
id = listItem.get_id();
if (listValues[0].Files.length != 0) {
if (fileCountCheck <= listValues[0].Files.length - 1) {
loopFileUpload(listName, id, listValues, fileCountCheck).then(
function () {
},
function (sender, args) {
console.log("Error uploading");
dfd.reject(sender, args);
}
);
}
}
else {
dfd.resolve(fileCountCheck);
}
},
function (sender, args) {
console.log('Error occured' + args.get_message());
}
);
return dfd.promise();
}
/*End of */
function loopFileUpload(listName, id, listValues, fileCountCheck) {
var dfd = $.Deferred();
uploadFile(listName, id, listValues[0].Files[fileCountCheck].Attachment).then(
function (data) {
var objcontext = new SP.ClientContext();
var targetList = objcontext.get_web().get_lists().getByTitle(listName);
var listItem = targetList.getItemById(id);
objcontext.load(listItem);
objcontext.executeQueryAsync(function () {
console.log("Reload List Item- Success");
fileCountCheck++;
if (fileCountCheck <= listValues[0].Files.length - 1) {
loopFileUpload(listName, id, listValues, fileCountCheck);
} else {
console.log(fileCountCheck + ": Files uploaded");
attcount += fileCountCheck;
if (arraycount == attcount) {
for (i = 0; i <= count; i++)
{
createSubformItem("SubForm",i);
}
oLoader.close();
window.location.replace(_spPageContextInfo.webAbsoluteUrl + "/Lists/ParentList/AllItems.aspx");
}
}
},
function (sender, args) {
console.log("Reload List Item- Fail" + args.get_message());
});
},
function (sender, args) {
console.log("Not uploaded");
dfd.reject(sender, args);
}
);
return dfd.promise();
}
function uploadFile(listName, id, file) {
var deferred = $.Deferred();
var fileName = file.name;
getFileBuffer(file).then(
function (buffer) {
var bytes = new Uint8Array(buffer);
var binary = '';
for (var b = 0; b < bytes.length; b++) {
binary += String.fromCharCode(bytes[b]);
}
var scriptbase = _spPageContextInfo.webServerRelativeUrl + "/_layouts/15/";
console.log(' File size:' + bytes.length);
$.getScript(scriptbase + "SP.RequestExecutor.js", function () {
var createitem = new SP.RequestExecutor(_spPageContextInfo.webServerRelativeUrl);
createitem.executeAsync({
url: _spPageContextInfo.webServerRelativeUrl + "/_api/web/lists/GetByTitle('" + listName + "')/items(" + id + ")/AttachmentFiles/add(FileName='" + file.name + "')",
method: "POST",
binaryStringRequestBody: true,
body: binary,
success: fsucc,
error: ferr,
state: "Update"
});
function fsucc(data) {
console.log(data + ' uploaded successfully');
deferred.resolve(data);
}
function ferr(data) {
console.log(fileName + "not uploaded error");
deferred.reject(data);
}
});
},
function (err) {
deferred.reject(err);
}
);
return deferred.promise();
}
function getFileBuffer(file) {
var deferred = $.Deferred();
var reader = new FileReader();
reader.onload = function (e) {
deferred.resolve(e.target.result);
}
reader.onerror = function (e) {
deferred.reject(e.target.error);
}
reader.readAsArrayBuffer(file);
return deferred.promise();
}
The issue is that upload another attachment before SharePoint is done processing the item with larger files. So when you try to perform another operation on the item (adding another attachment, etc) a race condition is is reached and SharePoint throws the error. When the attachment files are smaller, the process has time to complete before you start the next upload.
You need to find a way to check if the item has completed it's processing. One way of doing this might be to do a get and check the item's etag and ensure that it has incremented the correct number of times before sending another POST.

Getting resolved data undefined in jquery

I am trying to call synchronous call for getting data count using ajax call.
Here is my Jquery Code:
var baseurl = _spPageContextInfo.webServerRelativeUrl;
console.log(baseurl);
var ItemCount = $.Deferred();
function tilesCount(tilename, count)
{
var url = baseurl + "/_api/web/lists/getByTitle('policies')/rootFolder/Folders?$expand=ListItemAllFields";
count = 0;
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
$(data.value).each(function (i, folder) {
count = count + 1;
});
console.log("Call 1: " + count)
ItemCount.resolve(count);
return count;
},
error: function(error){
console.log("Error: " + JSON.stringify(error));
ItemCount.reject;
}
});
}
$(document).ready(function () {
var count = tilesCount("");
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
});
Output:
Call 1: 1
Call 2: undefined
Synchronous call working perfectly, but I am getting data as undefined
Since ajax is asynchronous return count; will be empty
var count = tilesCount("");
So the best solution is to just passed a callback function inside your method which can be call whenever your ajax is completed
function tilesCount(tilename, count, callback)
Wrap this inside your callback function
function(count) {
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
}
so your $(document).ready will be like this and just add parameter count inside the callback
$(document).ready(function () {
tilesCount("", "", function(count) {
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
});
});
your javascript code would be like this now
var baseurl = _spPageContextInfo.webServerRelativeUrl;
console.log(baseurl);
var ItemCount = $.Deferred();
function tilesCount(tilename, count, callback)
{
var url = baseurl + "/_api/web/lists/getByTitle('policies')/rootFolder/Folders?$expand=ListItemAllFields";
count = 0;
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
$(data.value).each(function (i, folder) {
count = count + 1;
});
console.log("Call 1: " + count)
ItemCount.resolve(count);
return callback(count);
},
error: function(error){
console.log("Error: " + JSON.stringify(error));
ItemCount.reject;
}
});
}
$(document).ready(function () {
tilesCount("", "", function(count) {
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
});
});

Using JQuery when and deferred returning undefined still

I'm new to using $.when and $.Deferred(), and I can't seem to get them to work
What I'm trying to do is run a few functions, and when they've all finished trigger a final function
Here are a couple of options I've tried
Option 1 - Returning d1.getRating is not a function (this is a function further up in the script) following docs from JQuery as I understood them
// Set Deferred
var d1 = $.Deferred();
// Return movie information
if (idResp[0].type === "movie") {
// Output Slug
traktSlug = 'movies/' + idResp[0].movie.ids.slug;
// Output
$.when(d1).done(function (ratingValue) {
console.log('Rating Is: ' + ratingValue);
outputIMDb(showCheckIn, traktSlug, ratingValue);
});
// Get Rating
d1.getRating(idResp[0].type, idResp[0].movie.ids.trakt);
}
Option 2 - Returning ratingValue is undefined
// Return movie information
if (idResp[0].type === "movie") {
// Output Slug
traktSlug = 'movies/' + idResp[0].movie.ids.slug;
// Output
$.when(getRating(idResp[0].type, idResp[0].movie.ids.trakt)).done(function (ratingValue) {
console.log('Rating Is: ' + ratingValue);
outputIMDb(showCheckIn, traktSlug, ratingValue);
});
}
Any suggestions or a nudge in the right direction would be much appreciated
Full source code can be viewed on GitHub
UPDATE
I realised after reading the JQuery docs again that resolve() is not the name of a generic function, so I modified my code, but I'm still getting ratingValue is undefined back
Updated Option 1 Code
// Set Deferred
var d1 = $.Deferred();
// Return movie information
if (idResp[0].type === "movie") {
// Output Slug
var traktSlug = 'movies/' + idResp[0].movie.ids.slug;
// Output Div
$.when(d1).done(function(ratingValue) {
console.log('Rating Is: ' + ratingValue);
outputIMDb(1, traktSlug, ratingValue);
});
// Get Rating
d1.resolve(getRating(idResp[0].type, idResp[0].movie.ids.trakt));
}
UPDATE 2
Sorry, my apologies for not including the getRating function. As follows
// Get Rating
function getRating(type, id, season = 0, episode =0) {
var slugType = "";
switch (type) {
case "movie":
slugType = "movies";
break;
default:
slugType = "movies";
break;
}
var request = new XMLHttpRequest();
request.open('GET', 'https://api.trakt.tv/' + slugType + '/' + id + '/ratings');
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('trakt-api-version', '2');
request.setRequestHeader('trakt-api-key', APP_KEY);
request.onreadystatechange = function () {
if (this.readyState === 4) {
// Get Response and put in array
var ratingsResp = JSON.parse(this.responseText);
// Return Rating
return Math.round(ratingsResp.rating * 10);
} else {
return 0;
}
};
request.send();
}
The main thing to do is to write getRating() to return a promise. You could promisify XMLHttpRequest() but it's much easier to use jQuery.ajax().
Here it is, based on the original code on GitHub :
function getRating(type, id, season=0, episode=0) { // mmm, formal defaults - odd for browser-based javascript.
var slugType;
switch(type) {
case 'movie':
slugType = 'movies';
break;
default:
slugType = 'movies';
}
return $.ajax({
url: 'https://api.trakt.tv/' + slugType + '/' + id + '/ratings',
headers: {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': APP_KEY
},
dataType: 'json'
}).then(function(response) {
return Math.round(response.rating * 10);
}).then(null, function(xhr, textMessage, errorThrown) {
console.error('getRating error: ', textMessage);
return $.when(0); // error recovery.
});
}
Then, use jQuery.ajax() in the main routine too :
chrome.storage.local.get('access_token', function(result) {
var ACC_TOK = result.access_token;
if (ACC_TOK && typeof ACC_TOK !== undefined) {
if (tabURL.includes('imdb.com')) {
$.ajax({
url: 'https://api.trakt.tv/search/imdb/' + tabURL.match(/tt\d{7}/),
headers: {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': APP_KEY
},
dataType: 'json'
}).then(function(idResp) {
if(idResp[0].type === 'movie') {
return getRating(idResp[0].type, idResp[0].movie.ids.trakt).then(function(ratingValue) {
console.log('Rating Is: ' + ratingValue);
outputIMDb(1, 'movies/' + idResp[0].movie.ids.slug, ratingValue);
});
} else {
console.log("Type: " + idResp.type);
}
}).fail(function(xhr, textMessage, errorThrown) {
console.error(textMessage);
});
}
}
});
Note that because jQuery.ajax() returns a promise, there's no need to generate/resolve your own jQuery.Deferred()s.
Code above may not be 100% correct but should go a considerable distance to a working solution.

why my textbox doesnot return any proper value in jquery?

I have created this controller for for getting existing value by searching id. this is my controller for searching data by id. this code is running well but result is not acceptable. i am new in jquery that's why i am explaining this very helpfully..
public string Search(string id=null)
{
string[] ci = new string[9];
//return "Artistry";
string cn = null;
cn = Request.QueryString["id"];
if (cn != null)
{
ClientInfo c = db.SingleOrDefault<ClientInfo>("where CId='" + cn + "'");
if (c != null)
{
// ci[0] = c.CId.ToString();
ci[1] = c.CName;
ci[2] = c.CCName;
ci[3] = c.PhoneNo.ToString();
ci[4] = c.Fax;
ci[5] = c.Email;
ci[6] = c.Address;
ci[7] = c.PostalCode.ToString();
ci[8] = c.Country;
return ci[5];
}
else
return null;
}
else
return null;
//*/
}
My view page script for showing my data..
<script type="text/javascript">
$(document).ready(function () {
$('#CId').blur(function () {
var v = $('#CId').val();
var url = "/Clients/Search/" + v;
// alert("Test : " + url);
$.get(url, function (data, status) {
$("#CName").val(1);
$("#CCName").val(2);
$("#PhoneNo").val(3);
$("#Fax").val(4);
$("#Email").val(5);
$("#Address").val(6);
$("#PostalCode").val(7);
$("#Country").val(8);
alert("Test : " + data + " Status :" + status);
});
});
});
</script>
And finally my sql server database for showing data in views are..
SELECT TOP 1000 [CId]
,[CName]
,[CCName]
,[PhoneNo]
,[Fax]
,[Email]
,[Address]
,[PostalCode]
,[Country]
FROM [test].[dbo].[ClientInfo]
I think you should return json type data like so:
public JsonResult Search(string id=null)
{
// view code
return Json(new {info=ci[5]});
}
And client code:
$.get(url, function (data, status) {
alert("Test : " + data.info + " Status :" + status);
});

Object doesn't support property or method 'save'

I am getting error while calling reportManager.save("/EditInitiatives.svc/SaveGridData"); method
<script type='text/javascript'>
$(function () {
$('#saveReport, #save-report-bottom').click(function () {
$.blockUI({ message: '<h4><img src="/Images/busy.gif" /> Saving your changes.<br/>This operation might impact several reports at once.<br/>Please be patient.</h4>' });
uiController.disableSaveButton();
reportManager.save("/EditInitiatives.svc/SaveGridData");
});
var reportManager = function () {
var tableData = JSON.stringify(handsontable.getData());
var input = JSON.stringify({ "input": tableData });
alert("Data" + input);
save = function(saveUrl) {
alert("save" + saveUrl);
$.ajax({
url: saveUrl,
type: 'POST',
dataType: 'json',
data: input, //returns all cells' data
contentType: 'application/json; charset=utf-8',
success: function(res) {
if (res.result === 'ok') {
console.text('Data saved');
}
$.unblockUI();
},
error: function (xhr) {
alert(xhr.responseText);
}
});
}
};
}
</script>
You can not access it since save is a Global variable and not part of reportManager.
The reason why it is global is because you are missing the var in front of the variable. That puts it into the Global Namespace. It will not magically be hooked up to the block scope of the function. You would need to use an OO approach to get that to work. Some basic ideas are
function Report() {
var x = 1;
this.save = function () {
alert("First: " + x);
}
}
var report = new Report();
report.save();
function report_2() {
var x = 1;
return {
save : function () {
alert("Second: " + x);
}
}
}
var report2 = report_2();
report2.save();
var report_3 = (function () {
var x = 1;
var returnObj = {};
returnObj.save = function () {
alert("Third: " + x);
}
return returnObj;
//return {
// save : function() {
// alert("Third: " + x);
// }
//}
})();
report_3.save();
Fiddle of Examples: http://jsfiddle.net/5Zhsq/
You have declared the save function without var keyword; so it will be declared in global scope not as function of reportManager.
Even if you put the var keyword before save function then it's not accessible from outside of reportManager function scope. to expose it to public you need kinda export it. here is a simple pattern to do it:
var reportManager = (function (self) {
function save(saveUrl) { ... } //this function is not public yet
self.save = save; //we are exporting the save function here
return self; //now we are returning an object with save function
})(reportManager || {});
reportManager.save("your-param-here"); //Now use your function the way you want

Categories