Persisting knockout ViewModel between ASP.NET WebForms server side posts ... - javascript

Using this article posted on CodeProject.com, "Using KnockoutJS in your ASP.NET applications" as a guide I am attempting to create a reusable, data-loaded drop down list using ASP.NET 3.5 Web Forms, but that leverages KnockoutJS for client-side data-binding. Multiple, independent instances of this drop down should be able to live independently in the same page.
So far the CodeProject.com post has been invaluable in directing me on how to set things up and I am successfully passing the updated ViewModel data back and forth between the server and client as a JSON string and converting it to and from an object (both on the server and client). What I am hanging up on is what should be the simple part; binding the ViewModel to the drop down list!
So I begin by loading the JSON string into a hidden field. It includes a list of Regions and a single SelectedRegion.
<input type="hidden" id="ddlRegionKO_hdnRegionListVMStorage" value="{"Regions":[{"RegionName":"Mid Atlantic USA","RegionId":2},{"RegionName":"Mid West USA","RegionId":10},{"RegionName":"North Central USA","RegionId":5},{"RegionName":"North East USA","RegionId":1},{"RegionName":"North West USA","RegionId":7},{"RegionName":"Other","RegionId":9},{"RegionName":"South Central USA","RegionId":6},{"RegionName":"South East USA","RegionId":3},{"RegionName":"South West USA","RegionId":8}],"SelectedRegion":{"RegionName":"North Central USA","RegionId":5}}" />
I then run convert this string into a Javascript Object using the ko.utils.parseJson() function.
var stringViewModel = document.getElementById("ddlRegionKO_hdnRegionListVMStorage").value;
var ddlRegionKO_pnlRegionDDLContainer_ViewModel = ko.utils.parseJson(stringViewModel);
Then I convert the property definitions into ko.observable and ko.observableArray methods (this is one of those sections that will be need to be refactored, but as a proof of concept it suffices).
//
// Convert all the model properties to KO Propety/Methods
for (var propertyName in ddlRegionKO_pnlRegionDDLContainer_ViewModel) {
switch(propertyName.toUpperCase())
{
//
// Multiple Region objects are stored as an array in the regions property.
case "REGIONS":
ddlRegionKO_pnlRegionDDLContainer_ViewModel[propertyName] = ko.observableArray(ddlRegionKO_pnlRegionDDLContainer_ViewModel[propertyName]);
break;
//
// Only a single region may be selected at any time.
case "SELECTEDREGION":
ddlRegionKO_pnlRegionDDLContainer_ViewModel[propertyName] = ko.observable(ddlRegionKO_pnlRegionDDLContainer_ViewModel[propertyName]);
break;
};
};
Given this, I would expect the drop down list to be populated and the SelectedRegion selected when the applyBindings method is called ...
ko.applyBindings(ddlRegionKO_pnlRegionDDLContainer_ViewModel);
I have put this all together at JSFiddle ... here ... I suspect that I may be overlooking something but I cannot see what it might be. Everything looks right to me.
If anybody can see something that I am overlooking I would be extremely grateful!
Thanks,
-g

You don't need to specify the model name in your bindings. Instead of options:ddlRegionKO_pnlRegionDDLContainer_ViewModel.Regions, just use options:Regions, etc.
<select id="ddlRegionKO_ddlRegionList"
data-bind="options:Regions,
optionsText:'RegionName',
optionsValue:'RegionId',
value:SelectedRegion,
optionsCaption:'Choose Region ...'">
</select>
Working fiddle
Edit: You were also missing an optionsValue binding that specified which property you want to be bound to the value of each option. I updated the fiddle to include this change.
Edit 2: Well, your selected region in your json is an object. I looked over the knockout documentation about binding and I didn't see a way to bind a selected value to an object, so if it's possible for you to modify the json, you can just specify the id of the selected value.
<input type="hidden"
id=".."
data-bind="..a bunch of array stuff... ,"SelectedRegion":5}"
/>
See what I did there? I replaced the object
'SelectedRegion':{'RegionName':'North Central USA','RegionId':5}
with just:
'SelectedRegion':5
Updated fiddle is here. But this won't help with your textbox situation because it will show the ID instead of the text in your textbox. It's a bit late here so I'm not really sure how to fix that off hand, but you might look here for some inspiration. Good luck.

Related

Knockout: Propper way of Option-Binding with Model-Instances

By the nature of instances, new anObject({id: 1}) != new anObject({id: 1}).
This leads me to a problem regarding Knockout:
I have an array of possible options (all instances of an model with different property-values) and another model which helds a selection.
From a UI-perspective, I have a simple <select data-bind="options: [...]-binding, which works fine as long as I select an option.
Because my ViewModel can get stored and later recalled in a new applyBinding, I get into the problem of my data-bind not recognizing my selected value and consequentially removing the value.
Now my simplest solution is some sort of initialisation-function, which loops through the options and selects the right model-instance through an id-comparison. After I have the correct instance, I then can apply it to the "selectedValue"-property.
I didn't tried it out yet, but I don't see how it wouldn't work.
Because I don't think that this a strange requirment and a lot of people are using Knockout - I was hoping there was some nicer way of doing this?
Thanks!
Take a look at the Knockout.js documentation for "optionsValue": http://knockoutjs.com/documentation/options-binding.html
Typically you’d only want to use optionsValue as a way of ensuring
that KO can correctly retain selection when you update the set of
available options. For example, if you’re repeatedly getting a list of
“car” objects via Ajax calls and want to ensure that the selected car
is preserved, you might need to set optionsValue to "carId" or
whatever unique identifier each “car” object has, otherwise KO won’t
necessarily know which of the previous “car” objects corresponds to
which of the new ones.

Backbone.js Modifying data in model before save

I was wondering how I would go about converting data when I call the set or save methods on a model. Specifically converting the inputted date string to epoch time.
I know I could just convert it in the view, but as far as I know, that wont work very well with my validations.
The model code is here if you are interested.
Thanks for any help!
What I can gather you have two options:
1 Convert them in your view
This means you can roll your own conversions for the view or use something like Backbone.modelbinder to make the conversions for you. Then you have to modify your validate method to accept an epoch date. Personally I would prefer this one, I think that it's suitable for the UI to handle verifying user input's well-formedness and conversion to the right unit and let the model handle validating if the values are within the accepted limits.
2 Convert them in your model
Backbone doesn't offer this out-of-the-box. If you set something to be something, there is no easy way to convert it to something else, especially between validate and set. Basically your best bet would be to roll your own set -function with something like
// Inside the set function
...
if (!this._validate(attrs, options)) return false; // real line in the set func
// insert something like this, after validate you know the values are eligible for conversion
attrs = this.convert(attrs); // a custom func that converts attributes to right units
...
// set continues as usual
Hope this helps!
You can overwrite the sync method in your model:
, sync: function(method, model) {
if(method === 'update'){
// change you model here
}
}
This will be invoke bevor data is send to the backend server. The "method" indecates 'create' or 'update'.
According to the sources, validate is the only callback that is called before set and save. You can to set the values in your validate method directly on the attributes object. Unfortunately you cannot make any changes to attributes at this point.
You can use a plugin like backbone.getters.setters to do this since it looks like it won't be a feature added to backbone.

Knockout Mapping Plugin - Capture Array Value

I have a JSON array coming from a REST API. I am using the Knockout mapping plugin to process the array and load the JSON into preset form values (if a user has added values to the form previously - I have data there to test the Knockout arrays). The form essentially adds or deletes div blocks with inputs so users can add/delete "work" experiences.
My trouble is with trying to decipher how the plugin maps the arrays. I am trying to locate a specific value (the id) of a row in the array so I can add it as a variable to tell the API to delete that specific row. I can get Knockout to explicitly output the row value in the html, but I can't figure out how to capture it otherwise. In the template "foreach" I have a button that references a "remove:" and that's where I'm stuck in trying to capture the value from the array.
For Example in the HTML:
This outputs the two rows of the "work" object no problem:
<span data-bind="text: ko.mapping.toJSON(workModel.work())"></span>
[{"id":"1","schoolID":"2","place":"","position":"Science Teacher","description":"I worked at ASD for 1 year as a Science teacher.","start":"2011","end":"2012","profileID":"91"},{"id":"2","schoolID":"1","place":"American School of Taiwan","position":"Science Guy","description":"I was just another science guy","start":"2008","end":"2011","profileID":"91"}]
This outputs the id of the first row and item in the array:
<span data-bind="text: ko.mapping.toJSON(workModel.work()[0].id)"></span>
"1"
But in the javascript, if you click on the remove button generated by the foreach template...
gone = function(work) {
alert(ko.mapping.toJSON(workModel.work(this).id));
}
Gives me this error in Firebug, and then the UI reloads and drops out the template block I just clicked on.
Unable to parse bindings. Message: TypeError: workModel.work()[0] is undefined; Bindings value: text: ko.mapping.toJSON(workModel.work()[0].id)
Even though, if I replace the above alert with the explicit statement:
gone = function(work) {
alert(ko.mapping.toJSON(workModel.work()[0].id));
}
I get the correct value of "1" again. I know it has to do with the "this" aspect of the code, but I'm not sure what the mapping plugin is doing so that I can capture the specific value from the array...make sense? Any help would be greatly appreciated.
I'm going out on a limb here, but I do think it's the this-problem yes. Scoping in Javascript can be a hassle sometimes. Try doing something like this in the scope containing the gone-function:
var self = this;
gone = function(work) {
alert(ko.mapping.toJSON(workModel.work(self).id));
}
Disclaimer: I'm not able to test this myself right now, but give it a try :)
I finally got it. It came from combining different post on Stack Overflow and also from the Knockout forums. I'm sure other folks have more elegant solutions than this, but it works for me.
In the foreach loop on the "Delete" (or whatever button you want to use to capture the value) button I included the following on the data-bind:
Remove
Then in the javascript I have:
var self = this;
var row_id;
self.remove = function(index){
var row_id = index;
alert(row_id);
}
The alert returns the row ID of the loaded JSON as I wanted. The $data.id() could be changed/used to return any mapped element from the loaded JSON. The row_id is then a global that can be accessed elsewhere as well.

Is there any documentation about how Revision.Description is populated and under what condition?

Is there any documentation about how Revision.Description is populated and under what condition?
I'm writing a Custom Application for Rally so that I can view changes made to Task and HierarchicalRequirement objects via a table with a rolling 7 day period.
The attributes that I'm interested in are:
HierarchicalRequirement
PlanEstimate
TaskEstimateTotal
TaskActualTotal
TaskRemainingTotal
Task
Estimate
ToDo
Actuals
I'm traversing Revisions to get snapshot views of tasks and stories:
It's easy to retrieve these attributes for the current day. However, I need to traverse RevisionHistory -> Revisions and then parse the Revision.Description to apply the differences for Task and HierarchicalRequirement objects. This may provide a daily snapshot of each object.
For example: the following were appended to Revision.Description after took place:
TASK REMAINING TOTAL changed from [7.0] to [4.0]
TASK ESTIMATE TOTAL changed from [7.0] to [4.0]
The "rolling 7 day" period is just an example. My intention is to create a table with a breakdown of Team -> Story -> Task -> Estimate -> ToDo along the y-axis and Iteration -> daily-date along the x-axis.
Tim.
The Revision.description field on many of the Rally object types was not originally intended for developers to get change information but rather for display purposes for our Rally ALM SaaS tool - that's why changes are put in a Revision attribute called 'description' which is just a text field. So there is no developer documentation on the format of this data since it is a text field and not intended to be parsed and the format could change in the future (in the future there will be a better way to get object change information. More on this later in this post...)
However, there is a pattern in this data. It is:
ATTRIBUTE_NAME action VALUE_CLAUSE
The actions are 'added' or 'changed'.
The value clause format is based on the action type. For the 'added' action the value clause is [value]. For the 'changed' action the value clause is 'from [old value] to [new value]'.
For example, for an existing User Story that had an owner set to 'Newt' from 'No Entry', a new revision instance is created the description would have this contained in it:
OWNER added [Newt]
If then later the user changed the owner to 'John', then a new revision will be created that looks like this:
OWNER changed from [Newt] to [John]
If there is more than one attribute change then the changes are separated by commas and there is no guaranteed sorting order of the changes.
Now for the better way to do this in the future. Since you are not the only developer that wants to get at object changes we have a new product under development that will have WSAPI endpoints exposed where you can get changes for an object in a programatic way that should avoid you needing to parse data. But since this product is under development you'll have to do what you are doing now and hopefully my explanation of the format of the data in the description will help you in the meantime.
Hope this helps.
The data you are seeking may also exist in the IterationCumulativeFlowData or ReleaseCumulativeFlowData objects in Rally's WSAPI:
https://rally1.rallydev.com/slm/doc/webservice/
That should be easier (and perform better) than grepping through all the revision history entries.

Related Parameters in HTML

I have a table of rows and columns on an HTML-based entry form that allows the user to edit multiple records. Each row corresponds to a database record and each column to a database field.
When the user submits the form, the server needs to figure out which request parameter belongs to which row. The method I've been using for years is to prefix or suffix each HTML input element's name to indicate the row it belongs to. For example, all input elements would have the suffix "row1" so that the server would know that request parameters whose names end with "row1" are field values for the first row.
While this works, one caveat of the suffix/prefix approach is that you're adding a constraint that you can't name any other elements with a particular suffix/prefix. So I wonder if there's a better, more elegant approach. I'm using JSP for the presentation layer, by the way.
Thanks.
I don't know JSP very well, but in PHP you would define your input fields' names with an array syntax.
<input name='person[]'>
<input name='person[]'>
<input name='person[]'>
When PHP receives a form like that, it gives you an array (within the standard $_POST array), thus:
$_POST['person']=array('alice','bob','charlie');
Which makes it very easy to deal with having as many sets of fields as you want.
You can also explicitly name the array elements:
<input name='person[teamleader]'>
<input name='person[developer1]'>
would give you an array with those keys. If your current prefixes are meaningful beyond simply numbering the records, this would solve that problem.
I don't know whether the identical syntax would work for JSP, but I imagine it would allow something very similar.
Hope that helps.
Current user agents send back the values in the order of the fields as presented to the user.
This means that you could (theoretically) drop the prefix/suffix altogether and sort it out based on the ordering of the values. You'd get something like
/?name=Tom&gender=M&name=Jane&gender=F&name=Roger&gender=M
I don't know how your framework returns that, but many return it as lists of each value
name = [Tom, Jane, Roger]
gender = [M, F, M]
If you pop an element off of each list, you should get a related set that you can work with.
The downside to this is that it relies on a standard behavior which is not actually required by the specification. Still... it's a convenient solution with a behavior that won't be problematic in practice.
When browsers POST that information back to the server, it is just a list of parameters:
?name_row1=Jeff&browser_row1=Chrome&name_row2=Mark&browser_row2=IE8
So really, I think you can answer a simpler question: how do you relate keys in a key-value list?
Alternatively, you can go to a more structured delivery method (JSON or XML), which will automatically give you a structured data format. Of course, this means you'll need to build this value on the browser first, then send it via AJAX (or via the value of a hidden input field) and then unpack/deserialize it in the server code.
XML:
<rows>
<row><id>1</id><name>Jeff</name><browser>Chrome</browser></row>
<row>...</row>
</rows>
or JSON:
[{ "name":"Jeff", "browser":"Chrome"}, { "name":"Mark", "browser":"IE8" }]
There are many resources/tutorials on how to do this... Google it. Or go with the ostensible StackOverflow consensus and try jQuery.

Categories

Resources