I am trying to when a person changes the state of previously bound checkbox, I want to update the server with the new value.
So here is what I have:
JSCRIPT
function JobViewModel() {
var self = this;
var baseUri = '/Api/Pedidos/';
self.TotalItems = ko.observable(#Model.TotalItems);
self.AbreviaNome = ko.observable(#Model.AbreviaNome.ToString().ToLower());
self.AbreviaFantasia = ko.observable(#Model.AbreviaFantasia.ToString().ToLower());
self.update = function () {
alert('Boom');
$.ajax({
type: "PUT",
url: baseUri,
data: self.Job,
datatype: "json",
contenttype: "application/json"
})
.done(function (data) {
//handleSuccessFunctionHERE(data);
alert('Magic');
})
.error(function (jqXHR, textStatus, errorThrown) {
alert(errorThrown);
alert("fail");
});
};
}
function JobDetailsViewModel() {
var self = this;
var baseUri = '/Api/Pedidos/';
self.Job = new JobViewModel();
}
HTML
<label class="btn btn-primary" data-bind="css: {active:Job.AbreviaNome }">
<input type="checkbox" data-bind="checked: AbreviaNome , onchange: Job.update" name="type" id="AbreviaNome "> Nome</input>
</label>
This never triggers the update function. I also have tried :
<label class="btn btn-primary" data-bind="css: {active:Job.AbreviaNome }">
<input type="checkbox" data-bind="checked: AbreviaNome , click: Job.update" name="type" id="AbreviaNome "> Nome</input>
</label>
And this within the JobViewModel:
this.AbreviaNome.subscribe(function (newValue) {
alert('test');
}, this);
Any ideas?
You should use subscribe for this and not the onchange handler. You mentioned you tried it, but you subscribed to AbreviaNome instead of AbreviaLogradouro.
Solved the problem, below is the binding:
<label class="btn btn-primary " data-bind="css: {active:Job.AbreviaNome}">
<input type="checkbox" data-bind="checked: Job.AbreviaNome, event: {change: Job.update}" name="type" id="AbreviaNome">Nome/Razão</input>
</label>
Not sure this was the best way but it works.
Related
I experimenting something with mvc and knockout.js but maybe a have binding problem.
My viewmodel is:
var urlPath = window.location.pathname;
var currencyVm = function () {
var self = this;
var validationOptions = { insertMessages: true, decorateElement: true, errorElementClass: 'errorFill' };
ko.validation.init(validationOptions);
//ViewModel
self.ID = ko.observable(0);
self.Id_Region = ko.observable("");
self.Description = ko.observable("");
self.Symbol = ko.observable("");
self.PayPalCode = ko.observable("");
self.IFSCode = ko.observable("");
self.isGridVisible = ko.observable(true);
self.isEditVisible = ko.observable(false);
self.isAddVisible = ko.observable(false);
//Objects ---------------------------------------------
self.Currency = ko.observable();
self.Currencys = ko.observableArray([]);
//Methods ---------------------------------------------
//Edit
self.editCurrency = function (dataItem) {
self.isGridVisible(false);
self.isEditVisible(true);
self.ID = ko.observable(dataItem.ID);
self.Id_Region = ko.observable(dataItem.Id_Region);
self.Description = ko.observable(dataItem.Description);
self.Symbol = ko.observable(dataItem.Symbol);
self.PayPalCode = ko.observable(dataItem.PayPalCode);
self.IFSCode = ko.observable(dataItem.IFSCode);
//var vm = ko.mapping.fromJS(dataItem);
//self.Currency(vm);
};
//Add
self.addCurrency = function () {
self.isGridVisible(false);
self.isAddVisible(true);
self.isEditVisible(false);
self.reset();
}
// Cancel Edit
self.cancelEdit = function () {
self.isEditVisible(false);
self.isGridVisible(true);
}
// Cancel Add
self.cancelAdd = function () {
self.isAddVisible(false);
self.isGridVisible(true);
}
//Reset
self.reset = function () {
self.ID(0);
self.Id_Region("");
self.Description("");
self.Symbol("");
self.PayPalCode("");
self.IFSCode("");
}
//Actions --------------------------------------------------
var Currency = {
ID: self.ID,
Id_Region: self.Id_Region,
Description: self.Description,
Symbol: self.Symbol,
PayPalCode: self.PayPalCode,
IFSCode: self.IFSCode
};
//Create
self.createCurrency = function () {
$.ajax({
url: "/Currency/CreateCurrency",
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(Currency),
success: function (data) {
self.isEditVisible(false);
self.isAddVisible(false);
self.isGridVisible(true);
$("#gridCurrencies").data("kendoGrid").dataSource.read();
}
}).fail(
function (xhr, textStatus, err) {
alert(err);
});
}
// Update
self.updateCurrency = function () {
$.ajax({
url: "/Currency/UpdateCurrency",
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(Currency),
success: function (data) {
self.isEditVisible(false);
self.isEditVisible(false);
self.isGridVisible(true);
$("#gridCurrencies").data("kendoGrid").dataSource.read();
}
})
}
//Delete
self.deleteCurrency = function (currency) {
$.confirm({
title: "Delete",
content: "Sure to delete ?",
confirm: function () {
$.ajax({
url: "/Currency/DeleteCurrency",
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(currency),
success: function (data) {
$("#gridCurrencies").data("kendoGrid").dataSource.read();
}
}).fail(
function (xhr, textStatus, err) {
alert(err);
});
},
cancel: function () {
}
});
}
};
in the view I call applybinding and then based on selected row of the Telerik grid I call editCurrency or deleteCurrency.
<script>
var viewModel = null;
$(function () {
viewModel = new currencyVm();
ko.applyBindings(viewModel);
});
function deleteCurrency(e) {
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
viewModel.deleteCurrency(dataItem);
}
function editCurrency(e) {
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
viewModel.editCurrency(dataItem);
}
</script>
In the markup I have the html elements with the bindings:
Thi is the div for add:
<div class="panel panel-default" data-bind="visible: isAddVisible" style="display:none">
<div class="panel-heading">Add New Currency</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3">
#Html.LabelFor(model => model.IFSCode)
<input type="text" data-bind="value: $root.IFSCode, valueUpdate: 'keypress'" class="form-control " />
</div>
<div class="col-md-3">
#Html.LabelFor(model => model.PayPalCode)
<input type="text" data-bind="value: $root.PayPalCode, valueUpdate: 'keypress'" class="form-control " />
</div>
<div class="col-md-3">
#Html.LabelFor(model => model.Symbol)
<input type="text" data-bind="value: $root.Symbol, valueUpdate: 'keypress'" class="form-control " />
</div>
</div>
<div class="row">
<div class="col-md-9">
#Html.LabelFor(model => model.Description)
<input type="text" data-bind="value: $root.Description" class="form-control" />
</div>
</div>
<br />
<div class="row">
<div class="col-md-3">
<input data-bind="click: $root.createCurrency" type="button" class="btn btn-success btn-sm" value="#DbRes.T("Save", "Common")" />
<input data-bind="click: cancelAdd" type="button" class="btn btn-warning btn-sm" value="#DbRes.T("Cancel", "Common")" />
</div>
</div>
</div>
the edit div is like the add div but use different binding like data-bind="value: IFSCode"
The problem is that in add mode it work but when I edit I dont see nothing
in the textbox elements.
Also I use the to debug,
in add mode I can see the observables updating while I type something in textbox,
in edit mode the observables are emtpy and remain while typeing something.
May some body help please to understand what appen to this code
Your help is very appreciated.
I fail to upload pictures using formdata. This is my previous code which was working fine(This is just part of a form):
<div class="fileinput fileinput-new" data-provides="fileinput" style="width: 100%;">
<div class="fileinput-preview" data-trigger="fileinput" style="margin: 10px 0px;"></div>
<div>
<span class="btn btn-default btn-file">
<input type="file" id="filePicture" name="filePicture">
</span>
</div>
</div>
And this is my Ajax call:
$(document).on("click", "#sledBuckSaveBtn", function() {
event.preventDefault();
event.stopPropagation();
var fd = new FormData(document.getElementById("saveSledBuckForm1"));
$.ajax({
type: "POST",
url: "/SledBuck/EditFromSledTestDetails", //url,
data: fd,
processData: false, // tell jQuery not to process the data
contentType: false,
success: function (result) {
var title = "Error", msgtype = "error";
if (result) {
title = "Success";
msgtype = "success";
}
document.location.reload();
}
});
});
But my requirement has changed, I need to use Krajee plugin to create an input type:
Here my new code:
<div class="col-md-9">
<input id="input-#Model.PictureIdList[i]" type="file" class="file" data-show-upload="false" data-show-caption="true" data-input-id="#Model.PictureIdList[i]">
</div>
what problem am I facing. I think the reason is I might need to do configuration for Krajee plugin but I'm not sure. Any suggestions?
I just found the answer. I lacked the name of the file input. Just do like this and the file will be regconized:
<input id="input-#Model.PictureIdList[i]" name= "fileInput" type="file" class="file" data-show-upload="false" data-show-caption="true" data-input-id="#Model.PictureIdList[i]">
What I want to achieve is once the data got saved into database, when it goes back to client, it will automatically update the observable array. But somehow I couldn't make it happen.
This is my Server side code:
[HttpGet]
public JsonResult GetTasks()
{
var tasks = context.ToDoTasks.ToList();
return Json(tasks.Select(c => new TaskViewModel(c)).ToList(), JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult AddTask(string text, string date)
{
var nTask = new ToDoTask()
{
Text = text,
Date = DateTime.ParseExact(date, "MM/dd/yyyy", System.Globalization.CultureInfo.InvariantCulture),
IsDone = false,
Order = 1,
};
context.ToDoTasks.Add(nTask);
context.SaveChanges();
return Json(new TaskViewModel(nTask), JsonRequestBehavior.AllowGet);
}
This is my cshtml file code:
<form>
<div class="controls controls-row" style="margin-top:40px;">
<input class="span7" type="text" placeholder="Task to do" style="margin-right:4px;" id="oText">
<div id="task-date" class="input-append date">
<input data-format="MM/dd/yyyy" type="text" placeholder="MM/dd/yyyy" name="taskDate" id="oDate" />
<span class="add-on">
<i data-time-icon="icon-time" data-date-icon="icon-calendar">
</i>
</span>
</div>
<button class="btn" type="submit" style="margin-top:-10px;" data-bind="click: save">+</button>
</div>
<div class="controls">
<label class="checkbox">
<input type="checkbox"> Mark all as complete
</label>
</div>
<div id="task-section" style="margin-top:20px;">
<ul data-bind="foreach: Tasks">
<!-- ko if: IsDone -->
<li>
<span><input type="checkbox" style="margin:-5px 5px 0px 0px;" data-bind="checked: IsDone" /></span>
<del><span data-bind="text: Text"></span></del>
<del><span class="task-date" data-bind="text: Date"></span></del>
</li>
<!-- /ko -->
<!-- ko ifnot: IsDone -->
<li>
<span><input type="checkbox" style="margin:-5px 5px 0px 0px;" data-bind="checked: IsDone" /></span>
<span data-bind="text: Text"></span>
<span class="task-date" data-bind="text: Date"></span>
</li>
<!-- /ko -->
</ul>
</div>
<div class="clearfix" style="margin-top:30px;">
<span class="pull-left" style="font-weight:bold;"><span data-bind="text: oItemLeft"></span> item left</span>
<span class="pull-right badge" style="cursor:pointer;" data-bind="click: remove">Clear # completed item</span>
</div>
</form>
And finally my JS:
var ViewModel = function (data) {
var self = this;
self.Tasks = ko.mapping.fromJS(data, {}, self.Tasks);
self.oItemLeft = ko.computed(function () {
var i = 0;
data.forEach(function (entry) {
if (!entry.IsDone) i++;
});
return i;
});
self.save = function () {
$.ajax({
url: "Home/AddTask",
type: "POST",
data: { text: $('#oText').val(), date: $('#oDate').val() },
success: function (response) {
ko.mapping.fromJS(response, ViewModel);
}
});
};
self.remove = function () {
alert('delete');
}
}
$(function () {
$.getJSON("/Home/GetTasks/", null, function (data) {
ko.applyBindings(new ViewModel(data));
});
// for datepicker
$('#task-date').datetimepicker({
language: 'pt-BR',
pickTime: false
});
});
self.save = function () {
$.ajax({
url: "Home/AddTask",
type: "POST",
data: { text: $('#oText').val(), date: $('#oDate').val() },
success: function (response) {
var task = ko.mapping.fromJS(response);
self.Tasks.push(task);
}
});
};
Also for oItemLeft you should be referring to self.Tasks instead of data:
self.oItemLeft = ko.computed(function () {
var i = 0;
self.Tasks().forEach(function (entry) {
if (!entry.IsDone) i++;
});
return i;
});
I self admitted newbie, but I have a view with some code I pasted to provide a file upload. The function works but if the code is in, the "Save" button for the View that was already there stops working. If I had to guess it has something to do with the "HTML.BeginForm" line being there twice.
Here is the top of the view,
#model BrooksSOR.Models.dataOffender
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<div >
<h2>Upload Files in MVC</h2>
<img src="#Model.Photograph" width="250" height="250" />*
#using (Html.BeginForm("FileUpload", "SOR",
FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input name="uploadFile" type="file" />
<input type="submit" value="Upload File"/>
<input type="hidden" name="parmPersonID" value="#Model.PersonID" />
}
</div>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>dataOffender</legend>
<p>
<input type="submit" value="Save" />
<input id="Details" type="button" value="Details" />
</p>
</fieldset>
Remove the #html.BeginForm
The script you would need to send for a file and some other fields with it.
//Purpose: Form Submit: SAVE Client
document.getElementById('frmPage').onsubmit = function (e) {
debugger;
var file = document.getElementById('fileToUpload').files[0];
var filename;
if (file) {
filename = file.name;
}
else {
filename = "";
}
$('#Image').val(filename);
var formObj = $(this);
var formURL = '#Url.Action("SaveMethod", "ControllerName")';
var formData = new FormData(this);
$.ajax({
url: formURL,
type: 'POST',
data: formData,
mimeType: "multipart/form-data",
contentType: false,
cache: false,
processData: false,
success: function (data, textStatus, jqXHR) {
debugger;
alert("Client saved successfully");
},
error: function (jqXHR, textStatus, errorThrown) {
}
});
e.preventDefault(); //Prevent Default action.
}
This is the design:
<div>
<form id="frmPage" class="form-horizontal">
<div class="form-body">
<div class="form-group">
<label id="lblImage" class="col-md-3 control-label">Upload File</label>
<div class="col-md-4">
<input type="file" id="fileToUpload" name="file" />
</div>
</div>
<div class="modal-footer" style="margin-top: 0px">
<div class="pull-right">
<button type="submit" id="btnSave" class="btn blue Save">Save</button>
</div>
</div>
</form>
</div>
Here: The btnSave is submit type, so it will submit that particular form. and form named frmPage will do the jquery script we added.
Ok so this is beginning to drive me insane. I have for several hours now searched and searched, and every single solution doesnt work for me. So yes, this question might be redundant, but i cant for the life of me get solutions to work.
I have a bunch of checkboxes being generated by a jquery template that is databound via knockout.js. However, it turns up unstyled. Afaik, it is something about jquery mobile does the styling before knockout renderes the template, so it ends up unstyled.
I have tried numerous methods to no avail, so i hope someone here can see what i am doing wrong.
(i am using jquery mobile 1.2.0 , jquery 1.8.2 and knockout 2.2.1)
This is the scripts:
<script type="text/javascript">
jQuery.support.cors = true;
var dataFromServer = "";
// create ViewModel with Geography, name, email, frequency and jobtype
var ViewModel = {
email: ko.observable(""),
geographyList: ["Hovedstaden","Sjælland","Fyn + øer","Nordjylland","Midtjylland","Sønderjylland" ],
selectedGeographies: ko.observableArray(dataFromServer.split(",")),
frequencySelection: ko.observable("frequency"),
jobTypes: ["Kontor (administration, sekretær og reception)","Jura","HR, Ledelse, strategi og udvikling","Marketing, kommunikation og PR","Handel og service (butik, service, værtinde og piccoline)","IT","Grafik og design","Lager, chauffør, bud mv.","Økonomi, regnskab og finans","Kundeservice, telefoninterview, salg og telemarketing","Sprog","Øvrige jobtyper"],
selectedJobTypes: ko.observableArray(dataFromServer.split(",")),
workTimes: ["Fulltid","Deltid"],
selectedWorkTimes: ko.observableArray(dataFromServer.split(","))
};
// function for returning checkbox selection as comma separated list
ViewModel.selectedJobTypesDelimited = ko.dependentObservable(function () {
return this.selectedJobTypes().join(",");
}, ViewModel);
var API_URL = "/webapi/api/Subscriptions/";
// function used for parsing json message before sent
function omitKeys(obj, keys) {
var dup = {};
var key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (keys.indexOf(key) === -1) {
dup[key] = obj[key];
}
}
}
return dup;
}
//Function called for inserting new subscription record
function subscribe() {
if($("#jobmailForm").valid()=== true){
//window.alert("add subscriptiooncalled");
var mySubscription = ko.toJS(ViewModel);
//var json = JSON.stringify(mySubscription);
var jsonSmall = JSON.stringify(omitKeys(mySubscription, ['geographyList','jobTypes','selectedJobTypesDelimited','workTimes']));
//window.alert(jsonSmall);
$.ajax({
url: API_URL,
cache: false,
type: 'POST',
contentType: 'application/json',
data: jsonSmall,
success: function (data) {
window.alert("success");
},
error: function (error) {
window.alert("ERROR STATUS: " + error.status + " STATUS TEXT: " + error.statusText);
}
});
}
}
function initializeViewModel() {
// Get the post from the API
var self = this; //Declare observable which will be bind with UI
// Activates knockout.js
ko.applyBindings(ViewModel);
}
// Handle the DOM Ready (Finished Rendering the DOM)
$("#jobmail").live("pageinit", function() {
initializeViewModel();
$('#jobmailDiv').trigger('updatelayout');
});
</script>
<script id="geographyTmpl" type="text/html">
<input type="checkbox" data-role="none" data-bind="attr: { value: $data }, attr: { id: $data }, checked: $root.selectedGeographies" />
<label data-bind="attr: { for: $data }"><span data-bind="text: $data"></span></label>
</script>
<script id="jobTypeTmpl" type="text/html">
<label><input type="checkbox" data-role="none" data-bind="attr: { value: $data }, checked: $root.selectedJobTypes" /><span data-bind="text: $data"></span></label>
</script>
Note, "jobmail" is the surrounding "page" div element, not shown here. And this is the markup:
<div data-role="content">
<umbraco:Item field="bodyText" runat="server"></umbraco:Item>
<form id="jobmailForm" runat="server" data-ajax="false">
<div id="jobmailDiv">
<p>
<label for="email">Email</label>
<input type="text" name="email" id="email" class="required email" data-bind="'value': email" />
</p>
<fieldset data-role="controlgroup" data-mini="true" data-bind="template: { name: 'geographyTmpl', foreach: geographyList, templateOptions: { selections: selectedGeographies } }">
<input type="checkbox" id="lol" />
<label for="lol">fkfkufk</label>
</fieldset>
<fieldset data-role="controlgroup" data-mini="true">
<p data-bind="template: { name: 'jobTypeTmpl', foreach: jobTypes, templateOptions: { selections: selectedJobTypes } }"></p>
</fieldset>
<fieldset data-role="controlgroup" data-mini="true">
<input type="radio" id="frequency5" name="frequency" value="5" data-bind="checked: frequencySelection" /><label for="frequency5">Højst 5 gange om ugen</label>
<input type="radio" id="frequency3" name="frequency" value="3" data-bind="checked: frequencySelection" /><label for="frequency3">Højst 3 gange om ugen</label>
<input type="radio" id="frequency1" name="frequency" value="1" data-bind="checked: frequencySelection" /><label for="frequency1">Højst 1 gang om ugen</label>
</fieldset>
<p>
<input type="button" value="Tilmeld" class="nice small radius action button" onClick="subscribe();">
</p>
Tilbage
</div>
</form>
Alternate method of invoking the restyling (doesnt work either):
$(document).on('pagebeforeshow', '#jobmail', function(){
// Get the post from the API
var self = this; //Declare observable which will be bind with UI
// Activates knockout.js
ko.applyBindings(ViewModel);
});
// Handle the DOM Ready (Finished Rendering the DOM)
$("#jobmail").live("pageinit", function() {
$('#jobmail').trigger('pagecreate');
});
Use a custom binding (Knockout) to trigger jQuery Mobile to enhance the dynamically created content produced by Knockout.
Here is a simple custom binding:
ko.bindingHandlers.jqmEnhance = {
update: function (element, valueAccessor) {
// Get jQuery Mobile to enhance elements within this element
$(element).trigger("create");
}
};
Use the custom binding in your HTML like this, where myValue is the part of your view model that changes, triggering the dynamic content to be inserted into the DOM:
<div data-bind="jqmEnhance: myValue">
<span data-bind="text: someProperty"></span>
My Button
<input type="radio" id="my-id" name="my-name" value="1" data-bind="checked: someOtherProperty" /><label for="my-id">My Label</label>
</div>
In my own case, myValue was part of an expression in an if binding, which would trigger content to be added to the DOM.
<!-- ko if: myValue -->
<span data-bind="jqmEnhance: myValue">
<!-- My content with data-bind attributes -->
</span>
<!-- /ko -->
Every dynamically generated jQuery Mobile content must be manually enhanced.
It can be done in few ways, but most common one can be done through the jQuery Mobile function .trigger( .
Example:
Enhance only page content
$('#page-id').trigger('create');
Enhance full page (header + content + footer):
$('#page-id').trigger('pagecreate');
If you want to find more about this topic take a look my other ARTICLE, to be more transparent it is my personal blog. Or find it HERE.