For instance I have the following model:
var Volume = function (id, path, isactive) {
var self = this;
self.id = ko.observable(id);
self.path = ko.observable(path);
self.isactive = ko.observable(isactive);
self.Save = function (data) {
//ajax post
}
self.Update = function (data) {
//ajax post
}
}
var ViewModel = function (data) {
var self = this;
self.volumes = ko.observableArray(data.volumes.map(function (item) {
return new Volume(item.id, item.path, item.isActive, item.description);
}));
self.AddVolume = function () {
self.volumes.push(new Volume());
}
}
After Save or Update, I want to refresh the parent ViewModel from Volume model, because some values have changed in the database.
How do I reinitialize the ViewModel?
var viewModel = new ViewModel(ko.utils.parseJson(data) || []);
ko.applyBindings(viewModel);
You can have a function in your parent model which loads the new data and populates new data. Then anywhere you need to get the new data you simply call that function.
Example :
var Volume = function (data) {
var self = this;
self.id = ko.observable(data.id);
self.path = ko.observable(data.path);
self.isactive = ko.observable(data.isactive);
self.Save = function (data) {
//ajax post
//whenever you want to load data again you call viewModel.Load();
}
self.Update = function (data) {
//ajax post
//whenever you want to load data again you call viewModel.Load();
}
}
var ViewModel = function () {
var self = this;
self.volumes = ko.observableArray();
self.Load = function (){
//your ajax call or whatever you do to get the data
self.volumes($.map(data.volumes, function (item) {
return new Volume(item);
}
}
self.AddVolume = function () {
obj = {id:"",path:"",isactive:false}
// need to pass data to Volume model
self.volumes.push(new Volume(obj));
}
}
var viewModel = new ViewModel();
viewModel.Load();
ko.applyBindings(viewModel);
I'd suggest you to have save and update functions in your parent model and use $parent array object in order to reference.
I think you dont need to refresh the parent vm, If you need you can changes the particular index of the array from the value after update. Or call the getall method and push all the values after clearing the old values in the array(but it is not recomended). Or you can refresh the page. Choose wisely
Related
i have javascript class and 2 prototype methods
var proEdit = function(){
this.client;
var self = this;
}
/*
class method to load all the project releted details
*/
proEdit.prototype.loadProject = function(){
this.client = $serviceCall.setClient("getAllByProjectID","project"); // method name and service
this.client.ifSuccess(function(data){ //sucess
self.kk();
})
this.client.ifError(function(data){ //falce
console.log("error loading setting data")
})
this.client.projectID($stateParams.projectID); // send projectID as url parameters
this.client.getReq(); // get request if post then use 'postReq('jsonbody')'
return this;
}
proEdit.prototype.kk = function(){
console.log("sass");
}
inside loadProject method, i'm calling an api using callback function to access data and if it is ifSuccess then i want call the kk prototype method. but it gives me this error.
angular.js:12722 TypeError: self.kk is not a function
i try to create new instance to the same class and then call the kk function. then it is working
this.client.ifSuccess(function(data){ //sucess
var in = new proEdit();
in.kk();
})
is it happening because of the callback? how can i prevent this. Thank you
As commented, you should use .bind
Sample
window.name = "Bar"
var MyClass = function(name) {
this.name = name;
}
MyClass.prototype.notify = function() {
console.log(this.name)
}
var m = new MyClass('Foo');
setTimeout(m.notify, 2000);
setTimeout(m.notify.bind(m), 2000);
you can change ths code like this:
var proEdit = function(){
this.client;
// var self = this;
}
/*
class method to load all the project releted details
*/
proEdit.prototype.loadProject = function(){
var that = this;
this.client = $serviceCall.setClient("getAllByProjectID","project"); // method name and service
this.client.ifSuccess(function(data){ //sucess
that.kk();
})
this.client.ifError(function(data){ //falce
console.log("error loading setting data")
})
this.client.projectID($stateParams.projectID); // send projectID as url parameters
this.client.getReq(); // get request if post then use 'postReq('jsonbody')'
return this;
}
proEdit.prototype.kk = function(){
console.log("sass");
}
Hope it helps!
finally found the answer. as mention in the comment, need to use the bind to pass the this to callback
var proEdit = function(){
this.client;
var self = this;
}
/*
class method to load all the project releted details
*/
proEdit.prototype.loadProject = function(){
this.client = $serviceCall.setClient("getAllByProjectID","project"); // method name and service
this.client.ifSuccess((function(data){ //sucess
console.log(data)
$scope.project = data.project[0];
$scope.task = data.task;
$scope.user = data.user;
$scope.balance = data.balance[0];
this.kk();
}).bind(this))
this.client.ifError(function(data){ //falce
console.log("error loading setting data")
})
this.client.projectID($stateParams.projectID); // send projectID as url parameters
this.client.getReq(); // get request if post then use 'postReq('jsonbody')'
return this;
}
proEdit.prototype.kk = function(){
console.log("sass");
}
I am very new to this Angular component communication. I am using Angular 1.5.X version and I am using factory to share data between components. I am facing one issue where Async value of Service Variable refreshes after certain time.
I understand one solution is to set watcher on non scope variable but I think I am missing something important here. Can you guys please share views?
This is Service.js code
Service.$inject = ['$http','$q'];
function Service($http,$q) {
this.$http = $http;
this.$q = $q;
};
Service.prototype.getTileCount = 0;
Service.prototype.getTileData = function(Url){
var deferred = this.$q.defer();
this.$http.get(Url)
.success(function(response){
Service.prototype.getTileCount = response.data.length;
console.log('data length :', Service.prototype.getTileCount);
deferred.resolve(response);
});
return deferred.promise;
};
This is component 1 controller code
function Component1Controller(Service) {
this.tileData ={};
var self = this;
var promise = Service.getTileData(this.sourceUrl);
promise.then(function(data) {
self.tileData = data;
Service.getTileCount = data.length;
console.log('This is tileData : '+ Service.getTileCount);
});
};
This is component 2 controller code
function Component2Controller(Service) {
var self = this;
console.log(Service.getTileCount);
// getting getTileCount = 0; After setting timeout function of 5 second I am able to get getTileCount value
};
The thing is that Service.getTileCount is updated asynchronously, that's why it's 0 at first and then at some point it changes. I would recommend you to simplify your service and always work with getTileData method, which would be a single source of data. The implementation would also become simpler:
function Service($http, $q) {
this._tileData = null;
this.$http = $http;
this.$q = $q;
}
Service.prototype.getTileData = function(Url) {
if (!this._tileData) {
this._tileData = this.$http.get(Url).then(function(response) {
return response.data;
}.bind(this));
}
return this._tileData;
};
Note, how it caches tiles response in "private" _tileData property. Now you can always rely on getTileData method which will return data no matter when you call it:
function Component1Controller(Service) {
this.tileData = {};
var self = this;
Service.getTileData(this.sourceUrl).then(function(data) {
self.tileData = data;
console.log('This is tileData:', self.tileData.length);
});
};
function Component2Controller(Service) {
var self = this;
Service.getTileData(this.sourceUrl).then(function(data) {
console.log('tile count', data.length);
});
};
In this case Service.getTileCount is not needed anymore.
Demo: http://plnkr.co/edit/3zE6VL4emXaLRx2nCRih?p=info
I have an observable Array being populated by an AJAX call but it is not updating.
function ViewModel() {
var self = this;
self.FinanceTypes = ko.observableArray([]);
self.Country = ko.observable('#Model.Country.CountryName');
function FinanceTypeViewModel(data) {
var self = this;
self.Name = data.Name;
self.Tax = data.TaxPercentage;
//self.Accounts = data.AccountTypes;
}
self.getFinanceTypes = function() {
var data = { country: ko.toJS(self.Country()) };
$.getJSON("/Admin/GetFinanceTypes", data, function(result) {
var mapped = ko.utils.arrayMap(result, function(item) {
return new FinanceTypeViewModel(item);
});
ko.utils.arrayPushAll(self.FinanceTypes(), mapped);
self.FinanceTypes.valueHasMutated();
});
}
self.getFinanceTypes();
}
The problem line is
self.FinanceTypes.valueHasMutated();
everything else works. This gives an "Uncaught ReferenceError: Name is not defined"
If I change it to
self.FinanceTypes().valueHasMutated();
I get "Uncaught TypeError: undefined is not a function"
Thanks
Calling ko.utils.arrayPushAll(self.FinanceTypes(), mapped) pushes all the items in mapped into the unwrapped FinanceTypes. Call it without unwrapping it and you won't have to call valueHasMutated: ko.utils.arrayPushAll(self.FinanceTypes, mapped)
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