Durandal / Knockout - Creating viewModel from server data prior to bindings - javascript

Sorry as this questions is mainly Knockout, but is in Durandal so the viewModel and some bindings my not look familiar to ko anwser contributors.
Basically my view is comprised of a table that renders server data:
view.html
<table class="table">
<tr>
<td>Company</td>
<td data-bind="text: Company"></td>
</tr>
<tr>
<td>First Name</td>
<td data-bind="text: FirstName"></td>
</tr>
<tr>
<td>Last Name</td>
<td data-bind="text: LastName"></td>
</tr>
</table>
I am on ASP.net so of course I use signalR to get my server data which represents each value in the table. This is an asynchronous call so it does not block the Durandal Composition callback which will do the Knockout bindings.
viewModel.js
define(['services/logger', 'global/session', 'jquery', 'knockout', 'knockout-mapping', 'hubs'],
function ($, ko, komapping) {
ko.mapping = komapping; // Needed
var myChildModel = function (Id) {
this.activate = function () {
$.connection.hub.start().done(function () {
con.server.getDetails(Id).done(function (data) {
ko.mapping.fromJS(data, {}, this);
console.log(data);
});
});
};
};
return myChildModel;
});
I REALLY want to take the server return and map it into the viewModel itself as shown above as I can then account for changes in the data on the server-side automatically (not included or relevant at this point), but as the viewModel does not exist prior to the binding callback
Causing the expected error:
Unable to process binding "text: function (){return Company }"
Message: Company is not defined; View: widgets/customerInfo; ModuleId:
widgets/customerInfo
To negate this issue I want to stop the Knockout applyBindings, then call it manually on my server return .done call back.
The Durandal docs do say you can stop the bindings, but does not include an example how or whether it is possible to have it apply the bindings manually when you want.
Durandal Docs
Has anyone been able to deploy this technique before, maintaining the viewModel comes from the server, and is not created then hydrated after, or has deployed a similar technique with the same outcome.

This seems like a trivial problem to solve but why not just add a conditional if above it?
<table class="table" data-bind="if: isLoaded">
<tr>
<td>Company</td>
<td data-bind="text: Company"></td>
</tr>
<tr>
<td>First Name</td>
<td data-bind="text: FirstName"></td>
</tr>
<tr>
<td>Last Name</td>
<td data-bind="text: LastName"></td>
</tr>
</table>
and just set isLoaded to false until your data is loaded async?

Related

Best way to sort page disposition on client side

I'm working on a MVC project in which I display a ViewModel in several partial views within a table. I have a control in which the user can change how the table is displayed, like this:
<table>
<tr>
<td>
partial view 1 partial view 2
</td>
</tr>
<tr>
<td>
partial view 3 partial view 4
</td>
</tr>
</table>
or
<table>
<tr>
<td>
partial view 1
</td>
</tr>
<tr>
<td>
partial view 2
</td>
</tr>
<tr>
<td>
partial view 3
</td>
</tr>
<tr>
<td>
partial view 4
</td>
</tr>
</table>
Any time a user wants to change how the view is displayed, he has to click the submit button of the from, and the application has to make the request to the server, get the results, and according to a flag (TableDisposition = 1 or 2), load some divs.
I know this is not the best way to do it, but my lack of knowledge regarding client side coding (javascript/jquery) is making it impossible for me to do this in a more efficient way.
Any input will be much appreciated.
EDIT: Solved in the comments.
First I started loading the viewModel in two tables, each one in a div with absolute positioning. Then I made a function to hide one of those tables:
$(function () {
$("#divTable1").css('visibility', 'hidden');
});
Finally, I made a function which is triggered by pressing a button:
$("#btnAlternateView").click(function () {
if ($("#divTable2").css('visibility') == 'hidden') {
$("#divTable2").css('visibility','visible');
$("#divTable1").css('visibility', 'hidden');
}
else {
$("#divTable2").css('visibility','hidden');
$("#divTable1").css('visibility', 'visible');
}
});
And that was it. Works like a charm.

jQuery With Mustache js Issue

I'm using jQuery and Mustache Js in a project.
I have the template in a script tag. The template requires some dynamic/variable html based on some data. Hence I load the template using jQuery, manipulate it, and then write it back into the script tag.
However, loading the template into a div(for manipulation), using jquery seems to alter the structure of the template (it moves things around), which invalidates the template.
How do i update the template dynamically and avoid this issue? (Any help / Pointers / etc)
Original Template
<table>
<thead>
<tr>
<td>FN</td>
<td>SN</td>
<td>OT</td>
</tr>
</thead>
<tbody>{{#Users}}
<tr class="tr-border" data-row-id="{{DCSID}}">
<td class="td-border">{{FN}}</td>
<td class="td-border">{{SN}}</td>
<td class="td-border">{{OT}}</td>
</tr>{{/Users}}</tbody>
</table>
Loading with jquery into another div causes this below(moves {{#Users}} {{/Users}} out of place)
{{#Users}} {{/Users}}
<table>
<thead>
<tr>
<td>FN</td>
<td>SN</td>
<td>OT</td>
</tr>
</thead>
<tbody><tr class="tr-border" data-row-id="{{DCSID}}">
<td class="td-border">{{FN}}</td>
<td class="td-border">{{SN}}</td>
<td class="td-border">{{OT}}</td>
</tr></tbody>
</table
The code below is how I'm going about it. Also this is a js fiddle link here
var $newDcsTemplate = $('<div/>').html($('#dcs-template').html()); // for manipulation
var original1 = $('#dcs-template').html()
var original2 = document.getElementById('dcs-template').innerHTML;
// manipulate the template here and replace content of script tag with it.
console.log(original1); // works fine
console.log(original2) // works fine
console.log($newDcsTemplate.html()) // messed up the template
Do it like this, with .html() and without </script>:
<table>
<thead>
<tr>
<td>FN</td>
<td>SN</td>
<td>OT</td>
</tr>
</thead>
<tbody>
<!-- {{#Users}} -->
<tr class="tr-border" data-row-id="{{DCSID}}">
<td class="td-border">{{FN}}</td>
<td class="td-border">{{SN}}</td>
<td class="td-border">{{OT}}</td>
</tr>
<!-- {{/Users}} -->
</tbody>
</table>
Your template is being modified by the DOM because you're inserting it into a <div> as "HTML", which it isn't. It's doing exactly what a browser would do if you gave it that markup. It's putting the {{# Users }} in the nearest available valid place to put it.
This is the same reason your template is originally stored in a <script> tag. You should always use <script> tags to hold your templates, and you should always use .text() to read and write them:
var $newDcsTemplate = $('<script/>').text($('#dcs-template').text());
console.log($newDcsTemplate.text())

Knockout stops updating observable after unpacking property

Just noticed some unexpected behaviour in knockout.js - Got some code that loops round an observableArray and repeats some bound HTML elements for each item in the array. One of the items is a property on a sub-object:
<tbody data-bind="foreach: Contact">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: Project().Name"></td>
<td data-bind="text: Percentage"></td>
</tr>
</tbody>
This renders fine on page load. But if the user performs actions that end up changing the Contact array or the items inside it, Name and Project().Name update but Percentage does not, even though stepping through shows it has the correct value.
However, if I remove the unpacked sub-object:
<tbody data-bind="foreach: Contact">
<tr>
<td data-bind="text: Name"></td>
<td></td>
<td data-bind="text: Percentage"></td>
</tr>
</tbody>
Everything works perfectly.
What's going on here, and is there a fix better than using a computed observable or somesuch to calculate and hold my Project().Name value?
try this
<td data-bind="text: ko.computed(function() { return Project().Name() })"></td>
if you need 2-way binding, you can use writable computed

Choose set of TDs conditionally with Angular

Say I have:
<tr ng-repeat="row in rows">
<td>{{row.field1}}</td>
<td>{{row.field2}}</td>
<td>{{row.field3}}</td>
<td>{{row.field4}}</td>
<td>{{row.field5}}</td>
<td>{{row.field6}}</td>
</tr>
Now I want a conditional where I pick either field1-3 or field4-6.. So right now it's:
<tr ng-repeat="row in rows">
<td ng-if='row.isFirst'>{{row.field1}}</td>
<td ng-if='row.isFirst'>{{row.field2}}</td>
<td ng-if='row.isFirst'>{{row.field3}}</td>
<td ng-if='!row.isFirst'>{{row.field4}}</td>
<td ng-if='!row.isFirst'>{{row.field5}}</td>
<td ng-if='!row.isFirst'>{{row.field6}}</td>
</tr>
Which, when I have many columns is annoying... I'd like to somehow have the conditional outside the actual "td" Like:
<tr ng-repeat="row in rows">
<% if row.isFirst { %>
<td>{{row.field1}}</td>
<td>{{row.field2}}</td>
<td>{{row.field3}}</td>
<% else { %>
<td>{{row.field4}}</td>
<td>{{row.field5}}</td>
<td>{{row.field6}}</td>
} %>
</tr>
kinda like underscore... But it seems that angular doesn't support outside conditionals only conditionals on elements. Any ideas?
I am going to give you the “angular way” or the MVVM way, not exactly the "technical trick" you may be expecting.
Angular is an MVVM framework and therefore all your “logic” should be in the controller or the services but not in the View(html).
What you are trying to achieve is to display:
Model1 ={ Column1: ‘a’, Column2: ‘b’, Column3: ‘c’}
Or
Model2 ={ Column4: ‘d’, Column5: ‘e’, Column6: ‘f’}
If you think in terms of the models and the view you will find that is much simpler separating and performing these actions in your controller and having a lighter view which is also in the nature of the MVVM way to resolve this.

select value of td and download content of selected tds

I have this table:
<table class="results" id="summary_results">
<tr>
<td>select all</td>
<td>name</td>
<td>id</td>
<td>address</td>
<td>url</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>john doe</td>
<td>1</td>
<td>33.85 some address</td>
<td>http://www.domain.com</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>jane doe</td>
<td>2</td>
<td>34.85 some address</td>
<td>http://www.domain2.com</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>sam</td>
<td>3</td>
<td>33.86 some address</td>
<td>http://www.domain3.com</td>
</tr>
</table>
I would like to select all the rows, then download the content of the URLs knowing that each URL is linked to the ID. For example the first url will be www.domain.com?id=1&report=report.
ok now I got the select to work but it is taking only the value of the first tr and not the other selected ones.
I think the main issue here is that client-side javascript isn't really the most appropriate method of going about what you seem to be trying to achieve. Parsing the URLs from the table and forming AJAX requests with them isn't in itself hugely tricky, see for example this jsfiddle. However as noted by wsanville in the comments, there are cross domain restrictions on AJAX that restrict the ability to make calls to arbitrary URLs.
What would be a more feasible method, if you don't want to navigate off the page, is to retrieve the URLs in the method used in the jsfiddle, and then post that list to your own server. Then you will be able to retrieve the URLs data on the server and perform more complicated actions before sending the data back to the user. Something along the lines of:
$.ajax({
url:'/load_urls', //this is assuming you have some routing for the load_urls endpoint
data:urls, //assuming you have the list of urls in a var called 'urls'
method:'POST',
success:function(data){
//do something with your data
},
error:function(err){
console.log(err); //it broke
}
});
I think overall you should maybe take a step back and re-evaluate exactly what you want to happen, and look at the constraints of the URLs you are going to be retrieving and so on, and then after that if you're still at a loss come back with a more specific question. Good luck!

Categories