Access Properties from Knockout Object - javascript

Here is the scenario:
I currently have an object that looks like the following:
After running modal.PersonInfo I get the object returned starting at line 3. See image above.
Question: Within my HTML, how can I call FirstNameand LastName
Error States:
Uncaught ReferenceError: Unable to process binding "text: function (){return PersonInfo().FirstName}" Message PersonInfo is not defined
JavaScript:
function Person() {
var modal = this;
modal.PersonInfo = ko.observable('');
modal.setData = function (id) {
$.ajax({
type: "GET",
url: '/Person/UserInformation?id=' + id,
contentType: "application/json; charset=utf-8",
success: function (data) {
modal.setPersonInfo(data);
$('#person-modal').modal('show');
}
});
modal.setPersonInfo = function (data) {
modal.PersonInfo = data;
}
}
};
HTML
My thought was that I could do the following:
<p data-bind="text: PersonInfo().FirstName"></p>
<p data-bind="text: PersonInfo().LastName"></p>

This line replaces the observable, instead of assigning the value:
modal.PersonInfo = data;
Try this instead:
modal.PersonInfo(data)
There's also a misplaced closing brace: model.setPersonInfo was inside model.setData
See working demo.

You would need to just use some kind of mapping, for instance ko.mapping. Make sure you bind your view model, start your function in some way and you should be ok.
var DemoPage = (function () {
function DemoPage() {
var _this = this;
_this.PersonInfo = ko.observable({});
_this.setData = function (id) {
// some ajax that returns data
var _data = {
FirstName: 'Frankie',
LastName: 'Jones'
};
_this.setPersonInfo(_data);
//$('#person-modal').modal('show');
};
_this.setPersonInfo = function (data) {
ko.mapping.fromJS(data, {}, _this.PersonInfo());
};
this.setData();
}
return DemoPage;
})();
var demoApp = new DemoPage();
ko.applyBindings(demoApp);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<p data-bind="text: PersonInfo().FirstName"></p>
<p data-bind="text: PersonInfo().LastName"></p>

Related

Passing in array of functions as parameter, for each func in functions run the function

I am trying to pass in an array of functions as a parameter to function x then execute them within the function x. I will also somehow pass in parameters but some of the parameters are only initialised within function x.
Some functions include things like:
_showData(data,type);
console.log(data);
$('#loading').remove();
Here is a sample:
// Called somewhere else
runFunctions([$('.dashboard').remove, $('.screen-loading').remove]);
var runFunctions = function(functions){
// do some things
for (var i = 0; i < functions.length; i++){
functions[i]();
}
Any ideas?
EDIT:
Sorry I just realised the program doesn't know what the objects are because I'm changing scope with an ajax call.
var runFunctions = function(functions){
$.ajax({
method: "POST",
url: "php/database.php",
dataType: "JSON",
data: {type:type},
success: function(data, type){
for (var i = 0; i < functions.length; i++){
functions[i]();
}
}
})
}
What about this:
_accessDatabase(
function(onSuccess){
$('.dashboard').remove();
var type = 'home';
_showData(data,type); // it doesn't know what data is, how can I pass it through?
$('.screen-loading').remove();
}
);
var _accessDatabase = function(onSuccess){
$.ajax({
method: "POST",
url: "php/database.php",
dataType: "JSON",
data: {},
success: function(data){
onSuccess(data);
}
})
}
I want to pass through var data to the onSuccess function, how can I do this?
Solved with:
var _request_successful = function onSuccess (data){
console.log("running onSuccess");
$('.dashboard').remove();
var type = 'home';
_showData(data,type);
$('.screen-loading').remove();
}
_accessDatabase(_request_successful);
var _accessDatabase = function(onSuccess){
$.ajax({
method: "POST",
url: "php/database.php",
dataType: "JSON",
data: {},
success: function(data){
onSuccess(data);
}
})
}
The problem with this code is that the functions that you're calling within the forLoop aren't bound to anything. Take this instead.
// Called somewhere else
runFunctions([
$('.dashboard').remove.bind($('.dashboard'))
, $('.screen-loading').remove.bind($('.screen-loading'))
]);
function runFunctions(functions){
// do some things
for (var i = 0; i < functions.length; i++){
console.log("running")
functions[i]();
}
}
What you could do instead is this:
function call(method, objs) {
objs.forEach(function (obj) {
obj[method]()
})
}
call('remove', [$('.dashboard'), $('.screen-loading')])
Here's a working fiddle: https://jsfiddle.net/ogfgocp4/
To explain a bit how it works, I don't know exactly the internal of JavaScript, but when you do: $('.dashboard').remove, it will return you the remove function. If you call it immediatly, it will be bound to the object which give you the method. If you affect it to something else, then it will be bound to the object it's being called from.
Here's a small snippet of code that explains it well I guess.
var obj = {
fun: function () {
console.log(this)
}
}
var fun2 = {
a: 1
}
//this -> obj
obj.fun()
// this -> window
fun = obj.fun
fun()
// this -> fun2
fun2.fun = obj.fun
fun2.fun()
When you call obj.fun, this will be the object obj. When you affect the method to a var, this then become window as it is the default object in this scope. Then if we finally bind the function to object fun2 and call it immediatly, this is now the object fun2.

method with parameter in unknown js function issues

I'm trying to protect a part of my js code wrapping my code with an Unknown function.
I have edit my function to change
function banana(url) {}
to method
banana: function(url){ },
when I try to call my function banana in another function i try to use
this.banana(url);
but i have this error:
TypeError: this.banana is not a function
Full code:
(function (){
var url_test = "./add_user.php?opt=get_user&token=<?php echo $token; ?>";
if (typeof $.customfnc == 'undefined')
$.customfnc = {}
$.customfnc.get = {
setup: function (){
var url = "google.ca";
this.banana(url);
},
banana: function (url){
console.log("my url: " + url);
};
};
};
// on ready render data
$(document).ready(function() {
$.customfnc.get.setup();
});
})(jQuery);
thanks for your help!
The issue here is that the scope of 'this' is not exactly what you might think it is.
The way I have handled this particular issue in the past is to add
var self = this;
Outside of the object that is attempting to self reference. This may impact how you have set up youre .get() object though.
$.customfnc.get = function(){
var self = this;
self.setup = function (){
//....
self.banana(URL)
}
self.banana = function(url){
//...
}
}

backbone marionette pass variable to view method

I have simple situation and can't understand why variable that I pass to function always undefined.
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
this.renderProgress('4'); //<== pass here
});
},
renderProgress: function (why) {
alert(why); //<== undefined
...
},
...
});
I expect that it equals '4'. In next step I want to pass "data" but now I realize that I can't pass anything.
Since you're invoking renderProgress on the return of $.getJSON you can simply provide the function reference to the done()method of the returned jQuery Promise. Your code would look like this:
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
$.getJSON('service/api.php/projects/' + id + '/progress')
.done(this.renderProgress);
},
renderProgress: function (data) {
alert(data);
...
},
...
});
If you'll need the view context inside renderProgress (like, for example, to refer to a view property), then provide done() a version of renderProgress that's bound to the view context:
$.getJSON('service/api.php/projects/' + id + '/progress')
.done(_.bind(this.renderProgress, this));
where _.bind is an UnderscoreJS function. Read more about it here.
You loose the context in $.getJSON done callback. Try this:
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
var _this = this;
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
_this.renderProgress('4'); //<== pass here
});
},
renderProgress: function (why) {
alert(why); //<== undefined
...
},
...
});
You don't have access to this inside " $.getJSON( " assign this to any variable and then call "renderProgress" method.
var currentObj = this;
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
currentObj .renderProgress('4'); //<== pass here
});
because in your case this points to current object of that function and not to view object.

Javascript Scope and this.Variable

So I have some javascript with the following (pseudo) structure. How do I set the this.last_updated variable of the parent function from the showUpdates function, without specifically referencing the name assignment (my_main_function).
var my_main_function = new main()
function main() {
this.last_updated;
function showUpdates(data){
//set this.last_updated=
// do Stuff
}
this.updateMain(){
$.ajax({
url:"/my_url/"
type:"POST",
datatype:"json",
data: {'last_updated':this.last_updated },
success : function(data) { showUpdates(data)},
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText); },
});
}
}
Updated the code base one the comments:
There are two way of creating objects.
If you need to create the object multiple time you will do it like this:
var YourDefintinon = function() {
};
YourDefintinon.prototype.foo = function() {
};
obj1 = new YourDefintinon();
obj2 = new YourDefintinon();
obj1.foo();
If you only need it once in your code you can just do it like that:
var obj = {
};
obj.foo = function() {
};
foo();
So your would need the main only once your code would look like this:
Using Function.prototype.bind (and its polyfill for older browsers) to bind the showUpdates to the obj.
var main = {
last_updated : null
};
function showUpdates(data){
this.last_updated = data.update_time;
}
main.updateMain = function () {
//<< bind showUpdates to `this` and save the bound function in the local variabel showUpdates
var showUpdates = showUpdates.bind(this);
$.ajax({
url:"/my_url/"
type:"POST",
datatype:"json",
data: {'last_updated':last_updated },
success : showUpdates, //<< uses the showUpdates variable not the function
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText);
},
});
};
As you don't want to make showUpdates accessible to others you could wrap the whole block into a function that is immediatly called:
var main = (function() {
var main = {
last_updated : null
};
function showUpdates(data){
this.last_updated = data.update_time;
}
main.updateMain = function () {
var showUpdates = showUpdates.bind(this);
$.ajax({
url:"/my_url/"
type:"POST",
datatype:"json",
data: {'last_updated':last_updated },
success : showUpdates,
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText);
},
});
};
return main;
}());

Scoping of 'this' in TypeScript

I have a very simple class, but already run into pain with the definition of ‘this’ in Typescript:
Typescript
/// <reference path='jquery.d.ts' />
/// <reference path='bootstrap.d.ts' />
module Problem {
export class Index {
detailsUrl: string;
constructor() {
$('.problem-detail-button').click((e) => {
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: this.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
Javascript
var Problem;
(function (Problem) {
var Index = (function () {
function Index() {
var _this = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(_this).closest('tr');
var problemId = $row.data('problem-id');
$.ajax({
url: _this.detailsUrl,
data: {
id: problemId
},
type: 'POST',
success: function (result) {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
}
});
});
}
return Index;
})();
Problem.Index = Index;
})(Problem || (Problem = {}));
Now the problem is that the line
var $row = $(this).closest('tr'); //this must be that of the callback
and this line
this.detailsUrl, //this must be the instance of the class
conflict in the meaning of 'this'
How do you handle the mixture of the 'this'?
module Problem {
export class Index {
detailsUrl: string;
constructor() {
var that = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: that.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
Explicitly declare that = this so you have a reference for that.detailsUrl, then
don't use a fat arrow for the click handler, so you get the correct this scope for the callback.
You need to fallback to the standard way of javascript. i.e store the variable as :
var self = this;
Then you can use function instead of ()=> and use this to access variable in callback and self to access the instance of the class.
Here is the complete code sample:
module Problem {
export class Index {
detailsUrl: string;
constructor() {
var self = this;
$('.problem-detail-button').click(function(e){
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: self.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
// Creating
var foo:any = {};
foo.x = 3;
foo.y='123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
// Reading
interface Bar{
x:number;
y?:string;
}
var baz:Bar = JSON.parse(jsonString);
alert(baz.y);
And your generated javascript:
var Problem;
(function (Problem) {
var Index = (function () {
function Index() {
var self = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(this).closest('tr');
var problemId = $row.data('problem-id');
$.ajax({
url: self.detailsUrl,
data: {
id: problemId
},
type: 'POST',
success: function (result) {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
}
});
});
}
return Index;
})();
Problem.Index = Index;
})(Problem || (Problem = {}));
var foo = {
};
foo.x = 3;
foo.y = '123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
var baz = JSON.parse(jsonString);
alert(baz.y);
If you're only supporting browsers that have .addEventListener, I'd suggest using that to associate your data with your elements.
Instead of implementing your code, I'll just give a simple example.
function MyClass(el) {
this.el = el;
this.foo = "bar";
el.addEventListener("click", this, false);
}
MyClass.prototype.handleEvent = function(event) {
this[event.type] && this[event.type](event);
};
MyClass.prototype.click = function(event) {
// Here you have access to the data object
console.log(this.foo); // "bar"
// ...and therefore the element that you stored
console.log(this.el.nodeName); // "DIV"
// ...or you could use `event.currentElement` to get the bound element
};
So this technique gives you an organized coupling between elements and data.
Even if you need to support old IE, you can shim it using .attachEvent().
So then to use it, you just pass the element to the constructor when setting up the data.
new MyClass(document.body);
If all the logic is in your handler(s), you don't even need to keep a reference to the object you created, since the handlers automatically get it via this.
I normally bind this to a variable as soon as I have it in the scope I want.
However the this you are after could be found like this:
constructor() {
var class_this=this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var callback_this=e.target;
Late to the thread, but I have something different to suggestion.
Instead of:
var $row = $(this).closest('tr'); //this must be that of the callback
Consider using:
var $row = $(e.currentTarget).closest('tr');
As in this example, anywhere you might want to use this in a jQuery callback, you have access to a function parameter you can use instead. I would suggest that using these parameters instead of this is cleaner (where "cleaner" is defined as more expressive and less likely to be turned into a bug during future maintenance).
module Problem {
export class Index {
constructor() {
$('.classname').on('click',$.proxy(this.yourfunction,this));
}
private yourfunction(event){
console.log(this);//now this is not dom element but Index
}
}
}
check about jquery.proxy().
just remind you there is another way.

Categories