Knockout.js mapping plugin how to update data - javascript

The following code will fetch and bind the JSON data correctly to the view but when I GET updated data the view will not update. The data has changed but the view doesn't update. Any ideas what I'm doing wrong? I have trouble understanding how the mapping plugin works.
jQuery(document).ready(function() {
setInterval(LeaderboardViewModel, (10 * 500));
function LeaderboardViewModel() {
var self = this;
this.ArrayOfPlayers = ko.mapping.fromJS([]);
$.ajax({
type: 'GET',
url: 'http://localhost:5500/leaderboard/',
context: this,
success: function(data) {
self.SuccessfullyRetrievedModelsFromAjax(data);
},
dataType: 'json'
});
this.SuccessfullyRetrievedModelsFromAjax = function(data) {
var array = $.map(data.leaderboard, function(value, index) {
return [value];
});
console.log(array);
ko.mapping.fromJS(array, {}, self.ArrayOfPlayers);
};
}
ko.applyBindings(new LeaderboardViewModel());
});
The following HTML renders the data correctly when I load the page:
<tbody data-bind="foreach: ArrayOfPlayers">
EDIT: When I load the page it works correctly. the set interval part doesn't work.
GET data:
{
"leaderboard": {
"1": {
"deaths": 52,
"game_count": 13,
"game_defeats": 0,
"game_deserts": 0,
"game_draws": 0,
"game_wins": 13,
"id": 2,
"kills": 78,
"level": 8,
"rank": 1,
"xp": 3260
},
"10": {
"deaths": 78,
"game_count": 13,
"game_defeats": 13,
"game_deserts": 0,
"game_draws": 0,
"game_wins": 0,
"id": 1,
"kills": 52,
"level": 5,
"rank": 10,
"xp": 1570
},
}
EDIT 2: no console errors.

That's because you use keyword 'new' for applyBinding, but set timeout to function definition.
It must work in this way:
jQuery(document).ready(function() {
function LeaderboardViewModel() {
var self = this;
this.ArrayOfPlayers = ko.mapping.fromJS([]);
this.SuccessfullyRetrievedModelsFromAjax = function(data) {
var array = $.map(data.leaderboard, function(value, index) {
return [value];
});
console.log(array);
ko.mapping.fromJS(array, {}, self.ArrayOfPlayers);
};
self.UpdateMappings = function(){
$.ajax({
type: 'GET',
url: 'http://localhost:5500/leaderboard/',
context: this,
success: function(data) {
self.SuccessfullyRetrievedModelsFromAjax(data);
},
dataType: 'json'
});
};
}
var viewModel = new LeaderboardViewModel();
setInterval(viewModel.UpdateMappings, (10 * 500));
ko.applyBindings(viewModel);
});

Related

Select2 and load JSON data

I have read a lot of questions here, but i'm not able to solve this
$('.titular').select2({
placeholder: 'Select',
width: '100%',
ajax: {
url: "/ajax/ajax.php",
data: function(params) {
var query = {
query: params.term
}
return query;
},
processResults: function(data) {
console.log(data);
return {
results: $.map(data, function (item) {
return {
id: item.id,
nome: item.nome
}
})
};
},
},
escapeMarkup: function (markup) { return markup; }
});
And my JSON:
[{"id":12,"nome":"Joe Bill"},{"id":13,"nome":"PJ"},{"id":14,"nome":"John"},{"id":16,"nome":"Acme"},{"id":17,"nome":"Acme2"},{"id":18,"nome":"Acme3"}]
The results are not showing and developer console shows:
jQuery.Deferred exception: Cannot use 'in' operator to search for 'length' in [{"id":16,"nome":"Acme"},{"id":17,"nome":"Acme2"},{"id":18,"nome":"Acme3"}] TypeError: Cannot use 'in' operator to search for 'length' in [{"id":16,"nome":"Acme"},{"id":17,"nome":"Acme2"},{"id":18,"nome":"Acme3"}]
I've tried to use the JSON viewed in oficial documentation, unsuccesfully...
I appreciate any help
The console error means that you are passing to the map an string so you need to parse your data as json before. I've done a little test and it is working like this:
processResults: function (data, params) {
data = JSON.parse('[{ "id": 12, "nome": "Joe Bill" }, { "id": 13, "nome": "PJ" }, { "id": 14, "nome": "John" }, { "id": 16, "nome": "Acme" }, { "id": 17, "nome": "Acme2" }, { "id": 18, "nome": "Acme3" }]');
var r = $.map(data, function (item) { return { id: item.id, text: item.nome }});
return { results: r };
}
Hope this helps.

Shopify Ajax API is not accepting properties

I am trying to pass three products through the shopify ajax api. It sends over the variant id and quantity but not the properties. the code is below. if I add request.properties to the Shopify.addItem function it stops after one item and gives me a pop saying that one item has been added to the cart. It does not add the other two items nor does it redirect. If I remove request.properties from the Shopify.addItem function it adds all three items to the cart but with no properties.
FINAL CODE Revised from #miglio code
var FreeTrial={
data:[],
ini:0,
total:0,
addItem:function(qty,id,properties,callback) {
var params = {quantity:qty,id:id};
if(properties != false){
params.properties = properties;
}
$.ajax({
type: 'POST',
url: '/cart/add.js',
dataType: 'json',
async:false,
data: params,
success: function(){
if(typeof callback === 'function'){
callback();
}
},
error: function(){}
});
},
recursive:function(){
FreeTrial.addItem(FreeTrial.data[FreeTrial.ini].qty,FreeTrial.data[FreeTrial.ini].id,FreeTrial.data[FreeTrial.ini].properties,function(){
//console.log(FreeTrial.data[FreeTrial.ini]);
FreeTrial.ini += 1;
if(FreeTrial.ini < FreeTrial.total){
FreeTrial.recursive();
}else{
//return false;
document.location.href = '/cart';
}
});
},
begin:function(){
/* SET YOUR ARRAY QTY's' ID's*/
FreeTrial.data = [
{
"qty": '1',
"id": 'VARIANT_ID_GOES_HERE',
"properties": false
},
{
"qty": '1',
"id": 'VARIANT_ID_GOES_HERE',
"properties": false
},
{
"qty": '1',
"id": 'VARIANT_ID_GOES_HERE',
"properties": false
},
{
"qty": '1',
"id": 'VARIANT_ID_GOES_HERE',
"properties": false
},
{
"qty": '1',
"id": 'VARIANT_ID_GOES_HERE',
"properties": {
"recurring_price": "200.00",
"shipping_interval_frequency": "30",
"shipping_interval_unit_type": "days",
"subscription_id": "12599"
}
}
];
FreeTrial.total = FreeTrial.data.length;
FreeTrial.recursive();
}
}
FreeTrial.begin();
To add properties I use this function and work fine for me.
addItem=function(qty,id,properties,callback) {
var params = {quantity:qty,id:id};
if(properties != false){
params.properties = properties;
}
$.ajax({
type: 'POST',
url: '/cart/add.js',
dataType: 'json',
data: params,
success: function(){
if(typeof callback === 'function'){
callback();
}
},
error: function(){}
});
}
//Example :
var qty = 1;
var id = 123456;//variant_id
var properties: {
"recurring_price": "12",
"shipping_interval_frequency": "34",
"shipping_interval_unit_type": "56",
"subscription_id": "78"
}
//
addItem(qty,id,properties,function(){
console.log('done');
});
Well, I did this code for multiple items and a save in a gist:
multiple add to cart
Any POST to the endpoint url: '/cart/add.js', can include properties. If the properties are setup correctly, it works like a charm. You can assign as many properties to a variant as you want. Has been working for what, 5 years now? This function has been working for that long at least... no trouble.
addItemWithProperties: function(variant_id, quantity, properties, callback) {
var quantity = quantity || 1;
if(properties) {
var data = properties.join("&")+"&quantity="+quantity+"&id="+variant_id;
} else {
var data = "quantity="+quantity+"&id="+variant_id;
}
var params = {
type: "POST",
url: "/cart/add.js",
data: data,
dataType: "json",
success: function(line_item) {
if((typeof callback) === "function") {
callback(line_item)
} else {
Shopify.onItemAdded(line_item)
}
},
error: function(XMLHttpRequest, textStatus) {
Shopify.api.onError(XMLHttpRequest, textStatus)
}
};
$.ajax(params)
},
edit. I guess you could do the call manually
add to cart
Considering the Shopify.addItem() function is, straight off their API I'm not sure you can simply add a parameter like that.
My guess is that the extra parameter has the effect that the function doesn't run.

How to call Angularjs controller function outside the controller in an Ajax call

Am trying to call Angularjs function outside the controller component like the below :
<script type="text/javascript">
function saveprof() {
$('.spinner').show();
$.ajax({
type: "POST",
url: "saveprof",
enctype: 'multipart/form-data',
async: true,
data: {
'rinput_Aj': JSON.stringify(angular.element(document.getElementById('rdExampleApp')).scope().$func()),
'csrfmiddlewaretoken': $("input[name=csrfmiddlewaretoken]").val()
},
success: function (data, textStatus, jqXHR) {
$('#message').html(data);
window.location.href = 'myprofile';
window.location('myprofile');
$('.spinner').fadeOut();
}
});
}
</script>
Here is the angularjs controller code :
<script>
var app = angular.module('rdExampleApp', ['ui.rdplot']);
app.controller('rdPlotCtrl', function ($scope) {
$scope.dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
$scope.func = function func() {
var jdata = $scope.dataset;
return jdata;
}
});
</script>
It throws an error
Uncaught TypeError: Cannot read property '$func' of undefined
EDIT:
After the suggestions, I converted my jquery ajax call to $http function in Angularjs.. But it does nothing.. No error in console :(
Here is how am invoking the $http service function
<body ng-controller="rdCtrl">
<a ng-click="saveprof()">Save</a>
<script>
var app = angular.module('rdExampleApp', ['ui.rdplot']);
app.controller('rdCtrl', function ($scope) {
$scope.dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
$scope.func = function func() {
var jdata = $scope.dataset;
return jdata;
}, function ($scope, $http) {
$scope.saveprof = function () {
//show spinner
$('.spinner').show();
$http.post('saveprof', {
data: { 'data': JSON.stringify($scope.dataset) }
})
.success(function (data) {
if (data == "null") {
//your code if return data empty
} else {
//your code if return data not empty
$('#message').html(data);
}
//hide spinner
$('.spinner').fadeOut();
})
.error(function (data, status, headers, config) {
console.log('error' + status);
//hide spinner in case of error
$('.spinner').fadeOut();
})
}
}
);
</script>
</body>
What am I missing?
in order to run XMLHttpRequest requests to the server you have many options in angularjs, you dont have to mess with simple javascript and call angular scope to get variables and functions.
you can do that either with $http or with services(leave it for now).
i am going to show how you can make the request with $http in native angular.
first of all you have to import the $http module on the declaration of your controller, like this :
var app = angular.module('rdExampleApp', ['ui.rdplot']);
app.controller('rdPlotCtrl', function ($scope,$http) {...});
into you controller you create the json object as you do it and then do the request like this:
//show spinner
$('.spinner').show();
$http.post('dal/addEventHalls.php', {
data: {'data': $scope.datase}
})
.success(function (data) {
if (data == "null") {
//your code if return data empty
} else {
//your code if return data not empty
}
//hide spinner
$('.spinner').fadeOut();
})
.error(function (data, status, headers, config) {
console.log('error' + status);
//hide spinner in case of error
$('.spinner').fadeOut();
})
as you can see we dont use url parameter but we pass the url directly into post() function. the data parameter is there to put whatever data you would like to send to the server.
hope helps good luck.
UPDATE
personally i dont stringify the data parameters.i pass them like json object
into php file , in order to get the data , try this:
$params = json_decode(file_get_contents('php://input'), true); //read values from angular factory-service

Filter select options based on data-* attribute

I have 2 select menus on my page.
First have 3 values: All, Active, Unactive and second is filled with data that comes from server.
I need to connect them, so that when I select an option from the first select menu, the second select will have only certain options (filter second select menu)
My idea was to get data from server with 3 elements:
[ { "Id": 1, "Name": "Active One", "Active":true },
{ "Id": 2, "Name": "Active Two", "Active":true },
{ "Id": 3, "Name": "Unactive One", "Active":false },
{ "Id": 4, "Name": "Unactive Two", "Active":false } ]
Second thing that I do is adding all options with custom attribute to that select:
<option value=' + n[index].Id + ' data-active='+n[index].Active+'>' + n[index].Name + '</option>
I have problem with filtering second select.
I've wrapped filling second select inside a plugin so it will be easier to reuse it.
Here is jsFiddle demo: http://jsfiddle.net/Misiu/pr8e9/4/
What I would like this to work is after filling select from server user will be able to select every option from second select, but when he will change first select the second one will update-show only appropriate options.
So if he'll select Active he will be able to select only Active One or Active Two.
EDIT: This is working solution thanks to #oe.elvik
Alternative solution:
(function ($) {
var meinData;
$.fn.createOptions = function(filter) {
var $this = this;
var n = meinData
var list = "";
for (var index = 0; index < n.length; index++) {
if(filter == -1 || (filter == 1 && n[index].Active) || (filter == 0 && !n[index].Active)){
list += '<option value=' + n[index].Id + ' data-active='+n[index].Active+'>' + n[index].Name + '</option>';
}
}
$this.filter("select").each(function () {
$(this).empty();
$(this).append(list);
if ($.ui.selectmenu && defaults.selectmenu) {
$this.selectmenu();
}
});
}
$.fn.ajaxSelect = function (options) {
var $this = this;
//options
var settings = $.extend({}, defaults, options);
//disable select
if ($.ui.selectmenu && settings.selectmenu && settings.disableOnLoad) {
$this.selectmenu('disable');
}
//ajax call
$.ajax({
type: settings.type,
contentType: settings.contentType,
url: settings.url,
dataType: settings.dataType,
data: settings.data
}).done(function (data) {
meinData = data.d || data;
$($this).createOptions(-1)
settings.success.call(this);
}).fail(function () {
settings.error.call(this);
});
return this;
};
var defaults = {
type: "POST",
contentType: "application/json; charset=utf-8",
url: '/echo/json/',
dataType: 'json',
data: null,
async: true,
selectmenu: true,
disableOnLoad: true,
success: function () {},
error: function () {}
};
})(jQuery);
$(function () {
var data = {
json: $.toJSON({
d: [{ "Id": 1, "Name": "Active One", "Active":true }, { "Id": 2, "Name": "Active Two", "Active":true },{ "Id": 3, "Name": "Unactive One", "Active":false }, { "Id": 4, "Name": "Unactive Two", "Active":false }]
}),
delay: 2//simulate loading
};
function ok() {
$('select#me').selectmenu({ select: function(event, options) {
$('select#mein').createOptions(options.value)
}
});
}
function error() {
alert('there was a problem :(');
}
$('select#me').selectmenu().selectmenu('disable');
$('select#mein').selectmenu().ajaxSelect({
data: data,
success: ok,
error: error
});
});

Map nested JSON data to Knockout observableArray

We have an observableArray in Knockout which include several JSON objects.
Under each JSON object we have an nested array which needs to be observable.
Knockout is not able to observe arrays nested in each JSON object, in the observableArray.
Is it possible to map an array which is already nested in an observableArray?
Here is an example of one JSON object in the observableArray.
Note: We need to make the "attributeValues" array observable.
{
"attribute": {
"id": 6,
"name": "Some attribute name",
"typeID": 5
},
"type": {
"id": 5,
"typeName": "list"
},
"attributeValues": [{
"id": 10,
"attributeID": 6,
"value": "Some attribute value",
"chosen": false
}, {
"id": 11,
"attributeID": 6,
"value": "Another attribute value",
"chosen": false
}, {
"id": 12,
"attributeID": 6,
"value": "Third attribute value",
"chosen": false
}]
}
Here is the code we're using now:
$.ajax({
type: 'GET',
url: '/JsonService',
success: function (data) {
avm.attributes(data.allAttributes);
},
dataType: 'json',
traditional: true
});
function attributeViewModel() {
var self = this;
self.attributes = ko.observableArray([]);
}
var avm = new attributeViewModel();
ko.applyBindings(avm);
One solution would be to define one more constructor function to wrap each item of the attributes array. In this function, you can then define the attributeValues array as observable as illustrated in the following example:
function AttributeValueViewModel (data) {
var self = this;
self.attribute = ko.observable(data.attribute);
self.type = ko.observable(data.type);
self.attributeValues = ko.observableArray(data.attributeValues);
}
function attributeViewModel() {
var self = this;
self.attributes = ko.observableArray([]);
self.addList = function (list) {
ko.utils.arrayForEach(list, function(item) {
self.attributes.push(new AttributeValueViewModel(item));
});
};
}
var avm = new attributeViewModel();
ko.applyBindings(avm);
$.getJSON('url', function (list) { avm.addList(list); });

Categories

Resources