knockout cross view model - javascript

var barcodeNum = ko.observable("");
VelocityMeetings.scan = function (params) {
var errorMessage = ko.observable("");
var viewModel = {
errorMessage: errorMessage,
scannumber: ko.observable(""),
errorVisible: ko.computed(function () {
return errorMessage().length != 0;
}),
scanBarcode: function () {
//Capture image with device and process into barcode
capturePhoto();
this.scannumber(barcodeNum());
//this.errorMessage(errMessage);
},
};
return viewModel;
};
I have the barcodeNum variable created outside of the view model, to try and pass data back into the scannumber variable. How do I access a variable defined inside of a view model?
The goal is to use the javascript Worker I have, to update the scannumber which will update my app accordingly, but I can't get it to function properly.
function receiveMessage(e) {
barcodeNum("Test function");
}
var DecodeWorker = new Worker("js/BarcodeScanner.js");
DecodeWorker.onmessage = receiveMessage;
The goal is something along the lines of this
VelocityMeetings.scan.viewModel.scannumber(barcodeNum());
but this isnt working properly

When you find yourself working with separate view models that have to communicate with each other, consider using knockout-postbox. You can make the communication one-way or two-way if you want. In your case, I think a one-way communication will be enough.
var barcodeNum = ko.observable('').publishOn('barcodeNum');
var viewModel = {
scannumber: ko.observable().subscribeTo('barcodeNum'),
// ...
};

I found the issue my self
scannumber: ko.observable(""),
scannumber can be defined as ko.computed with the return value being barcodeNum(), that will make any change made to barcodeNum also made to scannumber()
here is my computed
scannumber: ko.computed(function () { return barcodeNum(); },this),

I have been through this problem a lot of times before.. Then I decided to write an article on same...
You can refer to this article : http://www.wrapcode.com/knockoutjs/communication-between-multiple-view-models-in-knockoutjs-mvvm-the-right-approach/
I have explained how to deal with multiple view models and separate instances of multiple view models in this article..
Hope others will find it helpful :-)

Related

Knockout pureComputed Not Working

I'm having a ridiculous time trying to handle addresses using Knockout. My structure is something like:
viewModel.buildings()[0].Address()...
Where Address is:
var Address = function () {
var self = this;
self.cAddr1 = ko.observable("");
self.cCity = ko.observable("");
...
self.cDisplay = ko.pureComputed(function () {
return self.cAddr1() + '<br>' + self.cCity() + ...;
}
self.AddressActions = new AddressActions();
}
Everything seems to work ok. Each building has an address and the observables are updated properly. The cDisplay also works correctly.
I am wanting to add another computed/observable/whatever that will call a function that is part of AddressActions when the address changes. I tried this, but the console.log never even gets hit which doesn't make any sense to me:
var Address = function () {
// Same as above...
...
self.triggerAddressVerify = ko.pureComputed(function () {
console.log('here');
self.cAddr1(); self.cAddr2(); self.cCity(); self.cState(); self.cZip();
self.AddressActions.VerifyAddress(self);
}
}
Any ideas why this isn't working?
Result
So I'm still new to knockout.js (obviously) but it works a little bit differently than I thought. I basically used the accepted answer but wrapped everything into a pureComputed. Here's what I ended up adding:
var Address = function () {
...
self.addressChangeEvent = ko.pureComputed(function () {
return self.cAddr1() + self.cAddr2() + self.cCity() self.cState() + self.cZip();
}
self.addressChangeEvent.subscribe(function () {
self.AddressActions.VerifyAddress(self);
}
}
it looks like you're looking for subscribe rather than computed
self.cAddr1.subscribe(function(){
self.AddressActions.VerifyAddress(self);
});
you can add a subscription for each variable you need an event for
The Knockout documentation for pure computed specifically says to not use it when you intend to perform some action (side effects).
You should not use the pure feature for a computed observable that is meant to perform an action when its dependencies change.
You can use a regular computed instead.
self.triggerAddressVerify = ko.computed(function () {
console.log('here');
self.cAddr1(); self.cAddr2(); self.cCity(); self.cState(); self.cZip();
self.AddressActions.VerifyAddress(self);
});
But note that this will run once initially as well as on future changes. If you only want to perform an action on future changes, your approach of subscribing to a pure computed is better.

AngularJS - Using Model in Controller causing Model to update

I have an Angular application where in I'm pulling from a model some data which is saved on the load of the app. For simplicity sake, I've explicitly defined the data which is being pulled.
The issue I have is that in one of my controllers I am running a function on load of the controller which modifies the data pulled from the model. The point is that I want that extra data for that page which is using that controller only. I don't want that data to be saved back into the model (which is what's happening).
My model:
'use strict';
(function () {
var PotsMod = function ($log, _) {
return {
pots: [
{"comp" : "comp1"},
{"comp" : "comp2"}
],
getPots: function () {
return this.pots;
},
};
};
angular
.module('picksApp.models')
.factory('PotsMod', PotsMod);
})();
My controller:
(function () {
function AdmCtrl($log, $routeParams, PotsMod) {
var vm = this;
vm.pots = PotsMod.getPots();
vm.init = function() {
// populate pot.competition
_.forEach(vm.pots, function(pot) {
pot.comp = "test";
});
console.log(PotsMod.getPots());
}
vm.init();
}
angular
.module('picksApp.controllers')
.controller('AdmCtrl', AdmCtrl);
})();
The final line in vm.init(), PotsMod.getPots(), returns to me the updated model, with the values of "comp" as test.
So I tried this instead - I put the debug line under vm.pots like so:
var vm = this;
vm.pots = PotsMod.getPots();
console.log(vm.pots);
vm.init = function() {....
This also returns to me the array where the object values are test...
So I tried one final thing and added an extra debug line in the vm.init() function too:
var vm = this;
vm.pots = PotsMod.getPots();
console.log(vm.pots);
vm.init = function() {
// populate pot.competition
_.forEach(vm.pots, function(pot) {
console.log(pot.comp);
pot.comp = "test";
});
console.log(PotsMod.getPots());
}
vm.init();
The result of this confuses me... The output in the console reads:
[{"comp":"test"},{"comp","test"}]
comp1
comp2
[{"comp":"test"},{"comp","test"}]
I must be missing something here because I don't understand how it can be defining a variable using a model's value, printing that variable with the updated values, then using the old values and printing them, then printing the updated values again from the model (even though nothing in this code touches the model).
Any help would be brilliant please, I see to be making a fundamental mistake somewhere. Thank you.
You're referencing the service's pots object in your controller, so your controller code is also modifying the service's code.
I created a Plunker to demonstrate how angular.copy() creates a deep copy of your service's 'pots', and thus your controller's model is no longer referencing the original.
In your case, all you need to change is vm.pots = angular.copy(getPots());
http://plnkr.co/edit/jg5mWIWds1KMJd51e3o5?p=preview

AngularJS - Share scope value between controllers

I have a textarea in my HTML like this:
<textarea ng-model="commentBox"></textarea>
To access this i simply use "$scope.commentBox" in my controller. But my question is, how do i access the same commentBox within a different controller?
I do have a factory/service setup for this purpose, but i cant figure out how to get the commentBox value in there, for my other controller to use it.
In my factory i have an object var saved = {} and I want to add a property called "comment", with the value of whatever is inside the textarea. Like this saved.comment = commentbox And then access that value from the other controller.
I'm still new at Angular and tried to send the scope information in a parameter to the facory.
var saved = {};
factory.addComment = function (commentbox) {
saved.comment = commentbox
}
Then have my controller send the scope information on a button click,
$scope.testFunction = function () {
myFactory.addComment($scope.commentBox);
}
But yeah, that did not work out.
Note that i need this to work within the factory, and not by using another ng-controller in the HTML.
You need to return your saved var in your factory
var saved = {};
factory.addComment = function (commentbox) {
saved.comment = commentbox
}
return saved
Here there is an example using a service
app.service('fakeService', function() {
var savedData = {};
var addComment = function(newComment) {
savedData.commnet = newComment;
};
var getComment = function(){
return savedData.comment;
};
return {
addComment: addComment,
getComment: getComment
};
});
To inject a factory/service in your controller
app.controller('fakeController', ['$scope','yourFactory',function ($scope, yourFactory) {
console.log(yourFactory.comment) // here you should log saved comment
}])
Then in your controller, you can inject the factory/service and access to saved data. But remember, is your refresh your page, data will be lost, so, to avoid that, you should persist data on your DB.

How do I use fetched backbone collection data in another view?

Trying to learn Backbone and hitting a stumbling block when trying to fetch data, I fetch the data fine from with my view SearchBarView but once the data has been fetched I don't know how I can get this data in my SearchResultsView in order to template out each result?
Sorry if this sounds a little vague, struggling to get my head around this at the moment so could do with the guidance!
SearchBarView
performSearch: function(searchTerm) {
// So trim any whitespace to make sure the word being used in the search is totally correct
var search = $.trim(searchTerm);
// Quick check if the search is empty then do nothing
if(search.length <= 0) {
return false;
}
// Make the fetch using our search term
dataStore.videos.getVideos(searchTerm);
},
Goes off to VideoSearchCollection
getVideos: function(searchTerm) {
console.log('Videos:getVideos', searchTerm);
// Update the search term property which will then be updated when the url method is run
// Note make sure any url changes are made BEFORE calling fetch
this.searchTerm = searchTerm;
this.fetch();
},
SearchResultsView
initialize: function() {
// listens to a change in the collection by the sync event and calls the render method
this.listenTo(this.collection, 'sync', this.render);
console.log('This collection should look like this: ', this.collection);
},
render: function() {
var self = this,
gridFragment = this.createItems();
this.$el.html(gridFragment);
return this;
},
createItems: function() {
var self = this,
gridFragment = document.createDocumentFragment();
this.collection.each(function (video) {
var searchResultView = new SearchResultView({
'model': video
});
gridFragment.appendChild(searchResultView.el);
}, this);
return gridFragment;
}
Now I'm not sure how I can get this data within SearchResultView, I think I need to trigger an event from somewhere and listen for the event in the initialize function but I'm not sure where I make this trigger or if the trigger is made automatically.
Solution 1
If dataStore is a global variable then
SearchBarView
dataStore - appears like a global variable
videos - a collection attached to global variable
then in
SearchResultsView
this.listenTo(dataStore.videos, 'sync', this.render);
Solution 2
If dataStore is not a global variable
getVideos: function(searchTerm) {
console.log('Videos:getVideos', searchTerm);
// Update the search term property which will then be updated when the url method is run
// Note make sure any url changes are made BEFORE calling fetch
this.searchTerm = searchTerm;
var coll=this; //this should refer to the collection itself
this.fetch().done(function(){
var searchResultView = new SearchResultsView({collection:coll});
searchResultView.render();
});
},
It is not 100% clear how you are initializing your SearchResultView.
But, in order to have reference to the collection, can't you simply pass in the reference to the constructor of the view. Something like this:
// In your SearchbarView
var myCollection = new Backbone.Collection(); // and you are populating the collection somewhere somehow
var searchResultView = new SearchResultView(myCollection) // you just pass this collection as argument.
myCollection.bind("change", function(){
searchResultView.parentCollection = myCollection;
}
And inside your searchResultView you just refer this collection by parentCollection for instance.
If you make it more explicit as in how these 2 views are connected or related, I may be able to help you more. But, with given info, this seems like the easiest way.

Data binding issue with knockout and revealing module pattern

I am having trouble getting data binding to work with Knockout when using revealing module pattern.
my javascript is like this
var HMS = HMS || {};
$(function () {
HMS.PatientModel = function () {
this.Patient_Name = ko.observable();
this.Patient_Address = ko.observable();
};
HMS.PatientViewModel = function () {
var patient = ko.observable(),
loadPatient = function () {
patient = new HMS.PatientModel();
patient.Patient_Name("Premkumar");
};
return {
patient: patient,
loadPatient: loadPatient
};
} ();
HMS.PatientViewModel.loadPatient();
ko.applyBindings(HMS.PatientViewModel);
});
I am unable to get the data binding to work with patient name properly. The HTML div tag has data-bind="text:patient.Patient_Name".
Please refer to the code in jsFiddle http://jsfiddle.net/stprem/pp9ym/1/. I would appreciate if you could tell me what I am doing wrong in data binding.
In your loadPatient function you are replacing the patient variable with a new object, but your module already returned a reference to the original observable. So, updating it in this way will not update what the object returned.
Here is an option: http://jsfiddle.net/rniemeyer/pp9ym/6/
Basically, you keep patient as an observable and then update it in your loadPatient function. In your view, using the with binding can help you protect against your object being null, in case you want to load it after you call ko.applyBindings.

Categories

Resources