I am using Knockout and the Knockout Mapping plugin.
My MVC3 Action returns a View and not JSON directly as such I convert my Model into JSON.
This is a data entry form and due to the nature of the system validation is all done in the Service Layer, with warnings returned in a Response object within the ViewModel.
The initial bindings and updates work correctly its the "post-update" behavior that is causing me a problem.
My problem is after calling the AJAX POST and and receiving my JSON response knockout is not updating all of my bindings... as if the observable/mappings have dropped off
If I include an additional ko.applyBindings(viewModel); in the success things do work... however issues then arise with multiple bindings and am certain this is not the correct solution.
This is the HTML/Template/Bindings
<!-- Start Form -->
<form action="#Url.Action("Edit")" data-bind="submit: save">
<div id="editListing" data-bind="template: 'editListingTemplate'"></div>
<div id="saveListing" class="end-actions">
<button type="submit">Save Listings</button>
</div>
</form>
<!-- End Form -->
<!-- Templates -->
<script type="text/html" id="editListingTemplate">
<div class="warning message error" data-bind="visible: Response.HasWarning">
<span>Correct the Following to Save</span>
<ul>
{{each(i, warning) Response.BusinessWarnings}}
<li data-bind="text: Message"></li>
{{/each}}
</ul>
</div>
<fieldset>
<legend>Key Information</legend>
<div class="editor-label">
<label>Project Name</label>
</div>
<div class="editor-field">
<input data-bind="value: Project_Name" class="title" />
</div>
</fieldset>
</script>
<!-- End templates -->
And this is the Knockout/Script
<script type="text/javascript">
#{ var jsonData = new HtmlString(new JavaScriptSerializer().Serialize(Model)); }
var initialData = #jsonData;
var viewModel = ko.mapping.fromJS(initialData);
viewModel.save = function ()
{
this.Response = null;
var data = ko.toJSON(this);
$.ajax({
url: '#Url.Action("Edit")',
contentType: 'application/json',
type: "POST",
data: data,
dataType: 'json',
success: function (result) {
ko.mapping.updateFromJS(viewModel, result);
}
});
}
$(function() {
ko.applyBindings(viewModel);
});
</script>
And this is the response JSON returned from the successful request including validation messages.
{
"Id": 440,
"Project_Name": "",
"Response": {
"HasWarning": true,
"BusinessWarnings": [
{
"ExceptionType": 2,
"Message": "Project is invalid."
}, {
"ExceptionType": 1,
"Message": "Project_Name may not be null"
}
]
}
}
UPDATE
Fiddler Demo Is a trimmed live example of what I am experiencing. I have the Project_Name updating with the returned JSON but the viewModel.Response object and properties are not being updated through their data bindings. Specifically Response.HasWarning().
I've changed back to ko.mapping.updateFromJS because in my controller I am specifically returning Json(viewModel).
Cleaned up my initial code/question to match the demo.
I guess Response is reserved, when I change "Response" to "resp", everything went fine. See http://jsfiddle.net/BBzVm/
Should't you use ko.mapping.updateFromJSON on your success event? Chapter Working with JSON strings on Knockout Mapping site says:
If your Ajax call returns a JSON string (and does not deserialize it into a JavaScript object), then you can use the functions ko.mapping.fromJSON and ko.mapping.updateFromJSON to create and update your view model instead.
Related
I am trying to develop a widget in NopCommerce 4.3. I need to Post data from widget public view to the controller's Action method with Ajax. The problem is that when the call is made from the view, Product page is called instead of my widget view. This is the VS output:
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 POST https://localhost:44331/aliento-de-ogro application/x-www-form-urlencoded 292298. Excepción producida: 'System.ArgumentException' en System.Private.CoreLib.dll. Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint 'Nop.Web.Controllers.ProductController.ProductDetails (Nop.Web)' Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Route matched with {action = "ProductDetails", controller = "Product", area = ""}. Executing controller action with signature Microsoft.AspNetCore.Mvc.IActionResult ProductDetails(Int32, Int32) on controller Nop.Web.Controllers.ProductController (Nop.Web). Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor: Information: Executing ViewResult, running view ProductTemplate.Simple. Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor: Information: Executed ViewResult - view ProductTemplate.Simple executed in 411.9849ms.
This is how widget's view looks like:
<form id="AjaxForm" method="post">
<input type="hidden" id="productId" value=#ViewBag.sku />
<input type="hidden" id="input_elem" name="image" />
<div class="col-md-9">
<div id="ErrorAlert" class="alert alert-danger" style="display:none" role="alert">
Error en los datos enviados!
</div>
<div id="ExitoAlert" class="alert alert-success" style="display:none" role="alert">
Datos recibidos correctamente!
</div>
</div>
<button class="btn btn-success" id="SubmitBtn" type="submit">Save photo</button>
</form>
$.ajax({
type: "post", // Verbo HTTP
url: "#Url.Action("TakePhoto", "Photo")",
dataType: "application/json",
headers: { "RequestVerificationToken": $('input[name="__RequestVerificationToken: "]').val() },
data: JSON.stringify(
{
// Datos / Parámetros
ProductId: $("#productId").val(),
Role: role, //$("#role").val(),
ExpressionName: expressionName, //$("#expressionName").val(),
Photo: $("#photo").attr('src')
}),
contentType: "application/json; charset=utf-8"
})
.done(function (result) {
alert(result.Message);
}
})
.fail(function (xhr, status, error) {
})
.always(function () {
});
return false;
});
Asp.net backend is receiving "application/x-www-form-urlencoded" instead of "application/json". Why? Is this the reason because my widget's controller is not reached? Why Nop.Web.Controllers.ProductController.ProductDetails (Nop.Web)' is invoked?
It doesn't seem you're receiving the request from the Ajax call. Instead you're receiving the request from some form in the page.
This seems clear since you're receiving the request from a different endpoint and with a different content type. Since it seems you have an image in your form the multipart request is logical in that scenario.
My suggestions are:
Check your page and look for a multipart form in it. Some problem handling events could make that form to fire instead of your code. Are there nested forms?
Enable the request history in Chrome and the preserve log option. Get a verbatim copy of the request. Check the Url.
In .NET, retrieve a verbatim copy of the received request. Check the Url.
Set a breakpoint in your Javascript code and see if it is being called at all.
I think this will help you.
I am trying to create a small survey application using mvc. The model goes Questions has an ICollection of answers created by entity framework.
#{
ViewData["Title"] = "TakingSurvey";
}
#using (Html.BeginForm())
{
ICollection<SurveyAPI.Models.DB.Questions> _questions = ViewBag.questions;
ICollection<SurveyAPI.Models.DB.Answers> _answers = ViewBag.answers;
int i = 0;
foreach (var question in _questions)
{
<div class="form-group">
<label asp-for="#_questions.ElementAt(i).Answers">#_questions.ElementAt(i).QuestionText</label>
<input asp-for="#_answers.ElementAt(i).AnswerText" class="form-control" />
</div>
i++;
}
<div class="form-group">
<button type="submit" class="btn btn-default">Submit</button>
</div>
}
ViewBag.questions contains the questions. This part works. What I expected to happen was that I'd send an ICollection of answers back to the controller but I'm failing miserably. How can I send a collection of answers (with their respective questionID) to the controller? The answers viewbag is empty objects for each question
Add an ID to your form.
#using(BeginForm(null, null, FormMethod.Post, new {id="anID"}))
And then in your javascript you could serialize the form and send it to a controller function such as:
$(function(){
$("#anID").submit(function(event){
event.preventDefault();
var x = $("#anID").serializa();
$.ajax({
url: '#Url.Action("action", "controller")',
type: 'POST',
data: x,
success: function(result){
//whatever
}
});
});
});
Your controller actions the model of the model passed into the view.
I have an action which is showing a form for some user input. The inputs are plain text field. I wonder how can I pre fill the input field.
All the tutorials and blog posts I found are quite old and all of them are taking into account only one field. It is my understanding that I need a custom .ftl with a call to a web script in it.
<field id="myprop">
<control template="/org/alfresco/components/form/controls/mycustomfield.ftl"/>
</field>
The problem in my case is that I will end up doing at least six call to the same web script. Because that's the number of fields I currently have in my form.
Well I guess it could be implemented by using a form filter as well. Maybe not the nicest solution, but it should get the job done. https://wiki.alfresco.com/wiki/Forms_Developer_Guide#Form_Filter
There is only one better way... You don't need to use Share Form Engine. Take a look at "create site" dialog, this form doesn't use share form engine
You need to create custom share component that will return form with filled parameters and initialize this form in your front-end js that is executed while clicking on the action.
You can add new component to Share by the following way:
1) Create new descriptor my-form.get.desc.xml in web-extension/site-webscripts/com/pizdez/form
<webscript>
<shortname>my-form</shortname>
<description>Get HTML form</description>
<url>/pizdec/components/form</url>
</webscript>
2) Create new controller my-form.get.js in the same folder where you can call alfresco to get all needed information
var connector = remote.connect("alfresco");
var response = connector.get("/my/alfresco/webscript");
if (response.status == 200)
{
// Create javascript objects from the repo response
var obj = eval('(' + response + ')');
if (obj)
{
model.param1 = obj.param1;
}
}
3) Create ftl template my-form.get.html.ftl in the same folder
<#markup id="css" >
<#-- CSS Dependencies -->
<#link href="${url.context}/res/components/form/my.css" />
</#>
<#markup id="js">
<#script src="${url.context}/res/components/form/my.js" />
</#>
<#markup id="widgets">
<#createWidgets/>
</#>
<#markup id="html">
<#uniqueIdDiv>
<#assign el=args.htmlid?html>
<div id="${el}-dialog">
<div class="hd">TITLE</div>
<div class="bd">
<form id="${el}-form" method="POST" action="">
<div class="yui-gd">
<div class="yui-u first"><label for="${el}-title">Title:</label></div>
<div class="yui-u"><input id="${el}-title" type="text" name="title" tabindex="0" maxlength="255"/> *
</div>
</div>
<div class="yui-gd">
<div class="yui-u first"><label for="${el}-param1">Param1:</label></div>
<div class="yui-u"><input id="${el}-param1" type="text" name="title" tabindex="0" maxlength="255" value="${param1}"/> *
</div>
</div>
<div class="bdft">
<input type="submit" id="${el}-ok-button" value="${msg("button.ok")}" tabindex="0"/>
<input type="button" id="${el}-cancel-button" value="${msg("button.cancel")}" tabindex="0"/>
</div>
</form>
</div>
</div>
</#>
</#>
4) After that you need to get this component from ui js
var myForm = new Alfresco.module.SimpleDialog(this.id + "-dialog");
myForm.setOptions(
{
width: "50em",
templateUrl: Alfresco.constants.URL_SERVICECONTEXT + "/pizdec/components/form",
actionUrl: null,
destroyOnHide: true,
doBeforeDialogShow:
{
fn: doBeforeDialogShow,
scope: this
},
onSuccess:
{
fn: function (response)
{
},
scope: this
},
onFailure:
{
fn: function(response)
{
},
scope: this
}
}).show();
I just wanted to show you direction to research
I am using the "json-API" plugin for wordpress and I am trying to call that info into a phonegap app.
I am following a post on http://alexbachuk.com/wordpress-and-phonegap-part3/ and I am trying to find out how to call custom_fields in my content.
I have included the custom field like so:
'http://www.example.com/?json=get_recent_posts&custom_fields=store-icon' in the ajax request.
the ajax request looks like this:
product: function(){
function getProducts() {
var dfd = $.Deferred();
$.ajax({
url: 'http://delectable.site40.net/blog/?json=get_recent_posts&custom_fields=store-icon',
type: 'GET',
dataType: 'json',
success: function(data){
var source = $("#product-template").html();
var template = Handlebars.compile(source);
var blogData = template(data);
$('#product-data').html(blogData);
$('#product-data').trigger('create');
dfd.resolve(data);
},
error: function(data){
console.log(data);
}
});
return dfd.promise();
};
getProducts().then(function(data){
$('#all-posts').on('click','li', function(e){
localStorage.setItem('postData', JSON.stringify(data.posts[$(this).index()]));
});
});
}
and the template currently looks like this:
<script id="product-template" type="text/x-handlebars-template">
<ul data-role="listview" data-icon="false" class="mainContent" data-theme="a" id="all-posts">
{{#each posts}}
<li class="center productss"><p class="photo circle center" style="margin-left: 31%;"><img src="{{thumbnail}}" width="85" height="57" /></ br><a data-ajax="false" data-transition="slide" href="single.html?{{#index}}"><h3 class="main_product">{{title}}</h3></a></ br><h5 class="left">R4200-00</h5><h5 class="right"><img src="{{custom_fields[0].url}}" width="150" height="20" /></h5></p></li>
{{/each}}
</ul>
</script>
how would I go about inserting that into my html. The alexbachuk.com post uses handlebars to parse the json so the post title is output as {{title}} and thumbnail as {{thumbnail}}. Is there a way to output custom_fields in a similar manner?
Yes there is.
Use:
{{custom_fields.fieldname}}
I am a newbie in AdobeCQ5. I am facing some trouble in posting form. Here is my Structure -
/apps/<myproject>/components/mytestcomponent
mytestcomopnent.jsp has following code -
<form id="myForm" action="<%=resource.getPath()+".html" %>">
<input type="text" id="t1" class="input-small" placeholder="Temprature F" />
<input type="text" id="t2" class="input-small" placeholder="Temprature C" readonly/>
<button type="button" id="cbtn" class="btn">Convert</button>
</form>
<script>
$(document).ready(function() {
$('#cbtn').click(function () {
var URL = $("#myForm").attr("action");
alert(URL);
var t1=$("#t1").val();
var t2=$("#t2").val();
$.ajax({
url: URL,
data:{'t1':t1},
type:"post",
success: function(data, status) {
$("#t2").val(data);
},
error: function( xhr, txtStat, errThrown ) {
alert("ajax error! " + txtStat + ":::" + errThrown);
}
});
});
});
</script>
This is giving my response code 200 (Success) but the output is not desired. My mycomponent.POST.jsp has following code -
<%
// TODO add you code here
String t1=request.getParameter("t1");
%>
<%= t1 %>
It gives the following output
Content modified /content/imobile/en/jcr:content/social.html
Status
200
Message
OK
Location /content/imobile/en/_jcr_content/social.html
Parent Location /content/imobile/en/_jcr_content
Path
/content/imobile/en/jcr:content/social.html
Referer http://example.comt:4502/content/imobile/en.html
ChangeLog
<pre></pre>
Go Back
Modified Resource
Parent of Modified Resource
Please help to resolve this.
The JSP file handling the POST method for your component should be named POST.jsp rather than mycomponent.POST.jsp.
Please notice that if you intercept all POST requests to your component, you won't be able to edit it on the author instance using a dialog (as the dialog simply POSTs data to the component URL). To avoid it, consider using a custom selector (like form). Your form should look be declared like this:
<form id="myForm" action="${resource.path}.form.html">
and the script handling POST request should be called form.POST.jsp.
The second important thing is that you should use Java classes rather than JSP files to store business logic. In this case it means that the form.POST.jsp script can be replaced with a Sling servlet declared as follows:
#SlingServlet(
resourceTypes="myproject/components/mytestcomponent",
methods="POST",
selectors="form")