In a page I create an instance of a class (MyClass), this class has 2 methods. But I'd like to do 2 things :
In (1), set the value this.message
In (2), call the information method or another method of the class
Thank,
<script type="text/javascript">
$(document).ready(function () {
var myClass = new MyClass("MyParam");
$('#Target').click(function (event) {
myClass.save("Test");
});
});
</script>
function MyClass(myParam) {
this.myParam = myParam;
this.isError = false;
this.message = "";
}
// Define the class methods.
MyClass.prototype = {
save: function (action) {
**(2)**
},
information: function (action) {
**(1)**
}
};
Update1
When I execute the code below the data value in information is show as undifined
MyClass.prototype = {
click: function (action) {
var myData;
$.post(....., $("form").serialize(),
function (data) {
myData = data;
});
this.isError = this.information(myData);
},
information: function (data) {
alert(data);
return true;
}
};
Inside the save and information functions, this should be the current MyClass object.
So, inside save, this.message should work, and inside information, this.save() should work.
UPDATE:
click: function (action) {
var myData;
$.post(....., $("form").serialize(),
function (data) {
myData = data;
});
this.isError = this.information(myData);
}
$.post is an AJAX request and is ran asynchronously. Meaning that this.isError = this.information(myData); is ran before it finishes, therefore myData is undefined. You need to call this.information from inside the callback.
Inside the callback, this will no longer be your MyClass object, so we need to save a reference to it. Like this:
click: function (action) {
var that = this;
$.post(....., $("form").serialize(),
function (data) {
that.isError = that.information(data);
});
}
Related
I'm currently using the jQuery drag and drop formbuilder plugin. Now, I want to fetch drag and drop elements from my database and add those element to my drag and drop builder.
This is my request function:
function getElements(){
$.getJSON( getContextPath() + "/api/elements/get", function (data){
$.each(data, function(key, val) {
addElement();
});
});
}
Now, I want to add them to the formbuilder instance (I use this example and I use some pre-defined values):
function addElement(){
if (!window.fbControls) window.fbControls = [];
window.fbControls.push(function(controlClass) {
console.log("HERE");
class controlStarRating extends controlClass {
static get definition() {
return {
icon: '🌟',
i18n: {
default: 'Star Rating'
}
};
}
configure() {
this.js = '//cdnjs.cloudflare.com/ajax/libs/rateYo/2.2.0/jquery.rateyo.min.js';
this.css = '//cdnjs.cloudflare.com/ajax/libs/rateYo/2.2.0/jquery.rateyo.min.css';
}
build() {
return this.markup('span', null, {id: this.config.name});
}
onRender() {
let value = this.config.value || 3.6;
$('#'+this.config.name).rateYo({rating: value});
}
}
// register control
controlClass.register('starRating', controlStarRating);
return controlStarRating;
});
}
Unfortunately, my console.log("HERE") is not called, so it seems like it alle stops there. The weird thing is, if I use this as my request function the function is properly executed:
function getElements(){
var allElementsData = ["test"];
// $.getJSON( getContextPath() + "/api/template/get", function (data){
// });
$.each(allElementsData, function(key, val) {
addElement();
});
}
What is the problem?
In the function addElement you are pushing another function into the array window.fbControls. But that second function is only placed in the array not executed. You could for example execute it with this statement: window.fbControls[0](). Then you would see the console.log("HERE")
I have a Grandparent, Parent, Child ViewModel relationship setup in knockout and knockout mapping, CustomerViewModel, WorkOrderViewModel, and RepairViewModel.
I want to setup a child ko.computed value within the child that Take the amount of hours in the RepairViewModel and multiplies it by Rate within the WorkOrderView Model.
Within the RepairViewModel I have code like this:
self.RepairCost = ko.computed(function () {
return (self.Hours() * self.parent.LabourChargeCost()).toFixed(2);
});
Is there any way to get the parent's value?
Thanks so much!
Here is the JS code I'm using (simplified):
var workOrderMapping = {
'WorkOrders': {
key: function (workOrders) {
return ko.utils.unwrapObservable(workOrders.WorkOrderId);
},
create: function (options) {
return new WorkOrderViewModel(options.data);
}
},
'Repairs': {
key: function (repairs) {
return ko.utils.unwrapObservable(repairs.RepairId);
},
create: function (options) {
return new RepairViewModel(options.data);
}
}
};
RepairViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, workOrderMapping, self);
self.RepairCost = ko.computed(function () {
return (self.Hours() * self.parent.LabourChargeCost()).toFixed(2);
})
;
}
WorkOrderViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, workOrderMapping, self);
}
CustomerViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, workOrderMapping, self);
self.save = function () {
//alert(ko.toJSON(self));
$.ajax({
url: "/Customers/Save/",
type: "POST",
data: ko.toJSON(self),
contentType: "application/json",
success: function (data) {
//alert("succ");
//alert(data.customerViewModel);
// if (data.customerViewModel != null) {
//alert("succ2");
new PNotify({
title: 'Saved',
text: 'Record saved successfully',
type: 'success',
styling: 'bootstrap3'
});
ko.mapping.fromJS(data.customerViewModel, workOrderMapping, self);
if (data.newLocation != null)
window.location = data.newLocation;
},
});
};
}
You can't access the parent in the child model unless you pass the parent to the child and kept a reference.
What has worked better for me in the past is passing the value to the child model and then add to subscribe in the parent to update the child when the value is changed.
function Parent(){
var self = this;
self.someValue = ko.obserable();//init if you need to
self.children = [new Child(self.someValue())]
self.someValue.subscribe(function(value){
for(var i = 0;i<self.children.length;i++){
self.children[i].parentValue(value);
}
});
}
function Child(value){
var self = this;
self.parentValue = ko.observable(value);
}
Unfortunately you cannot do that, and it is a limitation not of Knockout but JS: you don't have access to the parent context from within an object property.
What you can do is, as #GrayTower mentioned, pass your parent as a parameter, but this to me, feels a bit like a hack (I admit to using it sometimes though). You could also modify your child viewmodels once they have been initiated, from either within the parent, or externally, before the view model is bound. I don't really understand the flow of properties in the code you presented, but I hope a smaller test case will suit your needs: http://jsfiddle.net/kevinvanlierde/507k237y/. Suppose we have the following parent-child viewmodels:
// symbolizes an employee
function ChildVM(name, hoursWorked) {
var self = this, parent;
this.name = name;
this.hoursWorked = hoursWorked;
}
// symbolizes a payment system
function MasterVM() {
var self = this;
this.rate = 25; // dollars/hour
this.employees = [
new ChildVM('Chris',16),
new ChildVM('Cagle',32)
];
}
var app = new MasterVM(),
view = document.getElementsByTagName('table')[0];
We want to add a property payout to each ChildVM in employees, which will use rate from MasterVM in combination with hoursWorked from ChildVM, i.e. a ko.computed. You could simply paste a function inside the MasterVM constructor, like this:
ko.utils.arrayForEach(self.employees, function(i) {
i.payout = ko.computed(function() {
return i.hoursWorked*self.rate;
});
});
Or you could make it a method and call it before calling ko.applyBindings:
this.initEmployees = function() {
ko.utils.arrayForEach(self.employees, function(i) {
i.payout = ko.computed(function() {
return i.hoursWorked*self.rate;
});
});
}
app.initEmployees();
ko.applyBindings(app, view);
Or you could even build an applyBindings wrapper, which executes a 'callBefore' (cf. <=> AJAX callbacks) before binding the view and the model, like so:
function initVM(VM, callbefore, element) {
callbefore(VM);
ko.applyBindings(VM, element);
}
initVM(app, function(vm) {
ko.utils.arrayForEach(vm.employees, function(i) {
i.payout = ko.computed(function() {
return i.hoursWorked*vm.rate;
});
});
},view);
fiddle
Note: Using ko.mapping.fromJSconverts all values to observables, while when your values don't need updating you don't need observables, you can use plain JS values/ objects.
The Problem
I am picking up Typescript and just learned that lambda functions are used to (edit) set the value of this. However, I'm not sure how to pass my view model's this into a function that calls another method that I have not defined. In my case, I'm trying to call a Knockout method. See example:
Desired JavaScript:
var MyViewModel = (function () {
function MyViewModel() {
var _this = this;
...
this.someMethod = function () {
ko.utils.arrayForEach(this.array1(), function (item) {
while (item.array2().length < _this.array3.length) {
item.array2.push(12345);
}
});
};
...
Actual JavaScript:
var MyViewModel = (function () {
function MyViewModel() {
var _this = this;
...
this.someMethod = function () {
ko.utils.arrayForEach(_this.array1(), function (item) {
while (item.array2().length < this.array3.length) {
item.array2.push(12345);
}
});
};
...
TypeScript:
method = () => {
ko.utils.arrayForEach(this.array1(), function(item){
while(item.array2().length < this.array3().length){
item.array2.push(0);
}
})
}
One Solution...
One solution I've used was to manually set this.array3().length to _this.array3.length(), but that's pretty hacky and I do not like it.
How should I go about passing the right this into my inner function?
You need to use another lambda to continue the chain of this :
method = () => {
ko.utils.arrayForEach(this.array1(), (item) => { // NOTE here
while(item.array2().length < this.array3().length){
item.array2.push(0);
}
})
}
Tips on this in TypeScript : https://www.youtube.com/watch?v=tvocUcbCupA&hd=1
I'm trying to bind the AJAX callback function to a certain scope, what am I doing wrong?
here is my code:
var MainApp = {
files:{
"A":{
url:"files/a.json",
content:""
},
"B":{
url:"files/b.json",
content:""
}
},
init:function () {
this.loadFiles();
},
loadFiles:function () {
for (var i in this.files) {
var f = function (data) {
console.log("callback",this);
};
console.log("binding",this);
f.bind(this);
$.get(this.files[i].url, f);
}
}
};
$(function () {
MainApp.init();
});
f.bind(this);
Function#bind doesn't alter the original function, it returns a new function bound to the parameter. You probably meant:
f= f.bind(this);
Try using call:
that = this;
$.get(this.files[i].url, function() {
f.call(that)
});
I have a setup where I get some information, in an ajax call, then I immediately use some of that information for another call, and then I populate some fields.
The problem is that I am not certain how to create the anonymous function so that it can call this.plantName.
Right now the value of this.plantName is undefined.
I know that there are missing close braces, but I am not going to try to line them up as that will cause confusion, so please ignore the missing close braces.
TabLinks.prototype.plantName = '';
TabLinks.prototype.init = function() {
this.updateTab('PlantDropDown', 'retrievePlants', 'retrieveAllPlants', 'tab3', function() { return this.plantName; });
};
function TabLinks() {
this.updateTab = function(entityname, wsbyid, webservicename, parentContainer, defaultValue) {
$("#" + parentContainer + "link").bind('click', function() {
$.ajax({
type: "POST",
...
success: function(result) {
myData = JSON.parse(result.d);
$.ajax({
type: "POST",
...
success: function(result2) {
...
myelem.value = JHEUtilities.testIsValidObject(defaultValue) ?
defaultValue() :
'';
Update:
Here is the solution that worked, I didn't realize the two returns of functions:
this.updateTab('PlantDropDown', 'retrievePlants', 'retrieveAllPlants', 'tab3',
function() {
var app = this;
return function() {
return function() {
return app.plantName;
}
}()
}
);
So I had to call it thusly:
defaultValue()();
The way I program my way out of this is to make sure that the closure is returned from a function in the scope of the captured variables. Something like so:
function foo(){
var myFoo = 1;
return function (){return function() { myFoo += 1; return myFoo }}()
} // ^^