I have this code:
var my = {};
(function () {
var self = this;
this.sampleData = { };
this.loadData = function() {
$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?',
{tags: "cat", tagmode: "any", format: "json"},
function(data){
self.sampleData = data;
}
);
};
}).apply(my);
my.loadData();
console.log(my.sampleData); // {}
The problem is my.sampleData not have anything.
Try this sample here: http://jsfiddle.net/r57ML/
The reason is that the getJSON call is asynchronous, so you're looking for the data before it's been returned. Instead, put your code using the data inside the callback, either directly or indirectly.
For instance, you can have your loadData call accept a callback:
var my = {};
(function () {
var self = this;
this.sampleData = { };
this.loadData = function(callback) {
$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?',
{tags: "cat", tagmode: "any", format: "json"},
function(data){
self.sampleData = data;
callback(); // <==== Call the callback
}
);
};
}).apply(my);
my.loadData(function() { // <=== Pass in a callback
console.log(my.sampleData); // Now the data is htere
});
Side note: Since your my object is a singleton, you can simplify that code a fair bit, no need for apply, this, or self, since your anonymous function is a closure over the context in which my is defined:
var my = {};
(function () {
my.sampleData = { };
my.loadData = function(callback) {
$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?',
{tags: "cat", tagmode: "any", format: "json"},
function(data){
my.sampleData = data;
callback();
}
);
};
})();
my.loadData(function() {
console.log(my.sampleData); // Now the data is htere
});
Of course, if you're using a constructor function or something instead (you weren't in your quoted code, but...), then of course you might need the more complex structure.
(function () {
var self = this;
this.sampleData = {};
this.loadData = function() {
$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?',
{tags: "cat", tagmode: "any", format: "json"},
function(data){
self.sampleData = data;
console.log(my.sampleData); // you get sample data after ajax response
},
'json');
};
}).apply(my);
my.loadData();
Related
I have a lot of (KnockOut) view models that get data from a rest service and then populate "item" view models that are pretty much simple and just contain the fields coming from the REST interface.
I was just wondering if there was a way to not having to define the item viewmodels but somehow just create them dynamic as objects (where each property is an observable).
So in the example below I would want to not have the "ItemViewModel" but just say within the AddItems function that it should create an object based on the data and make each entry an ko.observable. the passed "itemName" then contains "ItemViewModel1" (or in other call "ItemViewModel2" ...etc).
So e.g. if the Json Rest input has a field "LAST_NAME" it would add self.LAST_NAME = ko.observable()" filled with that value etc. (so I can still reference it in the views).
var ItemViewModel1 = function (data) {
var self = this;
self.PAR1 = ko.observable(data.PAR1)
self.PAR2 = ko.observable(data.PAR2)
self.PAR3 = ko.observable(data.PAR3)
self.PAR4 = ko.observable(data.PAR4)
// … etc
}
var MasterViewModel1 = function (data) {
var self = this;
ReportBaseViewModel.call(self)
}
var ReportBaseViewModel = function () {
var self = this;
/* commonly used vars */
self.report = ko.observable();
self.searchedCallBackFunction = ko.observable();
self.items = ko.observableArray();
self.selecteditem = ko.observable();
self.selectedPerson = ko.observable();
/* method: print */
self.PrintEventHandler = function (data) { window.print(); };
/* method: add items to array */
self.AddItems = function (data) {
var newitems = ko.utils.arrayMap(data, function (item) {
c = new window[self.itemname](item);
return c;
});
self.items(newitems);
};
/* eventhandler: select one item */
self.SelectEventHandler = function (item) {
selecteditem(item);
};
self.GetReport = function (selectedPerson, viewContainer, url, itemName) {
self.selectedPerson(selectedPerson);
self.itemname = itemName;
var jqxhr = $.ajax({
url: url,
type: "GET"
}).done(function (data, textStatus, jqXHR) {
if (data != null) {
self.AddItems(data);
$('#' + viewContainer).show();
document.getElementById(viewContainer).scrollIntoView();
}
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log('fail' + JSON.stringify(jqXHR));
toastr.options = {
"closeButton": true,
"debug": false,
"newestOnTop": false,
"progressBar": false,
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "0",
"hideDuration": "1000",
"timeOut": "0",
"extendedTimeOut": "0",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
toastr["error"]("ERROR");
}).always(function (jqXHR, textStatus, errorString) {
if (typeof self.searchedCallBackFunction() === 'function') {
self.searchedCallBackFunction();
}
});
}
}
There is. If your objects are simple and not nested, you can write the code to map them yourself:
var someJSON = '{ "firstName": "John", "lastName": "Doe" }';
var makeSimpleVM = function(obj) {
// Return a new object with all property
// values wrapped in an observable
return Object
.keys(obj)
.reduce(function(vm, key) {
vm[key] = ko.observable(obj[key]);
return vm;
}, {});
};
var myVM = makeSimpleVM(JSON.parse(someJSON));
console.log(ko.isObservable(myVM.firstName)); // true
console.log(myVM.firstName()); // John
myVM.firstName("Jane");
console.log(myVM.firstName()); // Jane
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
I think it's important to read through this naive implementation: it makes you understand why it's probably a better idea to use a ready-made plugin.
As soon as your server side code contains arrays, nested viewmodels or any properties that you don't want mapped, you'll run in to problems. The ko.mapping plugin has already solved these problems for you. It maps arrays to ko.observableArrays and lets you specify mapping strategies.
var someJSON = '{ "firstName": "John", "lastName": "Doe" }';
// Let's use the library this time
var myVM = ko.mapping.fromJS(JSON.parse(someJSON));
console.log(ko.isObservable(myVM.firstName)); // true
console.log(myVM.firstName()); // John
myVM.firstName("Jane");
console.log(myVM.firstName()); // Jane
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
You could try using the mapping plugin or the Json functions, depending on what exactly you are looking for. I think what you are looking for is the mapping plugin:
http://knockoutjs.com/documentation/plugins-mapping.html
http://knockoutjs.com/documentation/json-data.html
I have some functions that are making some Facebook API calls, and an init one:
var fbAPI = {
init: function () {
this.friendList(), this.friendCount();
},
friendList: function (closure) {
var _self = this;
return FB.api(
"/me/taggable_friends",
function (data) {
_self.setFriends(data);
closure(data);
}
);
},
friendCount: function (closure) {
var _self = this;
return FB.api(
"/me/friends",
function (data) {
_self.setFriendsCount(data.summary.total_count);
closure(data);
}
);
},
friends: null,
friendsCount: null,
setFriends: function (data) {
this.friends = data;
},
setFriendsCount: function (count) {
this.friendsCount = count;
}
};
When all the calls are finished I want to remove the loading overlay.
Something like:
(fbAPI.init()).finish(function(){
//do something
});
How can I achieve this?
I have the following:
$scope.option = {
generateID:function(){
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
},
values : [
{id:this.generateId()},
{id:this.generateId()},
{id:this.generateId()},
{id:this.generateId()}
],
markCorrect : function(option){
},
remove:function(option)
{
this.values = this.values.filter(function(value){return value.id!=option.id})
}
}
I always get a this.generateId is not a function error. I am pretty sure that i am missing something fundamental here!
It may be better to store the id generator function in a separate function so it is easier to reference:
function generateId = function() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
}
$scope.option = {
generateID: generateId,
values : [
{id: generateId()},
{id: generateId()},
{id: generateId()},
{id: generateId()}
],
markCorrect : function(option){
},
remove:function(option)
{
this.values = this.values.filter(function(value){return value.id!=option.id})
}
}
The primary issue is that you're trying to access properties of $scope.option in the middle of declaring it. Try doing something like this instead:
$scope.option = (function () {
function generateId () {
/* logic */
}
return {
values: [
{id: generateId()}
// ...
],
markCorrect: function () {},
remove: function () {}
};
}) ();
This is the 'revealing module pattern', i.e. a function that returns an object forming a closure on some other data or functionality.
There is a typo; rename generateID to generateId.
i'm using the same code from this article (internationalization with angularjs)
...
but i need the "tables" variable to be fetched from an ajax request response using "$http get" but couldn't !! ... here is the code,
var xlat = angular.module('xlat', []);
xlat.factory('xlatService', function ($http) {
var currentLanguage = 'en';
// var tables = $.extend(true, {}, initialXlatTables);
var tables = {
'en': {
'textKeys.events': 'Events'
}
};
var service = {
getData: function () {
var req = {
method: 'GET',
url: 'local/en_US.php',
cache: true,
headers: {
'Content-Type': 'json'
}
};
$http(req).success(function (data) {
tables = data;
});
},
setCurrentLanguage: function (newCurrentLanguage) {
currentLanguage = newCurrentLanguage;
},
getCurrentLanguage: function () {
return currentLanguage;
},
xlat: function (label, parameters) {
service.getData();
if (parameters === null || $.isEmptyObject(parameters)) {
return tables[currentLanguage][label];
} else {
return $interpolate(tables[currentLanguage][label])(parameters);
}
}
};
return service;
});
but the variable "tables" does not change when i use the filter...
var xlat = angular.module('xlat', []);
xlat.filter('xlat', ['xlatService', function (xlatService) {
return function (label, parameters) {
return xlatService.xlat(label, parameters);
};
}]);
Try this one:
var xlat = angular.module('xlat', []);
xlat.filter('xlat', ['xlatService', function (xlatService) {
function myfiler(label, parameters) {
return xlatService.xlat(label, parameters);
};
myfiler.$stateful = true;
return myfilter;
}]);
Stateful filters -
https://code.angularjs.org/1.3.9/docs/guide/filter
Secod: you should load tables in factory method not in xlat function.
I'm trying to get the member ID for a Trello account and then use that member ID in a constructor function to generate boards. My problem is that I can't access the member ID that I return outside of the object I created. How do I access the memberID outside of the TrellloConnect object?
Here is the code:
var TrelloConnect = {
init: function(config) {
this.config = config;
this.doAuthorize();
this.updateLogStatus();
this.bindLogIn();
this.bindLogOut();
this.whenAuthorized();
this.getMemberID();
},
bindLogIn: function() {
this.config.connectButton.click(function() {
Trello.authorize({
type: "redirect",
success: this.doAuthorize,
name: "WonderBoard",
expiration: "never"
});
});
},
bindLogOut: function() {
this.config.disconnectButton.click(function() {
var self = TrelloConnect;
Trello.deauthorize();
self.updateLogStatus();
});
},
doAuthorize: function() {
var self = TrelloConnect;
self.updateLogStatus();
},
updateLogStatus: function() {
var isLoggedIn = Trello.authorized();
this.config.loggedOutContainer.toggle(!isLoggedIn);
this.config.loggedInContainer.toggle(isLoggedIn);
},
whenAuthorized: function() {
Trello.authorize({
interactive: false,
success: TrelloConnect.doAuthorize
});
},
getMemberID: function() {
Trello.members.get("me", function(member) {
console.log(member.id);
return member.id;
});
}
};
TrelloConnect.init({
connectButton: $('#connectLink'),
disconnectButton: $('#disconnect'),
loggedInContainer: $('#loggedin'),
loggedOutContainer: $('#loggedout')
});
function Board(memberID) {
console.log(memberID);
}
var board = new Board(TrelloConnect.getMemberID());
Trello.members.get is an asynchronous function (i.e. it takes a callback instead of returning a value); you'll need to use a callback if you want to do something with the data that it fetches.
If you change getMemberID to take a callback
...
getMemberID: function(callback) {
Trello.members.get("me", function(member){
callback(member.id);
});
}
...
... then you could do something like this:
TrelloConnect.getMemberId(function(id){
new Board(id);
});