I'm using Knockout MVC im my project (ASP.NET MVC3, Razor). I can't find now to change decimal format. I want to use comma as a decimal separator. When I bind data using non-knockout Razor helper, it renders it correctly (with comma), but when I bind using Knockout binding it renders the number with the dot as comma separator.
How to change the decimal format that is would use comma?
You're looking at the differences between your server and client locale.
Your .NET code formats numbers with respect to the server locale whereas your JS code formats numbers with respect to the browsers locale.
Try changing the locale/region within your browser.
EDIT:
(I am leaving the above in case it helps anyone else, even though it did not help you)
The issue is your understanding of the differences between server-side and client-side.
Razor code is executed on the server and 'translated' into HTML.
Whereas, the server treats Javascript as text and just part of the HTML document it is sending.
Javascript is executed on the clients machine (i.e. the browser).
How the Razor Helper formats the number is based on the locale set on the server. Whereas, the javascript will format the number based on the locale set in the browser.
To force Knockout/Javascript to format the number how you want regardless of the locale (on the client-side), you can write a custom-binding using the following method at it's core:
function formatWithComma(x, precision, seperator) {
var options = {
precision: precision || 2,
seperator: seperator || ','
}
var formatted = x.toFixed( options.precision );
var regex = new RegExp('^(\\d+)[^\\d](\\d{' + options.precision + '})$');
formatted = formatted.replace(regex, '$1' + options.seperator + '$2');
return formatted;
}
So your binding will look something like this:
ko.bindingHandlers.commaDecimalFormatter = {
init: function(element, valueAccessor) {
var observable = valueAccessor();
var interceptor = ko.computed(function() {
return formatWithComma( observable() );
}
ko.applyBindingsToNode( element , { value: interceptor } );
}
}
And then in your Razor view:
#ko.Bind.Custom("commaDecimalFormatter ", m => m.MyCustom)
(Please note, I've never used KnockoutMVC and so this last line is straight from the documentation with the binding name changed - it is untested.
Also, I have used a ko.computed and so this binding is read-only - using it on an <input> element will be pointless. It's better used on a <span>. You can make this a two-way binding implementing the reverse:
ko.computed( {
read: function() {
/* method as above */
},
write: function(newValue) {
/* implement reverse */
observable( newValue );
}
}
EDIT 2
Hope this fiddle makes it clearer
Related
I'm trying to integrate a custom language to monaco editor and I went through https://microsoft.github.io/monaco-editor/monarch.html to get an idea on syntax highlighting.
But I couldn't find any doc on how we can add error/warning validations through syntax validation for this. In Ace editor we did this by writing a worker and performing validation function within it. Appreciate any links/help on this.
I recently done this successfully i just used monaco-css as boiler-plate and the only thing that i have to do now is write a parser for my language and other features that I want in in it. and here is my code.
Add your parser and other language services in lang_services folder in root dir of project.
I think it would be helpful.
Here is a succinct and easily customizable example that will display an error at position 2-5 of line 1 like so:
Just insert this code at the top (not bottom) of the playground code at https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages:
monaco.editor.onDidCreateModel(function(model) {
function validate() {
var textToValidate = model.getValue();
// return a list of markers indicating errors to display
// replace the below with your actual validation code which will build
// the proper list of markers
var markers = [{
severity: monaco.MarkerSeverity.Error,
startLineNumber: 1,
startColumn: 2,
endLineNumber: 1,
endColumn: 5,
message: 'hi there'
}];
// change mySpecialLanguage to whatever your language id is
monaco.editor.setModelMarkers(model, 'mySpecialLanguage', markers);
}
var handle = null;
model.onDidChangeContent(() => {
// debounce
clearTimeout(handle);
handle = setTimeout(() => validate(), 500);
});
validate();
});
// -- below this is the original canned example code:
// Register a new language
Note that for simplicity, this example ignores the consideration that onDidCreateModel and onDidChangeContent both return IDisposable objects which you may need to track and dispose of.
I’m trying to build this:
When I edit field on the left it should update the one on the right and vice-versa.
Editing a value in an input field causes the text cursor to jump at the end of it.
Typing "2" in the fahrenheit field gets replaced with 1.999999999999, as you can see on the screenshot. This happens because of the double conversion:
view’s Fº → model’s Cº → view’s Fº.
How can I avoid that?
Update:
I want to know the elegant way of dealing with two-way bindings in MVC frameworks such as Backbone.js.
MVC
var Temperature = Backbone.Model.extend({
defaults: {
celsius: 0
},
fahrenheit: function(value) {
if (typeof value == 'undefined') {
return this.c2f(this.get('celsius'));
}
value = parseFloat(value);
this.set('celsius', this.f2c(value));
},
c2f: function(c) {
return 9/5 * c + 32;
},
f2c: function(f) {
return 5/9 * (f - 32);
}
});
var TemperatureView = Backbone.View.extend({
el: document.body,
model: new Temperature(),
events: {
"input #celsius": "updateCelsius",
"input #fahrenheit": "updateFahrenheit"
},
initialize: function() {
this.listenTo(this.model, 'change:celsius', this.render);
this.render();
},
render: function() {
this.$('#celsius').val(this.model.get('celsius'));
this.$('#fahrenheit').val(this.model.fahrenheit());
},
updateCelsius: function(event) {
this.model.set('celsius', event.target.value);
},
updateFahrenheit: function(event) {
this.model.fahrenheit(event.target.value);
}
});
var temperatureView = new TemperatureView();
No MVC
celsius.oninput = function(e) {
fahrenheit.value = c2f(e.target.value)
}
fahrenheit.oninput = function(e) {
celsius.value = f2c(e.target.value)
}
function c2f(c) {
return 9/5 * parseFloat(c) + 32;
}
function f2c(f) {
return 5/9 * (f - 32);
}
Not only it fixes the problem, it’s also reduces the code 3.5⨉. Clearly I’m doing MVC wrong.
Here's my take on this; instead rendering the whole view on every change, in interactive views, use the view's jQuery or plain JS contexts just like your non-MVC example.
http://jsbin.com/fomugixe/1/edit
As the Backbone docs say:
"Two way data-binding" is avoided. While it certainly makes for a nifty demo, and works for the most basic CRUD, it doesn't tend to be terribly useful in your real-world app. Sometimes you want to update on every keypress, sometimes on blur, sometimes when the panel is closed, and sometimes when the "save" button is clicked.
Two methods come to mind. As Kinakuta mentioned you can do something like the following so you're math works on integers, instead of decimals:
temp = ((oldTemp * 100) * conversion stuff) / 100
Depending on how complex you want your app to be you can also use something like Backbone.ModelBinder. It automatically binds your view to your model so when one updates, the other updates automatically. You can then attach a converter function to the binding so when your value goes view -> model or model -> view it's run through the converter. I can elaborate more if that idea interests you.
Update: With a simple temp converter it's not surprising that Backbone requires 3.5x as much code. An MVC framework can reduce bloat in a large project, but for a small app it might be overkill. e.g. imagine using Backbone to display "Hello World".
As for your issue, how about only rendering the other input value when one is changed, instead of both? If F input changes, re-render value in C box. With ModelBinder I would do this by having two attributes in my model: tempF and tempC. When one is modified, I re-calculate the other and ModelBinder automatically displays it. Or you can go without MB and just listen for the change event.
set a variable at the view level where you hold the input field that started the conversion, so you don't call the conversion function on that field.
As far as I can tell, Backbone.js view represents DOM element. I take it from existing DOM or create it on the fly in el attribute.
In my case, I want to take it from server with AJAX request because I'm using Django templates and don't want to rewrite everything to JavaScript templates.
So I define el function that performs AJAX request.
el: function() {
model.fetch().success(function(response) {
return response.template
})
}
Of course, it does NOT work because AJAX request is executed asynchronous.
This means that I don't have el attribute and events does NOT work neither. Can I fix it?
Maybe the Backbone.js framework isn't the right tool for my needs? The reason I want to use that was to have some structure for the code.
P.S. I'm new to Backbone.js.
Do your ajax request from another view, or directly after the page load using jquery directly, and after you've downloaded your template, THEN instantiate your backbone view class with the proper id/el or whatever (depending on where you stored your ajax fetched template). Depending on your use-case, this may or may not be a sensible approach.
Another, perhaps more typical approach, would be to set up your view with some placeholder element (saying "loading" or whatever), then fire off the ajax, and after the updated template has been retrieved, then update your view accordingly (replace the placeholder with the actual template you requested).
When/if you update your view with new/other DOM elements, you need to call the view's delegateEvents method to rebind your events to the new elements, see:
http://backbonejs.org/#View-delegateEvents
I came across a similar requirement. In my instance, I was running asp.net and wanted to pull my templates from user controls. The first thing I would recommend is looking into Marionette because it will save you from writing a lot of boiler plate code in Backbone. The next step is to override how your templates are loaded. In this case I created a function that uses Ajax to retrieve the HTML from the server. I found an example of this function where they were using it to pull down html pages so I did a little modification so I can make MVC type requests. I can't remember where I found the idea from; otherwise, I would give the link here.
function JackTemplateLoader(params) {
if (typeof params === 'undefined') params = {};
var TEMPLATE_DIR = params.dir || '';
var file_cache = {};
function get_filename(name) {
if (name.indexOf('-') > -1) name = name.substring(0, name.indexOf('-'));
return TEMPLATE_DIR + name;
}
this.get_template = function (name) {
var template;
var file = get_filename(name);
var file_content;
var result;
if (!(file_content = file_cache[name])) {
$.ajax({
url: file,
async: false,
success: function (data) {
file_content = data; // wrap top-level templates for selection
file_cache[name] = file_content;
}
});
}
//return file_content.find('#' + name).html();
return file_content;
}
this.clear_cache = function () {
template_cache = {};
};
}
The third step would be to override Marionette's method to load templates. I did this in the app.addInitializer method. Here I am initializing my template loader and setting it's directory to a route handler. So when I want to load a template, I just set the template: "templatename" in my view and Backbone will load the template from api/ApplicationScreens/templatename. I am also overriding my template compiling to use Handlebars because ASP.net is not impressed with the <%= %> syntax.
app.JackTemplateLoader = new JackTemplateLoader({ dir: "/api/ApplicationScreens/", ext: '' });
Backbone.Marionette.TemplateCache.prototype.loadTemplate = function (name) {
if (name == undefined) {
return "";
} else {
var template = app.JackTemplateLoader.get_template(name);
return template;
}
};
// compiling
Backbone.Marionette.TemplateCache.prototype.compileTemplate = function (rawTemplate) {
var compiled = Handlebars.compile(rawTemplate);
return compiled;
};
// rendering
Backbone.Marionette.Renderer.render = function (template, data) {
var template = Marionette.TemplateCache.get(template);
return template(data);
}
Hopefully this helps. I've been working on a large dynamic website and it is coming along very nicely. I am constantly being surprised by the overall functionality and flow of using Marionette and Backbone.js.
I'm new to jqGrid and jquery and i'm learning as fast as i can but i still am a little lost about how to some things like how to append addition information to the post data in jqgrid that gets sent to php.
It would be nice for the php script to know what columns the grid wants when it initially loads or you press the jqgrid refresh/reload button.
I know i can use the postData option: postData:{name:val,,,}, but i was hoping to just automatically pull the column names from the colModel definitions using this function...
postData: function(){
colmodel = $('#tab4-grid').jqGrid('getGridParam','colModel');
colarray = '{';
for (var i in colmodel) {colarray += '"'+colmodel[i].name+'":"'+colmodel[i].name+'",';}
colarray += '}';
return colarray;
},
so i would not have to spell them out manually again. However, while the function produces the correct code, it's not getting posted. I can't seem to figure out the problem. Can someone help please?
thanks.
The first thing which you have to do is to rewrite
postData: function() { ... }
to
postData: {
myColumnsName: function() { ... }
}
jqGrid already send some standard parameters to the server (page, rows ,...). With the code above you will add and additional parameter with the name myColumnsName which you can fill in any way inside of the function body.
You current implementation is very dirty. You don't define local variables colmodel and colarray. Moreover you try to serialize array of strings as object ('{}') and not as array ('[]'). You should not use for (var i in colmodel) construction if you enumerate array items for (var i=0; i<colmodel.length; i++) is better. Additionally 'colModel' contain some column names ('rn', 'cb', 'subgrid') which you should skip in the enumeration. You can define as var colNames = []; and use colNames.push to fill it. Then you can use standard JSON.stringify method from json2.js to convert array to the JSON string.
I'm using the Obout.com MVC controls and have included the following code in one of my views:
#{
Html.Obout(new ComboBox("Languages") {
Width = 175,
SelectedIndex = (int) ViewData["DefaultLanguage"] - 1,
ShowSelectedImage = true
}
);
}
I'm doing it that way because my original attempt failed:
#Html.Obout(new ComboBox("Languages") { Width = 175, SelectedIndex = (int) ViewData["DefaultLanguage"] - 1, ShowSelectedImage = true })
...it seems I need to use the #{} structure. However, when the output gets generated, the code that Html.Obout() generates comes ahead of all other output. the <!DOCTYPE html> and the real page follows the control's output. is this a function of the #{} structure, or is it some issue with the control itself?
It looks like this method was designed for ASPX views and writes directly to HttpContextBase.Response.OutputStream.
Since Razor buffers its output in WebPageBase.Output, you will not easily be able to use these helpers in Razor.
You could put them in a separate ASCX partial view, and they will work.
Depending on how the helpers are implemented, you may be able to force them to write to WebPageBase.Output; since I don't use Obout, I don't know.
The Razor compatible version of the Obout MVC ComboBox will be available soon:
http://forum.obout.com/yaf_postsm2112_Examples-pleease.aspx#post2112