knockout mapping to be done only first time - asp.net-mvc-4

I am generating the knockout mapping from server side view model using below
var bindData2ViewModel = function (data) {
var rdata = ko.toJSON(data);
ko.mapping.fromJSON(rdata, {}, vm.model());
ko.applyBindings(vm);
};
var CustomerViewModel = function () {
var self = this;
self.model = ko.observable({});
return { model: self.model };
};
var vm = new CustomerViewModel();
now there is another call which is giving me the data... i just want to bind that data to the client side viewmodel without changing the binding... how to do that?
var rebindData2ViewModel = function (data) {
var rdata = ko.toJSON(data);
vm.model.set(rdata);
ko.applyBindings(vm);
};
tried above but not working... what is the correct way to do this?

basically to rebind the data to the existing model.. you just need to set the data using the angular brackets.. no need of json etc... because data should itself return as jsonresult
var rebindData2ViewModel = function (data) {
vm.model(data);
};

Related

MVC model binder issues when posting data from knockout

I have a knockout model that I'm trying to post to an MVC4 controller. A simplified version looks like this:
var ItemModel = function (id, name) {
var self = this;
self.id = ko.observable(id);
self.name = ko.observable(name);
};
var Entry = function () {
var self = this;
self.Id = ko.observable();
self.areas = ko.observableArray([]);
};
var EntryModel = function () {
var self = this;
self.entry = new Entry();
self.save = function () {
$.post("/Edit", ko.toJS(self.entry), function (data) {
...
})};
};
};
If I add two areas to my model, like this:
viewModel.entry.areas.push(new ItemModel(1, "A"));
viewModel.entry.areas.push(new ItemModel(2, "B"));
and post it using viewModel.save I get two areas from the model binder, but no data in them (i.e. id = 0, name = "").
After some research I found that I'm posting data like this:
id = 1
name = test
area[0][id] = 1
area[0][name] = "A"
area[1][id] = 2
area[1][name] = "B"
and that MVC expects this:
id = 1
name = test
area[0].id = 1
area[0].name = "A"
area[1].id = 2
area[1].name = "B"
How do I get this posted as expected?
Your model contains the array so you have to stringify your model and send the resultant string to the controller. You can try this:
var EntryModel = function () {
var self = this;
self.entry = new Entry();
//toJSON function produce the JSON string representing entry model
var dataToSend = ko.toJSON(self.entry);
self.save = function () {
$.post("/Edit", dataToSend, function (data) {
...
})};
};
};

Creating a pdf from a Knockout JS view & viewModel

Scenario: In our application a user can create a invoice by filling in certain fields on a Knockout view. This invoice can be previewed, via another Knockout page. I want to use the preview url within our PDF creator (EVOPdf), so we can provide the user with a PDF from this invoice.
To preview the invoice we load the data (on document ready) via an ajax-request:
var InvoiceView = function(){
function _start() {
$.get("invoice/GetInitialData", function (response) {
var viewModel = new ViewModel(response.Data);
ko.applyBindings(viewModel, $("#contentData").get(0));
});
};
return{
Start: _start
};
}();
My problem is within the data-binding when the PDF creator is requesting the url: the viewModel is empty. This makes sense because the GetInitialData action is not called when the PDF creator is doing the request. Calling this _start function from the preview page directly at the end of the page does not help either.
<script type="text/javascript">
$(document).ready(function() {
InvoiceView.Start();
});
</script>
Looking at the documentation of EvoPdf, JavaScript should be executed, as the JavaScriptEnabled is true by default: http://www.evopdf.com/api/index.aspx
How could I solve this, or what is the best approach to create an pdf from a knockout view?
Controller action code:
public FileResult PdfDownload(string url)
{
var pdfConverter = new PdfConverter();
// add the Forms Authentication cookie to request
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
pdfConverter.HttpRequestCookies.Add(
FormsAuthentication.FormsCookieName,
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
var pdfBytes = pdfConverter.GetPdfBytesFromUrl(url);
return new FileContentResult(pdfBytes, "application/pdf");
}
Javascript:
var model = this;
model.invoiceToEdit = ko.observable(null);
model.downloadInvoice = function (invoice) {
model.invoiceToEdit(invoice);
var url = '/invoice/preview';
window.location.href = '/invoice/pdfDownload?url=' + url;
};
The comment of xdumaine prompted me to think into another direction, thank you for that!
It did take some time for the Ajax request to load, but I also discovered some JavaScript (e.g. knockout binding) errors along the way after I put a ConversionDelay on the pdf creator object
pdfConverter.ConversionDelay = 5; //time in seconds
So here is my solution for this moment, which works for me now:
To start the process a bound click event:
model.downloadInvoice = function (invoice) {
var url = '/invoice/preview/' + invoice.Id() + '?isDownload=true';
window.open('/invoice/pdfDownload?url=' + url);
};
which result in a GET resquest on the controller action
public FileResult PdfDownload(string url)
{
var pdfConverter = new PdfConverter { JavaScriptEnabled = true };
// add the Forms Authentication cookie to request
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
pdfConverter.HttpRequestCookies.Add(
FormsAuthentication.FormsCookieName,
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
pdfConverter.ConversionDelay = 5;
var absolutUrl = ToAbsulte(url);
var pdfBytes = pdfConverter.GetPdfBytesFromUrl(absolutUrl);
return new FileContentResult(pdfBytes, "application/pdf");
}
The Pdf creator is requesting this action on the controller, with isDownload = true (see bound click event):
public ActionResult Preview(string id, bool isDownload = false)
{
return PartialView("PdfInvoice", new InvoiceViewModel
{
IsDownload = isDownload,
InvoiceId = id
});
}
Which returns this partial view:
PartialView:
// the actual div with bindings etc.
#if (Model.IsDownload)
{
//Include your javascript and css here if needed
#Html.Hidden("invoiceId", Model.InvoiceId);
<script>
$(document).ready(function () {
var invoiceId = $("#invoiceId").val();
DownloadInvoiceView.Start(invoiceId);
});
</script>
}
JavaScript for getting the invoice and apply the knockout bindings:
DownloadInvoiceView = function() {
function _start(invoiceId) {
$.get("invoice/GetInvoice/" + invoiceId, function(response) {
var viewModel = new DownloadInvoiceView.ViewModel(response.Data);
ko.applyBindings(viewModel, $("#invoiceDiv").get(0));
});
};
return {
Start: _start
};
}();
DownloadInvoiceView.ViewModel = function (data) {
var model = this;
var invoice = new Invoice(data); //Invoice is a Knockout model
return model;
};

function + module.exports

I have a problem with module.export on titanium. I tried following https://wiki.appcelerator.org/display/guides/CommonJS+Modules+in+Titanium but it doesn't work at all.
I have 2 little pieces of code. App.js:
var fenetreBase = Titanium.UI.createWindow({fullscreen:true,backgroundColor:"white",exitOnClose:true});
fenetreBase.open();
var vueimage = new (require('UI/viewimage'))();
vueimage.test();
fenetreBase.add(vueimage);
and viewimage.js in the folder UI.
function viewimage() {
var lavue = Ti.UI.createView({backgroundColor:'red' });
var item =...
lavue.add(item...);
return lavue;
}
viewimage.prototype.test = function() {
Ti.API.info("test");
};
module.exports = viewimage;
I have an error saying
Object #<view> has no method 'test' in app.js vueimage.test()
In my mind, I follow the example of "Instantiable Objects" in the wiki above but I may have not understand something. I expect I made a stupid mistake. I tried many other things, each uglier than other and it doesn't work anyway.
Can somebody tell me where the mistake is?
your mistake is assuming that you have an instance of viewimage when you run:
var vueimage = new (require('UI/viewimage'))();
you are getting an instance of
var lavue = Ti.UI.createView({backgroundColor:'red' });
which doesn't have a test property.
Perhaps you could use an object like this instead:
function viewimage() {
var result = {};
var lavue = Ti.UI.createView({backgroundColor:'red' });
var item =...
lavue.add(item...);
result.lavue = lavue;
result.test = function() {
Ti.API.info("test");
};
return result;
}
EDIT
In your App.js:
var vueimage = new (require('UI/viewimage'))();
vueimage.test();
fenetreBase.add(vueimage.lavue);

Deferring a Dojo Deferred

I'm having a bit of a problem getting a deferred returned from a method in a widget. The method is itself returns a Deferred as it's an xhrPost. The code is as such (using dojo 1.8)
Calling Code:
quorum = registry.byId("quorumPanel");
var deferredResponse = quorum.updateSelectionCount();
deferredResponse.then(function(data){
console.log("Success: ", data);
}, function(err){
console.log("Error: ", err);
});
and the code in the widget:
updateSelectionCount: function() {
var self = this;
var deferredResponse = xhr.post({
url: "ajxclwrp.php",
content: [arguments here],
handleAs: "json"});
deferredResponse.then(function(response) {
var anotherDeferred = new Deferred();
var _boolA = true;
var _boolB = true;
dojo.forEach(response.result, function(relationshipInfo){
[do a bunch of stuff here too set _boolA and/or _boolB]
});
self._sethasRequiredAttr(_hasRequired);
self._setHasRequestedAttr(_hasRequested);
self.quorumInfo.innerHTML = quorumHtml;
// Below is not working
anotherDeferred.resolve('foo');
return anotherDeferred;
});
}
Do I need to set up another promise and use promise/all. Im confused/frustrated at this point.
TIA.
the .then() method returns another deferred. You just need to put a return statement in.
updateSelectionCount: function() {
var self = this;
var deferredResponse = xhr.post({
url: "ajxclwrp.php",
content: [arguments here],
handleAs: "json"});
return deferredResponse.then(function(response) {
var _boolA = true;
var _boolB = true;
dojo.forEach(response.result, function(relationshipInfo){
[do a bunch of stuff here too set _boolA and/or _boolB]
});
self._sethasRequiredAttr(_hasRequired);
self._setHasRequestedAttr(_hasRequested);
self.quorumInfo.innerHTML = quorumHtml;
return "foo";
});
}

Angular http testing

I have a fairly simple controller that gets a simple json list of objects ...
function ProductGroupsCtrl($scope, $http, $routeParams, sharedService, popupService) {
$scope.list = null;
$scope.selectedItem = null;
$scope.selectedItemJsonString = '';
$scope.selectItem = function (item) {
$scope.selectedItem = item;
$scope.selectedItemJsonString = JSON.stringify(item);
//alert(JSON.stringify(item));
};
$scope.closePopup = function () {
$scope.selectedItem = null;
$scope.selectedItemJsonString = '';
};
// sharedService.prepForBroadcast($routeParams.anotherVar);
$http({
method: 'GET',
url: '/ProductGroup'
}).success(function (data) {
$scope.list = data;
}).
error(function (data) {
$scope.message = 'There was an error with the data request.';
});
}
I then try to mock the request in the test class:
var scope, ctrl, $httpBackend, sharedServiceMock = {}, popupServiceMock = {};
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httsypBackend.expectGET('/ProductGroup').
respond([{
ProductGroupID: 5,
MenuTitle: "Promotional Products",
AlternativeText: "Coming soon - a collection of environmentally friendly Promotional Products",
OrdinalPosition: 5,
Active: false
}]);
scope = $rootScope.$new();
ctrl = $controller(ProductGroupsCtrl, {
$scope: scope,
$http: $httpBackend,
sharedService: sharedServiceMock,
popupService: popupServiceMock
});}));
However I receive an error in the testacular window object undefined. What have I done wrong here?
Found the answer. If I remove the error callback function from the $http.get method then it works, i.e. remove the following ...
error(function (data) {
$scope.message = 'There was an error with the data request.';
}
I have to say Angular sure is a steep learning curve for someone who is not a day to day JavaScript programmer (although I seem to be doing more and more). Thanks for the help anyway KatieK :-)