EmberJS - modifying model before render and resetting during update - javascript

The question may be a little ambiguous. Added a JsBin example below.
http://emberjs.jsbin.com/julimila/4/
I have two questions.
ONE:
As you can see in the example, for some reason Ember doesn't like properties starting with uppercase characters. In the example, Subject doesn't get rendered but subjectInLowercase does.
The API that I request data from always send data in this uppercase notation and I have no control over it. Currently I'm working around this by passing the data to a method that converts all the properties to camelCase. Fortunately, the server doesn't care if I send the data back in camelCase. How do I fix this?
TWO:
I need to modify some properties in the data before rendering them. In the example, I added a new property called formattedDate. This is the modified version of the Timestamp field. I usually use a Handlebars helper to do this formatting but this time, it is an input field. I don't want this formattedDate property going back to the server when the model is updated. Is there any better way than updating their relevant properties in the model and then manually deleting these extra fields.

You can put your format date logic in the controller since controller is a proxy for model.
App.IndexController = Ember.ObjectController.extend({
getFormattedDate: function(){
return moment(this.get('model.Timestamp')).format('DD/MM/YYYY');
}.property('id'),
actions: {
updateForm: function() {
var data = this.get('model');
console.log(data);
// Post this model back to the server
}
}
});
And in the view use
Formatted Date: {{input value=getFormattedDate}}

Related

How do I pass an Eloquent model to a Knockout.js view model constructor?

I have a solution (see below), but it's awkward and klunky & I'd like to hear how others have solved this problem.
Here's a simple example to illustrate the problem:
Imagine I have an Eloquent User model and I'm putting together an "edit user" page that uses Knockout to handle value-to-DOM-element bindings.
In my controller method for this page, I'm doing the following:
public function getEditUser($id = null)
{
return View::make("account.edituser")
->with("user", User::find($id))
->with("groups", Group::all());
}
where the Group model is for my access control groups. The user can be a member of zero or more groups and the relationship is set up in the normal way for Eloquent models.
In the template, I have a bunch of elements (text, select, radio buttons, etc.) set up with data-bind attributes to bind my Knockout view model to the elements.
In the <script> portion of the page, I have the following:
var createUserViewModel = function (user, groups) {
return {
id: ko.observable(user.id),
name: ko.observable(user.name),
groups: ko.observableArray(user.groups),
// ...other attributes go here
// the list of available groups
availableGroups: ko.observableArray(groups)
};
};
// instantiate the view model
var viewModel = createUserViewModel(
// note that I'm using Smarty for my templating system. The
// lines below apply the json_encode method and disable the
// htmlspecialchars function which I have set up to encode
// all output by default
{$user|json_encode nofilter},
{$groups|json_encode nofilter}
);
ko.applyBindings(viewModel);
The user argument to the createUserViewModel function will be JSON-encoded, so it will look like this: { id: 1234, name: "Joe Smith", groups: ['group1', 'group2'], ...}
This is a simple example: I have several forms for entering other information which are significantly more complicated than this.
There are a couple of problems with this:
As I said, it's awkward. The source generated by this is difficult to read for larger models.
I suspect there's an XSS vulnerability here, since I'm echoing data via json_encode directly into the body of the script element. I haven't been able to exploit this, but I think someone else could find a way.
But I can't think of another way to do it that doesn't radically change the way the application works (ie. use AJAX calls to retrieve the data).
Can anyone share a better way to do this?
Re (1) you need to look at the very useful mapping which is designed for exactly this - http://knockoutjs.com/documentation/plugins-mapping.html ... this will replace the createUserViewModel function for you, and save you having to manually turn a JSON object into something observable.
Re (2) I'm not expert enough with security and XSS to give an authoritative answer, but it looks OK to me. So long as the user and group data was sanitised before you stored it, then it should be OK to echo it back as JSON. If you're not providing a textbox or other input for a user to type into that then affects this data then there's no angle for an attacker to inject a malicious script. Plus JSON-encoding itself is quite a good sanitisation, in that any JavaScript will just end up as a string. So to be sure, given:
{$user|json_encode nofilter}
will print out some JSON, you could immediately try JSON.parse on it:
var user = JSON.parse({$user|json_encode nofilter})
before you then do anything with it in your script.

Checking for any dirty Backbone model data within collection

I have a requirement to "nag" a user about unsaved changes when they switch between different Backbone collection models (by clicking on a table row). I've googled for "check backbone model dirty data" (for instance) and not found anything definitive.
I accomplished this using underscore's "some" and isEqual functionality, in a manner such as the following, "some()" being sufficient to determine if there are any un-saved changes (as opposed to what those precise changes might be), in particular because the model attribute is actually an array of objects.
var anyDirty = _.some(myCollection.models, function(model) {
return !_.isEqual(model.get('nodes'), model.previousAttributes()['nodes]);
});
I am new to Backbone and am wondering if this is an accepted sort of approach for adhoc checking for dirty model data. Or, does Backbone provide some sort of built in functionality for this purpose, that my initial attempts at googling did not reveal?
I have another attribute I need to monitor in addition to 'nodes', so I'm switching to using changedAttributes(): http://backbonejs.org/#Model-changedAttributes:
var anyDirty = _.some(myCollection.models, function(model) {
return model.changedAttributes();
});
What may make this an imperfect solution is that it seems like it will return an object of changedAttributes even if the attribute got changed back to it's original value. So it almost seems that what I need in the long run is to take a snapshot of the original data and compare against that. Still though, using model.changedAttributes() is a more concise alternative to what I first posted.

Pass data to dynamically created template with meteor

Following up on this queston/answer (second answer) Dynamically loading templates in Meteor.js
I have set up a helper in the current displayed template and i am returning the template dynamically. This works great.
Template.template1.helpers({
dynamicView: function(){
return Template['dynamic_template']();
}
})
This ends up showing my html in template 1:
Questions:
How do i pass data to this Template as it's being created, can i do something like this:
Template['dynamic_template'](data1, jsonData2);
I want the dynamicView helper in template1 to also be dynamic in the sense that it can have a different template based on some other criteria. Can i use a Session here and change the dynamicView return statement to something like this:
dynamicView: function(){
return Session.get('dynamicTemplate');
}
and somewhere else Session.set('dynamicTemplate', Template['dynamic_template']()); This works, but is it recommended. It's just a string but i am concerned about performance problems and the size of my template being passed reactively
OK I guess ill have to split my answer up:
With 1.
The templates are compiled handlebars templates so you just need to provide a context to them so the handlebars data can be filled up:
data = {name1:value1, name2:value2}
return Template['dynamic_template'](data);
So that {{name1}} and {{name2}} get filled up with value1 and value2 respectively.
With 2.
Yes that should work, you can pass off any data that will give off HTML as a result. For a very detailed videocast have a look at the EventedMind screencast on template functions: http://www.eventedmind.com/posts/meteor-rendering-template-functions
The template system's use case might not be this exactly. You might want to use Meteor.render instead, but it depends on what you want to do.
Despite the Session name, its just a reactive javascript variable so it should be fine with larger strings too to the same that would be used in an ordinary javascript variable

KnockoutJS: Update/Insert data to a viewModel using mapping

I've been trying to figure this out for quite some time now. I couldn't find anything that addresses this problem, but please correct me if I'm wrong.
The problem:
I have data from a JSON API comming in, with an nested array/object structure. I use mapping to initially fill the model with my data. To update this, I want to extend the model if new data arrives, or update the existing data.
As far as I found out, the mapping option key, should do this trick for me, but I might have misunderstood the functionality of the mapping options.
I've boiled down the problem to be represented by this example:
var userMapping = {
key: function(item) {
return ko.utils.unwrapObservable(item.id);
}
};
// JSON call replaced with values
var viewModel = {
users: ko.mapping.fromJS([], userMapping)
};
// Should insert new - new ID?
ko.mapping.fromJS([{"id":1,"name":"Foo"}, {"id":2,"name":"Bar"}], userMapping, viewModel.users);
// Should only update ID#1 - same ID?
ko.mapping.fromJS([{"id":1,"name":"Bat"}], userMapping, viewModel.users);
// Should insert new - New ID?
ko.mapping.fromJS([{"id":3,"name":"New"}, {"id":4,"name":"New"}], userMapping, viewModel.users);
ko.applyBindings(viewModel);​
Fiddle: http://jsfiddle.net/mikaelbr/gDjA7/
As you can see, the first line inserts the data. All good. But when I try to update, it replaces the content. The same for the third mapping; it replaces the content, instead of extening it.
Am I using it wrong? Should I try to extend the content "manually" before using mapping?
Edit Solution:
I solved this case by having a second helper array storing all current models. On new data i extended this array, and updated the view model to contain the accumulated items.
On update (In my case a WebSocket message), I looped through the models, changed the contents of the item in question, and used method valueHasMutated() to give notice of changed value to the Knockout lib.
From looking at your example code the mapping plugin is behaving exactly as I would expect it to. When you call fromJS on a collection you are effectively telling the mapping plugin this is the new contents of that collection. For example:
On the second line, How could it know whether you were updating or whether you had simply removed id:2?
I can't find any mention of a suitable method that treats the data as simply an update, although you could add one. Mapped arrays come with some helpful methods such as mappedIndexOf to help you find particular items. If you receive an update data set simply loop through it, find the item and update it with a mapping.fromJS call to that particular item. This can easily be generalized into reusable method.
You can use ko.mapping.updateFromJS() to update existing values. However, it does not add new values so that would be a problem in your instance. Take a look at the link below for more details.
Using updateFromJS is replacing values when it should be adding them
Yes, you should first collect all data into a list or array and then apply the mapping to that list. Otherwise you are going to overwrite the values in your viewModel.

Dynamically Add JS to an ASP.NET page and get results from the JS

Here's my issue:
Client(s) give me separate JS files which will run a check of some sort on the user's system (Browser type, are cookies enabled?, etc.) and a list of acceptable values to be returned from this check.
I want to run through each JS file when a user visits the site and compare the results to the list of acceptable values, then alert the user if they pass these requirements or not.
I'm currently using RegisterClientScriptBlock() to add the JS to the client's page, so it's being run, but I'm having issues getting the result value from the JS back to ASP.NET in order to do the comparison.
I've tried using hidden fields that the JS will dump the value to and ASP.NET will read from, but I'm having difficulty generating the hidden fields on demand (since I have no idea how many Js files the client could have) and have them work in the ASP.NET code.
Any help, or suggestions in the right direction would be awesome, thanks!
What I would do is have the results be an array of KeyValuePair objects that you would then serialize to JSON. So you create the javascript object type like so:
function KeyValuePair(key, value){
this.Key = key;
this.Value = value;
}
Then you would build up an array of KeyValuePairs like so:
//This array is declared in the global scope
var ValueArray = new Array();
function someFunction(){
//this assumes that the key and value variables are created earlier in the function
var valueToStore = new KeyValuePair(key, value);
ValueArray[ValueArray.length] = valueToStore;
}
So at the point when you are done with all your checks you would use the json2 serializer to serialize the array to json for storage in your hidden field.
var jsonToSaveToHiddenField = JSON.stringify(ValueArray);
//Logic to store resulting json and trigger the serverside evaluation here
On the server side you would use JavascriptSerializer to deserialize your json to an array of KeyValuePairs. Here is the msdn doc on that: JavaScriptSerializer Class Reference
So with this approach you only need one hidden field. So you don't need to dynamically create it which should simplify the server side retrieval quite a bit.
The above should work with minimal changes however I haven't run this through a compiler so there might be some minor syntax errors preset.

Categories