Problems with editable-text and beforeUpdate hook (Meteor) - javascript

folks. I'm working on my first serious Meteor project, and I'm running into a bit of an issue.
I've got a small page here that contains a table of Categories, and I am using editable-text to allow for edit-in-place functionality for a couple of the fields. Editing seems to work, but to handle some verification, I've written/attached a callback to handle the beforeUpdate callback on the limit field for my document.
The callback seems to be...well...called back, but it's not having the effect on my data that I believe it should have. Basically, if a user types in a value of something like 43, I want this callback to force the value to 43.00 (two decimal places mandatory).
This code segment contains the callback itself:
EditableText.registerCallbacks({
// Callback for editing category limit/budget.
budgetCurrency: function(doc) {
// Variables based on editing.
var oldVal = this.oldValue;
var newVal = this.newValue;
// Verification:
if (isNaN(newVal)) {
// Reject value if not a number, don't make change.
oldVal;
} else {
newVal = Number(newVal);
console.log(newVal.toFixed(2));
return _.extend(doc, {limit: newVal.toFixed(2)});
}
}
});
I'm not sure if I'm doing something wrong or not. The value that is returned via console.log() is just as I would expect it to be, however the same value (in code) is not what ends up being stored in the updated document.
Any assistance would be greatly appreciated. I could just be overlooking something as far as how beforeUpdate works (callback), for all I know.
Documentation for the package in question is located at: https://atmospherejs.com/babrahams/editable-text

What is happening is as expected. Javascript Number cannot be stored directly into mongo. It has to be converted to either an int or a float/double. In your case, it is being converted to an int which does not have decimal points.
Here are your options:
Store the value as a String (and loose the ability to do mongo airthmatic operations such as inc etc.)
Store the value as two separate integers divided between dollar and cents for before and after decimal point.
Use float or double and loose accuracy (only use this if you are storing something approximate as this is not suitable for exact financial values)

Related

How to get the text from an Insert event in CKEditor 5?

I am trying to process an insert event from the CKEditor 5.
editor.document.on("change", (eventInfo, type, data) => {
switch (type) {
case "insert":
console.log(type, data);
break;
}
});
When typing in the editor the call back is called. The data argument in the event callback looks like approximately like this:
{
range: {
start: {
root: { ... },
path: [0, 14]
},
end: {
root: { ... },
path: [0, 15]
}
}
}
I don't see a convenient way to figure out what text was actually inserted. I can call data.range.root.getNodeByPath(data.range.start.path); which seems to get me the text node that the text was inserted in. Should we then look at the text node's data field? Should we assume that the last item in the path is always an offset for the start and end of the range and use that to substring? I think the insert event is also fired for inserting non-text type things (e.g. element). How would we know that this is indeed a text type of an event?
Is there something I am missing, or is there just a different way to do this all together?
First, let me describe how you would do it currently (Jan 2018). Please, keep in mind that CKEditor 5 is now undergoing a big refactoring and things will change. At the end, I will describe how it will look like after we finish this refactoring. You may skip to the later part if you don't mind waiting some more time for the refactoring to come to an end.
EDIT: The 1.0.0-beta.1 was released on 15th of March, so you can jump to the "Since March 2018" section.
Until March 2018 (up to 1.0.0-alpha.2)
(If you need to learn more about some class API or an event, please check out the docs.)
Your best bet would be simply to iterate through the inserted range.
let data = '';
for ( const child of data.range.getItems() ) {
if ( child.is( 'textProxy' ) ) {
data += child.data;
}
}
Note, that a TextProxy instance is always returned when you iterate through the range, even if the whole Text node is included in the range.
(You can read more about stringifying a range in CKEditor5 & Angular2 - Getting exact position of caret on click inside editor to grab data.)
Keep in mind, that InsertOperation may insert multiple nodes of a different kind. Mostly, these are just singular characters or elements, but more nodes can be provided. That's why there is no additional data.item or similar property in data. There could be data.items but those would just be same as Array.from( data.range.getItems() ).
Doing changes on Document#change
You haven't mentioned what you want to do with this information afterwards. Getting the range's content is easy, but if you'd like to somehow react to these changes and change the model, then you need to be careful. When the change event is fired, there might be already more changes enqueued. For example:
more changes can come at once from collaboration service,
a different feature might have already reacted to the same change and enqueued its changes which might make the model different.
If you know exactly what set of features you will use, you may just stick with what I proposed. Just remember that any change you do on the model should be done in a Document#enqueueChanges() block (otherwise, it won't be rendered).
If you would like to have this solution bulletproof, you probably would have to do this:
While iterating over data.range children, if you found a TextProxy, create a LiveRange spanning over that node.
Then, in a enqueueChanges() block, iterate through stored LiveRanges and through their children.
Do your logic for each found TextProxy instance.
Remember to destroy() all the LiveRanges afterwards.
As you can see this seems unnecessarily complicated. There are some drawbacks of providing an open and flexible framework, like CKE5, and having in mind all the edge cases is one of them. However it is true, that it could be simpler, that's why we started refactoring in the first place.
Since March 2018 (starting from 1.0.0-beta.1)
The big change coming in 1.0.0-beta.1 will be the introduction of the model.Differ class, revamped events structure and a new API for big part of the model.
First of all, Document#event:change will be fired after all enqueueChange blocks have finished. This means that you won't have to be worried whether another change won't mess up with the change that you are reacting to in your callback.
Also, engine.Document#registerPostFixer() method will be added and you will be able to use it to register callbacks. change event still will be available, but there will be slight differences between change event and registerPostFixer (we will cover them in a guide and docs).
Second, you will have access to a model.Differ instance, which will store a diff between the model state before the first change and the model state at the moment when you want to react to the changes. You will iterate through all diff items and check what exactly and where has changed.
Other than that, a lot of other changes will be conducted in the refactoring and below code snippet will also reflect them. So, in the new world, it will look like this:
editor.document.registerPostFixer( writer => {
const changes = editor.document.differ.getChanges();
for ( const entry of changes ) {
if ( entry.type == 'insert' && entry.name == '$text' ) {
// Use `writer` to do your logic here.
// `entry` also contains `length` and `position` properties.
}
}
} );
In terms of code, it might be a bit more of it than in the first snippet, but:
The first snippet was incomplete.
There are a lot fewer edge cases to think about in the new approach.
The new approach is easier to grasp - you have all the changes available after they are all done, instead of reacting to a change when other changes are queued and may mess up with the model.
The writer is an object that will be used to do changes on the model (instead of Document#batch API). It will have methods like insertText(), insertElement(), remove(), etc.
You can check model.Differ API and tests already as they are already available on master branch. (The internal code will change, but API will stay as it is.)
#Szymon Cofalik's answer went into a direction "How to apply some changes based on a change listener". This made it far more complex than what's needed to get the text from the Document#change event, which boils down to the following snippet:
let data = '';
for ( const child of data.range.getChildren() ) {
if ( child.is( 'textProxy' ) ) {
data += child.data;
}
}
However, reacting to a change is a tricky task and, therefore, make sure to read Szymon's insightful answer if you plan to do so.

What is the role of variables (var) in Javascript

I am currently learning JavaScript and I am wondering what is the role of the variables (var).
In the example bellow, on the last two lines we first define a variable "monCompte" in which we call "john.demandeCaissier(1234)". Then we use console.log(monCompte) to print the result on the screen. What I don't understand is why do we first need to define the variable "monCompte" to call "john.demandeCaissier(1234)". Why can't we just do something such as:
console.log(john.demandeCaissier(1234));
Example
function Personne(prenom,nom,age) {
this.prenom = prenom;
this.nom = nom;
this.age = age;
var compteEnBanque = 7500;
this.demandeCaissier = function(mdp) {
if (mdp == 1234) {
return compteEnBanque;
}
else {
return "Mot de passe faux.";
}
};
}
var john = new Personne('John','Smith',30);
var monCompte = john.demandeCaissier(1234);
console.log(monCompte);
Thank you for you answers.
Yes, you can inline your function call and avoid the need for a variable. However, if an error occurs on that line, it becomes harder to debug:
var monCompte = john.demandeCaissier(1234);
console.log(monCompte);
vs
console.log(john.demandeCaissier(1234));
in the second example, there are several different modes of failure that would not be apparent in a debugging session. When split over two lines, some of those failures become easier to track down.
Second, if you wanted to reuse the value returned by john.demandeCaissier(1234) (the author might have shown this), then a variable becomes very useful indeed.
In my opinion, it's a worthy pursuit to perform only a single operation per line. Fluent-style advocates might disagree here, but it really does make debugging considerably easier.
You could definitely do that, but in more complex programs you will need to store variables for several reasons:
Shortening Long Expressions
Imagine if you saw this code somewhere:
console.log((parseInt(parseFloat(lev_c + end_lev_c)) - parseInt(parseFloat(lev_c + start_lev_c)) + 1));
BTW I got that from here
Wouldn't it be so much simpler just to split that expression up into different variables?
Storing Data
Let's say that you take some input from the user. How would you refer to it later? You cannot use a literal value because you don't know what the user entered, so do you just call the input function again? No, because then it would take the input a second time. What you do is you store the input from the user in a variable and refer to it later on in the code. That way, you can retrieve the value at any time in the program.
If you are a beginner, you might not see any use for variables, but when you start writing larger programs you will start to use variables literally in almost every line of code.
Variables exist to store data. They're useful because instead of invoking an operation over and over again, which is criminally inefficient, they allow you to invoke an operation once, and then use that result where necessary.
And that's for all languages, not just JavaScript.
Variables are structures that store some value (or values). They're only that and you could probably do all your code (or the majority of it) without them.
They help you organize and add some readability to your code. Example:
alert(sumNumbers(askNumber()+askNumber()));
takes a lot more effort to read/understand then this:
var firstNumber = askNumber();
var secondNumber = askNumber();
var total = sumNumbers(firstNumber + secondNumber);
alert(total);
Sure it's longer but it's more readable. Of course you don't have to use var for everything, in this case I could just hide the total.
Another common use for variables is "caching" a value.
If you had a function that sums like 1 million values, if you keep calling it for everything, your code would always have to do all that hard work.
On the other hand, if you store it on a variable the first time you call it, every other time you need that value again, you could just use the variable, since its a "copy" of that calculation and the result is already there.

Breezejs Double trouble in angularjs

I am trying to change a value in a angular view from a integer to a float/double value that is bind to ngmodel. The input don’t except anything other than a integer.
My guess is that breeze does something in the background to validate the value or something on the "defined properties". But my knowledge of JavaScript prototyping is very limiting aka I need to learn it..
This is really hard to explain so I created a plunk that can hopefully help: http://plnkr.co/edit/Gcj0VvBE3f8DRbIjMtqt?p=preview
In the plunk I also added a normal object to test the same values and it is working as expected when changing the numbers to floats/doubles.
So the question is why won’t the value changed when binding to a float/double value from breeze?
I've checked a preliminary fix into GitHub for this. Please check it out and let me know if it works ( or not). We are still testing it.
This issue is caused by Angular's (new) behavior where if the angular digest cycle does not see a change to a model property then it seems to reset the UI to what it was on the previous digest cycle. So.. the idea behind this fix is to convince angular that the model value has changed even when it hasn’t.
and.. nice catch ( this was not obvious and your plunkr helped) :)
Ward adds: You must love the breeze team responsiveness :-) Jay jumped right on this and came up with an interesting solution. But please note the word "preliminary" in Jay's answer. We are discussing this "fix" internally and it may be withdrawn. Consider the zEquivalent directive in this new plunker or just wait until the dust settles.
Update 12 March
I found what I believe is a better solution for your use case because it does not involve "debouncing" nor any change to Breeze. See the zEquivalent directive in this new plunker
Reminder: the "best" resolution of the problem you discovered is still up in the air within the Breeze core team. You should not lock into a particular outcome until we can make a more definitive recommendation.
p.s.: I should have mentioned that Jay and I are on the Breeze core team. We are doing our best to get you out of a jam but sometimes we move a wee too quickly. Bear with us please.
This answer deprecated as of 12 March
Leaving it here for "historical" purposes.
I think you should try the zEquivalent directive first.
It is important to know that the Angular team is working on a robust extension to data binding that includes "debounce" which is still a good idea for many scenarios. See this (long) pull request thread on the Angular GitHub site.
Original Answer
Consider the zDebounce directive currently located in this plunker which is based on #qorsmond's second attempt.
I'd like to know your thoughts. I'm inclined to call this "the solution" and to add it to Breeze Labs. I'll update this answer when/if I do add it to the labs.
you can use input type="number" step="any" and hide the arrows using css
I tracked down the behaviour in the breeze code, when the value change it is intercepted and parsed:
var coerceToFloat = function (source, sourceTypeName) {
if (sourceTypeName === "string") {
var src = source.trim();
if (src === "") return null;
var val = parseFloat(src);
return isNaN(val) ? source : val;
}
return source;
};
So when the value change this function is called that parse the string to a float, the problem is as soon as the value entered is some number and a point like 5. it parse it to the number 5 witch is obviously correct. So you will never be able to get past the point.
When changing the input to a number type it works because its sourceTypeName is not a string.
Update
I ended up changing the breeze code to enable the decimal to be entered, I’m still not sure if I missed something but this works for me.
var coerceToFloat = function (source, sourceTypeName) {
if (sourceTypeName === "string") {
var src = source.trim();
if (src === "") return null;
var val;
if (src.indexOf('.', src.length - 1) !== -1) {
val = src;
}
else {
val = parseFloat(src);
}
return isNaN(val) ? source : val;
}
return source;
};
This doesn't work for large or small numbers that might be entered using an exponent form. If I want to enter 2.55e35 (a large salary ;)) the current implementation stops at the 'e'. Is there an easy way to fix this?

javascript var limit, value not exceeding 127

I am facing a strange issue in the below javascript code. From a page, the values which are getting pushed in project_list array are project_id (value range from 1 to 150) and bid_amount (value range from 1 to 2000). Everything works fine apart from one thing, whenever value of project_id (sent from page) is greater than 127, the value getting inserted in code line project_id = project_list[i]; is 127. As I checked, var does not have such a low limit. Also this code line bid_amount = parseFloat($("#bidAmount_"+project_list[i]).val()); works fine, easily taking value greater than 127. Please help, I have not declared shortint anywhere!
function bid_project_tournament(current_round)
{
var i;
var project_list= [];
$('#B2WProjectList :checkbox:checked').each(function(i){
project_list.push($(this).val());
});
$("#bid_project").attr("disabled","disabled");
var project_id;
var bid_amount;
var bid_data="0";
for(i=0;i<project_list.length;i++)
{
project_id = project_list[i];
bid_amount = parseFloat($("#bidAmount_"+project_list[i]).val());
bid_data = bid_data+";"+project_id+","+bid_amount;
}
}
We figured out the reason for this specific problem in the comments (a column with the type TINYINT instead of INT), but I'll leave a more general answer in case people come here with similar problems.
If you find that numbers are being capped at or around some power of two when sent to the server, there's a good chance the problem lies somewhere in your back-end code or database. Here are some things to check:
Have you accidentally set a column in your database to BYTE, TINYINT, SMALLINT or similar instead of INT or larger? Check your column definitions!
Are you passing the value to a variable in your back-end code which has a type like short, word, byte or similar?
If the values are in pre-filled HTML form fields, have you checked if the values in that HTML are correct?
If everything above checks out, fire up your browser's debug tools and watch the network activity panel to confirm if the values are being capped on the client.
There's no (reasonable) limit to the value of a checkbox in HTML, or indeed to any field without any restriction like a max or maxlength attribute. The biggest number you can represent in JavaScript without losing precision is 9,007,199,254,740,992, so if your numbers are being capped or rounded above that value, the problem might be JavaScript.

How to convert a string equation to a numeric value with Javascript

Basically, I have a user input field where a user can enter a number. They would like to also be able to enter equations in the input field as well.
Something like "874.45 * 0.825 + 4000" and have that converted to its real numeric value.
All the solutions I found point to the dreaded eval() method. Especially with this being a user entered field, I'm concerned about just running eval("874.45 * 0.825 + 4000") and hoping to get a number out the back end.
I suppose I could do a web service call back to the server (ASP.NET), but I'm afraid a slight delay will create some frustration from the user.
Does anyone know of either a good technique or existing libraries?
What you really need here is an "expression parser", because you're trying to allow users to express their values using a small domain-specific language.
The basic mechanics work like this:
Tokenize their expression into operators and operands.
Based on the order of operations (e.g, where multiplication is evaluated with higher priority than addition), push the operators and operands onto a stack.
Pop the operands from the stack and push intermediate results back onto the stack. Repeat until the stack is empty.
I've done this a gazillion times for different "little languages" throughout my projects. But never in javascript. So I can't directly recommend a suitable parsing library.
But a quick googling reveals "PEG.js". Check it out here:
http://pegjs.majda.cz/
All of the examples in their documentation are for exactly the kind of "expression parser" you're trying to build.
Simply multiply it by 1 and it will force javascript to treat it as an integer from then on.
Eg
int = '2343.34' * 1;
int = input * 1;
And what is so wrong about the eval in this case?
As for me it perfectly fits in your task. If you want to shield its execution context then you can define function like:
function calc(str) {
var window = null, self = null, document = null;
// other globals like: $ = null, jQuery = null, etc.
try { return eval(str); } catch(e) {...}
}
and use it where you need to interpret the string. Simple and effective.
I think eval can pose a lesser security risk if you parse the resulting string and validate its content to be only digits and operators and execute the evaluation by faking the outer scope variables like document etc as 'var document = null'.

Categories

Resources