How do I handle form submission in ember.js? - javascript

I have a form with various controls. When the submit button is pushed an ajax request is sent to the server which answers with some json data I want to display properly. This is a one-off thing, no bindings, etc needed, the data is read-once and discarded afterwards. I can think of some ways to do this combining views and jquery but what is the proper way to do this in Ember.js?
More specifically:
1) How do I communicate the form parameters from the view to the controller that is going to handle the submission event?
2) If I were to create a route to represent the submitted form state how do I serialize the parameters into a route path that makes sense for Ember? Is that even possible?

Since no one answered yet, i have created a fiddle showing how i would to this.
This is the basic approach:
I would setup a controller with a fresh (== empty) model.
Use bindings to synchronize the values of form elements to the model of the controller.
Create a action that takes the updated model and does whatever you want with it (this replaces the traditional form submit).
So the approach is fundamentally different from the traditional way of handling forms this way:
There is no HTML form element, since it is not needed.
The data is not submitted automatically to the server, instead you would send/submit it manually via javascript logic. Imho this is an advantage as you could perform additional logic before or after submitting the data to the server.
This plays nicely with REST-API approaches like ember-date or ember-epf :-)
The example shows a form (just conceptually, as there is no HTML form element) to enter a first and last name. The entered values are synced to the model and you can can "perform a submit".
The JS code:
App = Ember.Application.create({});
App.Person = Ember.Object.extend({
firstName : "",
lastName : ""
});
App.IndexRoute = Ember.Route.extend({
model: function(){
return App.Person.create()
},
setupController : function(controller, model){
controller.set("model", model);
}
});
App.IndexController = Ember.ObjectController.extend({
submitAction : function(){
// here you could perform your actions like persisting to the server or so
alert("now we can submit the model:" + this.get("model"));
}
});
The template showing the use of value bindings:
<script type="text/x-handlebars" data-template-name="index">
<h2>Index Content:</h2>
{{input valueBinding="model.firstName"}}
{{input valueBinding="model.lastName"}}
<button {{action submitAction target="controller"}}>Pseudo Submit</button>
<p>{{model.firstName}} - {{model.lastName}}</p>
</script>

Related

Access property of an object of type [Model] in JQuery

So in my MVC project I have a custom Model called Survey, which contains a handful of properties. In the Survey Controller, I am saving Survey to a Session variable, so that the values of the survey's properties are persisted per session.
I want to be able to manipulate the DOM of a View based on the values of the session survey's properties. But I'm having trouble with how to access those.
I did find this relatively recent question that seems very similar but doesn't have an answer: Cannot access properties of model in javascript
Here's what I have so far: In the View I am getting the session's survey like so:
<input type="hidden" name="activeS" value="#HttpContext.Current.Session("Survey")" />
Then in the Section Scripts at the bottom I have this script to get that value and do something with it:
#Section Scripts
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(function () {
var survey = $("[name=activeS]").val();
$("[name=mddbccu][value=" + survey.mddbccu + "]").prop('checked', true);
})
</script>
End Section
If I insert "alert(survey);" after "var survey..." It does give me an alert that displays the type that the survey object is. So it looks like the survey is being retrieved fine. But if I try "alert(survey.mddbccu);" the alert simply says "undefined".
Note that the line after that ("$([name=mddbccu]...") I know works - having previously set a variable to a specific value, using that the appropriate item is checked. But in attempting to get the value of this particular property of the survey, nothing is checked.
So how do I get the values of the survey's properties here? Thank you!
Your approach would work with some hackery and workarounds but it is not in the spirit of MVC. Here is how you could accomplish it in the MVC way. Basically you move all the heavy lifting (parsing the item from the session) 0 to the controller and store the results in a ViewModel. This keeps the logic out of the view and makes for much cleaner and easier to maintain code.
If you have a ViewModel:
public ActionResult Survey()
{
SurveyViewModel model = new SurveyViewModel();
Survey surveySession = HttpContext.Current.Session("Survey") as Survey; // youll have to do extra null checks and such here
// map other properties from the survey object retrieved from the session to your viewmodel here!
model.mddbccu = surveySession.mddbccu;
model.otherProperty = surveySession.otherProperty
return View(model);
}
If you are just using the Survey object as the model inside the view then its even simpler:
public ActionResult Survey()
{
Survey model = HttpContext.Current.Session("Survey") as Survey;
return View(model);
}
Then, MVC magically selects stuff for you depending on what you have set in the controller. If you are using #RadioButtonFor(m => m.mddbccu, "three") then the radio will be selected if the value "three" was put into the property mddbccu in the controller.

How to select option by ID from parent controller in AngularJS

Summary
What's a clean way of updating a select element in a child controller scope so that the selected item's ID matches an ID in a parent scope?
Details
I have two controllers:
OrderController
CustomerController
OrderController loads an Order and shows it on an order form.
CustomerController is the scope for a subform within the OrderController form. It shows a list of all customers, with the default customer being the one associated with the order. The user can edit the details of the selected customer or add a customer right from the subform.
Possible Solutions
I've thought of two so far, but neither seems very good.
Include a list of all customers in the JSON passed to the Order $resource. That won't work because the user needs a full separate controller to update customers on the subform.
Fire an event when the customers have loaded within CustomerController. OrderController handles that event, updating the select based on its order's customer_id property. That seems better but still hacky.
To communicate between two controllers you would usually broadcast/emit, e.g:
(Coffeescript)
CustomerController:
Api.Customer.get(
id: $scope.customer_id
).$promise.then (data) ->
$scope.$emit 'event:updateOptionId', id
OrderController:
$scope.$on 'event:updateOptionId', (event, id) ->
$scope.customer_id = id
I don't know the structure of your app but this is passing the new customer id to order controller based on an action in the customer controller. If you post some of your code you may get a more thorough answer.
I found that Angular does handle the update automatically. The problem was that my server was returning a one-item array for the order data. So the customer_id of the order property wasn't going into $scope.order.customer_id; it was going into $scope.order[0].customer_id. Correcting that on the back end solved the issue.

backbone.stickit and html-form: How to save (patch) only changed attributes?

tl;dr
How to use backbone.stickit with a html form to change an existing model fetched from the server and only PATCH the changed attributes (changed by user input within the html form) to the server?
/tl;dr
I'm using backbone.stickit in a backbone.js application to bind a model to a HTML-form which is part of a backbone view. This works fine so far, but it becomes a little bit complicated if I'm going to save the bound model. This is because I want to use the PATCH-method and only send the changed attributes to the server. I try to illustrate what I've done so far:
Fetching the model from Server
user = new User(); //instatiate a new user-model
user.fetch(); //fetching the model from the server
console.log(user.changedAttributes()); // Returns ALL attributes, because model was empty
The last line indicates my problem, because I thought I can used the changedAtrributes() method later to get the attributes which need a patch on the server. So I tried this workaround which I found here
user.fetch({
success: function (model, response, options) {
model.set({});
}
});
user.changedAtrributes(); //Returns now "false"
Do stickit-bindings
Now I render my view and call the stickit() method on the view, to do the bindings:
//Bindings specified in the view:
[...]
bindings: {
"#username" : "username"
"#age" : "age"
}
[...]
//within the render method of the view
this.stickit();
The bindings work fine and my user model gets updated, but changedAttributes() remain empty all the time.
Save the model to the server
If the user has made all required changes, the model should be saved to the server. I want to use the PATCH method and only send the changed attributes to the server.
user.save(null, {patch:true}); //PATCH method is used but ALL attributes are sent to the server
OR
user.save(user.changedAttributes(),{patch : true});
With the second approach there are different outcomes:
if I didn't use the user.set({}) woraround, all attributes get PATCHED to the server
if I use the user.set({}) woraround the return value of changedAttributes() is "false" and all attributes are PUT to the server
if I call a user.set("age","123") before calling save(), then only the age attribute is PATCHED to the server
So outcome 3 is my desired behaviour, but there are 2 problems with this: First stickit doesn't seem to use the set() method on the model to update the attributes if they are changed within the html-form. And second, if you call set() with one attribute and afterwards with another, only the second attributes is returned by changedAttributes().
Maybe I just overseen something in the backbone or backbone.stickit docs, so I didn't get the desired behaviour working. Any ideas about that?
NOTE: As found out the problem wasn't directly related to backbone.stickit, more to backbone itself.
Solved this problem on my own, maybe this helps someone who may stumble upon this question:
Backbone only keep track of unchanged attributes, but not of unsaved attributes. So with
model.changedAttributes();
you will only get the attributes of the model, which was changed since the last
model.set("some_attribute","some_value")
Finally I stumbled upon backbone.trackit which is a backbone.js plugin maintained by the creator of backbone.stickit. With this plugin you can track unsaved attributes (all attributes which have changed since the last model.save()) and then use them in the save-method of a model. Example (my usecase):
Backbone.View.extend({
bindings: {
"#name" : "name",
"#age" : "age"
},
initialize: function () {
this.model = new User();
this.model.fetch({
success: function (model, response, options) {
//this tells backbone.stickit to track unsaved attributes
model.startTracking();
}
});
},
render: function () {
this.$el.html(tmpl);
this.stickit();
return this;
},
onSaveUserToServer: function () {
//first argument: only unsaved attributes, second argument: tell backbone to PATCH
this.model.save(this.model.unsavedAttributes(), { patch: true });
});
});

How to handle ajax in knockout viewmodels?

I am using knockout in my project. I have multiple viewmodel, each viewmodel have its own save function implemented in it. Now whenever user clicks on save button the viewmodel data post to the server, i want to block the save button until the response came back from server.
Currently i am handling this by creating an extra observable property saving in each viewmodel. So when user click over the save button i am setting saving observable to true and in callback i am setting it to false. And i have bind this saving property with the button using knockout disable binding.
But i feel that this approach is not good and it contains the following big drawbacks:
For this i have to add an extra property in each viewmodel.
I have to add multiple line of code like setting it to true and again set it to false.
The approach is not centralize, the code for this approach is scattered.
So i want to know is there any other better way to handle this, a plugin or some standard way ??
Edit
Just to clarify, my question has nothing to do with asp.net postback, the question is how i can handle efficiently the ajax, like block the save button, displaying the response message etc
??
This is generally what makes a viewmodel a viewmodel. In a pattern like MVC, your controller shouldn't really have any idea what your view looks like, what controls it has, or what it's state is, and your model only contains data for the view to model. In an MVVM pattern, as knockout is, your viewModel actually does have knowledge of the current states of controls on the view. This isn't to say your viewmodel should directly update the view, but it usually will contain properties that are bound to states of the view. Things like SaveButtonEnabled or IsSavingData or even things like StatusLabelColor are accepted in a viewmodel.
Perhaps use $.ajaxSetup(). You call this in your document ready function.
$.ajaxSetup({
beforeSend: function(jqXHR)
{
//this will be called before every
//ajax call in your program
//so perhaps, increment an observable viewmodel variable
//representing the number of outstanding requests
//if this variable is > 0 then disable
//your button
},
complete: function(jqXHR)
{
//this will be called after every
//call returns
//decrement your variable here
//if variable is zero, then enable button
}
});
I'd recommend you take a look at http://durandaljs.com/, a framework using Knockout and has some great data patterns, even if you don't use it directly.

Adapt my old work flow to Backbone

Im starting to build a new app and I would like to use Backbone as my framework. Below is a basic workflow that this (and most apps) follow.
What is the correct/best model to use with Backbone?
Old Way
User navigates to a page.
Selects "Create New widget"
User is presented with a form filled with inputs
At this point I would probably take the values entered (after passing basic validation), wrap them up and send them to the server via an ajax request
Request comes back as "OK" and the user is taken somewhere else (This step isn't entirely important)
Some basic pseudo-code
// Grab values
var userName = $('.UserName').val(),
dateOfBirth = $('.DateOfBirth').val();
...
...
...
$.ajax({
url: "/Webservices/ProcessStuff",
success: function(result){
if (result) {
// Render something or doing something else
} else {
// Error message
}
},
error: function () {
// Error message
}
});
Backbone way
Using the same example as above; I assume I'd have a model for the user information and a view to display the inputs. However, processing the actual call to the web service is one of the things I'm confused about. Where does this need to go? In the model or in the view click of some "Go" button?
Model.UserInformation = Backbone.Model.extend({ username: null, dateOfBirth: null });
Maybe also have a collection of these UserInformation models?
UserInformations = Backbone.Collection.extend({ model: Model.UserInformation' });
So bottom line what I'm asking is...
What is the best way to achieve this functionality?
What is the proper way to actually perform CRUD? Where to put the actual call to delete/update/create/etc?
You have the right idea and Backbone should make it easy for you to get things done using the same basic high level overview of your workflow. Note that you're still going to be using jQuery for this functionality - you'll just be doing it through the organizational aspects of Backbone's types.
There are a couple of key items that you'll want in place, most of which you already mentioned:
A backbone View to coordinate the HTML elements with your Javascript code
A backbone Model to store all of the data that the user input into the HTML elements
A back-end server that can handle RESTful JSON calls via AJAX requests from jQuery
I think the only thing you are missing is that the model has a save method on it, which wraps up all of the logic to call the create / update routes on your back-end server. The model also has a delete method to handle deletion from the server.
As a very simple example, here's a form that renders an HTML template to the screen, gathers the user input in to the model and then saves it to the server.
An HTML template:
<script id="myTemplate" type="text/x-jquery-tmpl">
First name: <input id="first_name"><br/>
Last Name: <input id="last_name"><br/>
<button id="save">Save!</button>
</script>
The code to run this:
MyModel = Backbone.Model.extend({
urlRoot: "/myModel"
});
MyView = Backbone.View.extend({
template: "#myTemplate",
events: {
"change #first_name": "setFirstName",
"change #last_name: "setLastName",
"click #save": "save"
},
initialize: function(){
_.bindAll(this, "saveSuccess", "saveError");
},
setFirstName: function(e){
var val = $(e.currentTarget).val();
this.model.set({first_name: val});
},
setLastName: function(e){
var val = $(e.currentTarget).val();
this.model.set({last_name: val});
},
save: function(e){
e.preventDefault(); // prevent the button click from posting back to the server
this.model.save(null, {success: this.saveSuccess, error: this.saveError);
},
saveSuccess: function(model, response){
// do things here after a successful save to the server
},
saveError: function(model, response){
// do things here after a failed save to the server
},
render: function(){
var html = $(this.template).tmpl();
$(el).html(html);
}
});
myModel = new MyModel();
myView = new MyView({model: myModel});
myView.render();
$("#someDivOnMyPage").html(myView.el);
This will give you a quick start for a form that saves a new model back to the server.
There are a couple of things your server needs to do:
Return a valid HTTP response code (200 or some other response that says everything was "ok")
Return the JSON that was sent to the server, including any data that the server assigned to the model such as an id field.
It's very important that your server do these things and include an id field in the response. Without an id field from the server, your model will never be able to update itself when you call save again. It will only try to create a new instance on the server again.
Backbone uses the id attribute of a model to determine if it should create or update a model when pushing data to the back end. The difference between creating a new model and saving one is only the id attribute. You call save on the model whether it's a new or an edited model.
A delete works the same way - you just call destroy on the model and it does a call back to the server to do the destroy. With some HTML that has a "delete" link or button, you would attach to the click event of that HTML element the same as I've shown for the "Save" button. Then in the callback method for the delete click, you would call this.model.destroy() and pass any parameters you want, such as success and error callbacks.
Note that I included a urlRoot on the model, as well. This, or a url function are needed on a model if the model is not part of a collection. If the model is part of a collection, the collection must specify the url.
I hope that helps.
If the "el" of the view is the form tag, then you could probably use the built in event object to bind a function to submit, but if the root of the view is something else, then you'll need to attach the click handler in the render function.

Categories