For example,
I have the page where KO has been already registered and there is a viewmodel with observable property "someProperty";
I check that the "someProperty" is observable property by ko.isObservable(viewmodel.someProperty) - it returns 'true';
I do the ajax call to get some html markup where KO is registered too;
Now If you check the ko.isObservable(viewmodel.someProperty) it will return false;
Also all KO extensions which has been added manually will be lost. It looks like bug (or feature) in jQuery (http://bugs.jquery.com/ticket/10066).
var viewModel = new function() {
var self = this;
this.serverData = {
Controller: ko.observable(null),
Enabled: ko.observable(false),
Id: ko.observable(null),
ParentId: ko.observable(null),
Title: ko.observable(null),
MaterialId: ko.observable(null),
Alias: ko.observable(null)
};
this.treeData = {
tree: ko.observable(null),
node: ko.observable(null)
};
this.submit = submit;
this.cancel = cancel;
this.openMaterials = menuOptions.openMaterials;
}
// ...
var data = ko.utils.createUnobservable(viewModel.serverData);
// ...
(function(ko) {
ko.utils = ko.utils || {};
ko.utils.createUnobservable = function(observable) {
var unobservable = {};
(function() {
for (var propertyName in observable) {
var observableProperty = observable[propertyName];
if (ko.isObservable(observableProperty) /* always 'false' after ajax */) {
unobservable[propertyName] = observableProperty();
}
}
})();
return unobservable;
};
})(ko = ko || {});
You could fix this by saving a copy of the ko global variable before you include the loaded ajax, and then restoring it afterwards.
var savedKo = window.ko;
.... // do the ajax thing
window.ko = savedKo;
Related
I learn to create my own library. This is new for me. I start learn with this library, if you see, I have similar clear function like that library, but my code didn't replace var history.
Init value: var history = [];
Show(): return []
Add ('asd'): history[0] = 'asd';
Show(): return [asd]
Clear: revert var history to empty var history = []
Show(): return [asd] what I expect is []
Here is my code:
;(function() {
'use strict';
var testLib = function() {
var index = -1,
history = [];
return {
add: function(data) {
history[history.length] = data;
},
show: function() {
return history;
},
clear: function() {
var index = -1,
history = [];
}
};
};
window.testLib = testLib;
}());
$(function() {
var mylib = new testLib();
mylib.add('asdasd');
console.log(mylib.show());
mylib.clear();
console.log(mylib.show()); //expect: [] empty array
});
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
Just remove the var in your clear method:
clear: function() {
index = -1;
history = [];
}
You are declaring new variables instead of modifying the ones in your closure. In modern ECMAScript, you might want to consider creating your library as a "class":
class History{
// ...
clear() {
this.history = [];
this.index = -1;
}
}
Just set your variables in the proper scope ;)
First in your sample, you redefine variables in your clear function, by using the keyword var.
Then, from my experience, it is simpler to set everything in the same object and not have private scopes inside your object. This way, you can work in a more flexible way!
So, do not use var, set your properties in the object you return, and use this and it will work fine!
(function() {
'use strict';
var testLib = function() {
return {
index: -1,
history: [],
add: function(data) {
this.history[this.history.length] = data;
},
show: function() {
return this.history;
},
clear: function() {
this.index = -1;
this.history = [];
}
};
};
window.testLib = testLib;
}());
$(function() {
var mylib = testLib();
mylib.add('asdasd');
console.log(mylib.show());
mylib.clear();
console.log(mylib.show()); //expect: [] empty array
});
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
I'm begin transform part of system in my work in Knockout Component, but any time, I need access functions defined in my component.
Example I will create a msgComponent:
In Html:
<body>
<msgComponent params="message: 'blablabla'"></msgComponent>
</body>
In Js:
ko.components.register('msgComponent', {
viewModel: function (params)
{
this.message = ko.observable(params.message);
this.showMessage = function()
{
alert(this.messenge());
}.bind(this);
},
template: '<div data-bind="text: message"></div>'
});
function myMasterViewModel()
{
var master = this;
// How to can I access my component this way?
master.msgComponent = msgComponent;
master.showMessage();
}
Like in my example, how to can I access my component function with other view model?
Fist Edit:
#Dandy
If I need instance more msgComponent in page, I can define this way?
var sharedComponent = function(params){
var params = params || {};
this.message = ko.observable(params.message);
this.showMessage = function()
{
alert(this.message());
}.bind(this);
}
ko.components.register('msgcomponent', {
viewModel: function(params)
{
this.instance = params.viewModel;
},
template: '<div data-bind="with: instance"><div data-bind="text: message"></div></div>'
});
function myMasterViewModel()
{
var master = this;
master.msg = new sharedComponent({message: 'huahua'});
master.show = function()
{
master.msg.showMessage();
};
}
Or have any more correct way to this situation?
You can try the instance method of component registration. Something like
var sharedComponent = function(params){
var params = params || {};
this.message = ko.observable(params.message);
this.showMessage = function()
{
alert(this.message());
}.bind(this);
}
var comShared = new sharedComponent ({message: 'test mssage'});
ko.components.register('msgcomponent', {
viewModel: {instance: comShared },
template: '<div data-bind="text: message"></div>'
});
function myMasterViewModel()
{
var master = this;
// How to can I access my component this way?
master.msgComponent = comShared;
master.msgComponent.showMessage();
}
Read more here.
So I bind my Knockout template as follows:
First ajax, get data then I pass the data can call a function named bindKo:
function bindKo(data) {
var length = data.length;
var insertRecord = {};
if (length > 0) {
insertRecord = data[data.length - 1]; //last record is an empty PremlimViewModel for insert
insertRecord.Add = true;
data.splice(data.length - 1, 1); //remove that blank insert record
}
function prelims(data) {
var self = this;
var model = ko.mapping.fromJS(data, { copy: ["_destroy"] }, self);
self.BidPriceFormatted = ko.computed({
read: function () {
var bidPrice = this.BidPrice();
if (bidPrice) {
if (!isNaN(bidPrice)) {
var input = '<input type="text" value="' + bidPrice + '"/>';
return $(input).currency({ decimals: 0 }).val();
}
}
},
write: function (value) {
value = value.replace(/\D/g, '');
this.BidPrice(value);
},
owner: this
});
return model;
}
var mapping = {
create: function (options) {
return new prelims(options.data);
}
};
function viewModel(prelimData) {
var self = this;
self.prelims = ko.mapping.fromJS(prelimData, mapping);
self.remove = function (prelim) {
self.prelims.destroy(prelim);
};
self.addOption = function () {
var clone = jQuery.extend(true, {}, insertRecord);
self.prelims.push(ko.mapping.fromJS(clone));
};
}
ViewModel = new viewModel(data);
ko.applyBindings(ViewModel);
}
I have a template defined where you can add and remove records, and user does just that:
<script type="text/html" id="PrelimsTemplate">
<!--Template Goodness-->
</script>
Then, ajax call, records updated in datanbase, latest results returned and I do:
ko.mapping.fromJS(newestData, ViewModel)
But this does not work because my ViewModel is complex.
So I would just like to reBind the template entirely. Make is disappear and reappear with latest data.
Wrap your template in a container than you can hook onto with jQuery.
When you need to trash it use ko.cleanNode and jQuery .empty()
emptyTemplate: function(){
ko.cleanNode($('#template-container')[0]);
$('#template-container').empty();
}
Load your template back up
fillTemplate: function(){
$('#template-container').html('<div data-bind="template: {name:\'templateId\', data: $data}"></div>');
ko.applyBindings(data,$('#template-container')[0])
},
See my fiddle
I have binded my json array to knockout by using knockout-mapping plugin
JSON
{
"info":[
{
"Name":"Noob Here",
"Major":"Language",
"Sex":"Male",
"English":"15",
"Japanese":"5",
"Calculus":"0",
"Geometry":"20"
},
{
"Name":"Noob Here",
"Major":"Calculus",
"Sex":"Female",
"English":"0.5",
"Japanese":"40",
"Calculus":"20",
"Geometry":"05"
}
]
}
Binded using knockout-mapping plugin
var data = [];
$.each(data1.info, function (index, element) {
data.push({
English: element.English,
Japanese: element.Japanese,
Calculus: element.Calculus,
Geometry: element.Geometry,
name: element.Name,
major: element.Major,
sex: element.Sex
});
});
dataFunction.prototype = function () {
var getAllItems = function () {
var self = this;
ko.mapping.fromJS(data, {}, self.Items);
};
Now I want to alert the value of English.
I tried alert(this.English()); inside dataFunction.prototype and it doesn't work.
How to alert that value?
JS-Bin code: http://jsbin.com/ipeseq/4/edit
You need to define a proper view model and work from that in your mark-up.
I put together a view model with a custom view model mapping where I map your data into objects I called 'Student' that you can use in your markup. This object I extended with a ko.computed that calculates the total (It is in this object you can read and manipulate your observables).
var Student = function(data) {
var self = this;
ko.mapping.fromJS(data, { }, self);
self.total = ko.computed(function() { // Calculate total here
return self.English() + self.Japanese() + self.Calculus() + self.Geometry();
});
};
var viewModelMapping = { // Map all objects in 'info' to Student objects
'info': {
create: function(options) {
return new Student(options.data);
}
}
};
var ViewModel = function(data) { // Create a view model using the mapping
var self = this;
ko.mapping.fromJS(data,viewModelMapping,self);
}
$(document).ready(function () {
vm = new ViewModel(data);
ko.applyBindings(vm);
});
You can see the resulting JSBin code here
You can read more in the Customizing object construction using “create” and Customizing object updating using “update” sections here
First I'm new to using knockout.
I have bound array1 to my template now I would like change it to use array2 is this possible with knockout?
What I was messing with
var viewModel = function(){
var _this = this;
this.test = [{ name: 'Fruit4'}, {name: 'Vegetables'}];
this.categories = ko.observableArray(this.test);
this.changeItems = function()
{
this.test= [{ name: 'Fruit2'}, {name: 'Vegetables2'}];
categories = ko.observableArray(this.test);
}
};
ko.applyBindings(viewModel());
Create a computed observable that will return one of the two arrays based on your conditions whatever they would be and bind to it. Make sure that the conditions that decide which to choose are also observable so it will update properly.
function ViewModel(data) {
this.array1 = ko.observableArray(data.array1);
this.array2 = ko.observableArray(data.array2);
// change this value to true to use array2
this.chooseArray2 = ko.observable(false);
this.array = ko.computed(function () {
return this.chooseArray2()
? this.array2()
: this.array1();
}, this);
}
<div data-bind="foreach: array">
...
</div>
Of course the logic could be more complex than that. To be more manageable, I would make the condition observable computed as well and create the logic in there. The computed observable that returns the array wouldn't have to change much.
function ViewModel(data) {
this.array1 = ko.observableArray(data.array1);
this.array2 = ko.observableArray(data.array2);
// which to choose depends on a number of conditions
this.someCondition = ko.observable(false);
this.anotherCondition = ko.observable(true);
this.someNumber = ko.observable(132);
this.chooseArray2 = ko.computed(function () {
// some complex logic
if (this.someNumber() < 0) {
return this.someCondition();
}
return this.someCondition() || !this.anotherCondition();
}, this);
this.array = ko.computed(function () {
return this.chooseArray2()
? this.array2()
: this.array1();
}, this);
}