Why do the ngModelCtrl.$formatters get run from last to first - angularjs

The ngModelCtrl.$parsers gets run through from the first parser in the array to the last parser in the array, while ngModelCtrl.$formatters gets run through from the last formatter in the array to the first formatter in the array. Just wondering the rationale behind this.
Snippet from angular.js (v1.2.1):
...
var formatters = ctrl.$formatters,
idx = formatters.length;
ctrl.$modelValue = value;
while(idx--) {
value = formatters[idx](value);
}
...

That allows to always push the parsers and the formatters.
Let's say you have a parser that transforms the entered string into a number of milliseconds, and then another parse that transforms the milliseconds into a Date.
You'll need corresponding formatters: one that transforms a Date into a number of milliseconds, and a second one that transforms the milliseconds into a String. Having the formatters run in the inverse order of the parsers makes sense: you can simply do
ctrl.$parsers.push(stringToMillis);
ctrl.$formatters.push(millisToString);
...
ctrl.$parsers.push(millisToDate);
ctrl.$formatters.push(dateToMillis);

Related

How to get raw date string from date picker?

I'm struggling for hours with this seemingly trivial issue.
I have a antd datepicker on my page.
Whenever I choose a date, instead of giving me the date I chose, it gives me a messy moment object, which I can't figure out how to read.
All I want is that when I choose "2020-01-18", it should give me precisely this string that the user chose, regardless of timezone, preferably in ISO format.
This is not a multi-national website. I just need a plain vanilla date so I can send it to the server, store in db, whatever.
Here are some of my trials, so far no luck:
var fltval = e;
if (isMoment(fltval)) {
var dat = fltval.toDate();
//dat.setUTCHours(0)
fltval = dat.toISOString(); // fltval.toISOString(false)
var a = dat.toUTCString();
//var b = dat.toLocaleString()
}
It keeps on moving with a few hours, probably to compensate for some timezone bias
UPDATE 1:
the datestring is data-wise correct. But its not ISO, so I cant use it correctly. I might try to parse this, but I cannot find a way to parse a string to date with a specific format.
UPDATE 2:
I also tried adding the bias manually, but for some reason the bias is 0
var dat = pickerval.toDate()
var bias = Date.prototype.getTimezoneOffset()// this is 0...
var bias2 = dat.getTimezoneOffset()// and this too is 0
var d2 = new Date(dat.getTime()+bias)
var mystring= dat.toISOString() //still wrong
Thanks!
Javascript date functions can be used,
I assume you are getting in 2022-01-03T11:19:07.946Z format then
date.toISOString().slice(0, 10)
to 2022-01-03
There are 2 ways to get the date string:
Use the moment.format api:
date.format("yyyy-MM-DD")
Use the date string that is passed to the onChange as second parameter
Here is a Link.
I am assuming your code snippet is inside the onChange method. This gives you a moment and a date string to work with (the first and second parameters of the function respectively).
You have a few options. You could set the format prop on the DatePicker to match the format of the string you want. Then just use the date string. Or you can use the moment object as Domino987 described.

Swift Array multiple appending on click

I'm creating a button that when clicked adds the current date and time to an array but when I try to append to the array it only appends once and not repeating the process
the Entries struct:
struct Enteries {
var dates:[String] = []
}
convert date to String:
func DateConverter(){
format.timeZone = .current
format.dateFormat = "yyyy-MM-dd HH:mm"
dateString = format.string(from: currentDate)
}
The function that appends: also its called later whenever an IBAction is triggered
func AddToDatabase () {
var entery = Enteries()
entery.dates.append(dateString)
print(entery.dates)
}
`
Yikes, there's a lot going on here.
First of all, Swift's convention is to use lowerCamelCase for functions. Only type names should be UpperCamelCase.
Secondly, function names should be verbs or verb phrases, type names should be nouns. If I saw DateConverter in some code, I would expect it to be a type. It's an UpperCamelCase noun, that's how types should be named. But yours is a function (which would be a total surprise to every other Swift developer, because it violates the expectations they've built up from Swift's naming conventions), that function should probably be called parseDate.
Which way does DateConverter convert? From String to Date, Date to String, or both? What's its input? What's it's output? These things should be obvious from a good function name, but are totally unknown here without looking at the implementation.
Critically, the DateConverter function doesn't take input from parameters, and doesn't return a result, instead it takes input from a side effect (accessing the variable currentDate) and returns a result via side effect (writing to an a variable dateString). This is really bad, for several reasons:
It's not reusable. You have no way to use this date parsing code somewhere else without copy/pasting it, which is how code duplication and complexity arise. If you ever decide to change the date format in your app, you won't have a central source-of-truth that you can change, instead you'll have to manually hunt down every copy of this function, and change it, hoping you don't miss any. Not good.
It's not thread safe
It's more complex than a simple function that has type (Date) -> String. It obfuscates what's going on.
It defies peoples' expectations, without justification.
Enteries.dates has a default value of [], which doesn't seem to be a good idea if you're going to be appending to it as soon as you create it. Instead, take the array via an initializer parameter.
Enteries.dates has type [String]. Why?! You already have Date objects, store those!
They're smaller (in memory)
They're presentation-agnostic, meaning you can properly format them for different interfaces and different locales at a later time, as necessary
They support date math. I often see people storing dates as strings, and ask questions like "How do I sort my array of dates?" (which are actually stored as strings), "How do I add 1 day to "2019-12-24", and they start doing funky parsing, splitting, joining, and it's all just an absolute mess
Here's how I would improve this code:
struct Entries {
var entries: [Entry]
}
struct Entry {
let date: Date
}
// Call this from your view layer, only when you're about to present a `Date` to a user.
func parse(date: Date) -> String {
let df = DateFormatter()
df.timeZone = .current
df.dateFormat = "yyyy-MM-dd HH:mm"
return format.string(from: currentDate)
}
var entries = Entries(entries: [])
func addToDatabase(entry: Entry) {
entries.append(entry)
print(enteries.entries)
}
you are creating a new entery object eveytime the function is called. SO its creating a new object everytime. Declare your entery object outside the function.
var entery = Enteries()
func AddToDatabase () {
entery.dates.append(dateString)
print(entery.dates)
}

proper way to get the last element of the array when split is used on the string within JSON

I have a JSON response from the server and I am using map to use only necessary key:valuepairs in Angular (typescript) that will be used to display on the Frontend side.
here bizStep is actually according to a standard (EPCIS) and has the following value:
urn:epcglobal:cbv:bizstep:receiving
I only want to the user to read receiving hence I used split and obtained the last value of the array to display the value.
The logic is shown below:
this.serv.getEpcisInfo(code) // HTTP GET Service from Angular
.subscribe(res => {
this.data = res.map(el => { // map only some key value pairs now!
return {
'business step': el.bizStep.split(':')[el.bizStep.split(':').length - 1]
});
});
But it is observed that in order to obtain the overall length of the splited string array I have to write the expression el.bizStep.split(':') twice.
Is there a shorthand or elegant expression to obtain the last string value of the array.
I did try to use el.bizStep.split(':')[-1] however this expression failed and did not provide me any value.
You can use Array.pop since you don't need to preserve the result of the split, i.e. el.bizStep.split(':').pop().
A more general approach would be to use an anonymous function, e.g.:
(s => s[s.length-1])(el.bizStep.split(':'))
You could modify this to get elements other than the last. Of course, this example has no error checking on the length or type of el.bizStep.

Compare array with sorted Array, pick first element

The setup is the following :
targets = ['green','orange','red']; //targets are in order of priority
sources = ['redalert','blackadder','greenlantern'];
I am trying to make a function that returns the one source element which contains the highest priority target string. In this case, it would be 'greenlantern', as it contains the string 'green', which has higher priority than 'red' found in 'redalert'.
I have done it already using for loops and temp arrays, but I know these manipulations aren't my forte, and my real-life arrays are way larger, so I'd like to optimize execution. I have tried with Lodash too, but can't figure out how to do it all in one step. Is it possible?
The way I see it, it has to :
for each target, loop through sources, if source elem matches target elem, break and return.
but I'm sure there's a better way.
Here's another lodash approach that uses reduce() instead of sortBy():
_.reduce(targets, function(result, target) {
return result.concat(_.filter(sources, function(source) {
return _.includes(source, target);
}));
}, []);
Since targets is already in order, you can iterate over it and build the result in the same order. You use reduce() because you're building a result iteratively, that isn't a direct mapping.
Inside the reduce callback, you can concat() results by using filter() and includes() to find the appropriate sources.
This gets you the sorted array, but it's also doing a lot of unnecessary work if you only want the first source that corresponds to the first target:
_.find(sources, _.ary(_.partialRight(_.includes, _.first(targets)), 1));
Or, if you prefer not to compose callback functions:
_.find(sources, function(item) {
return _.includes(item, _.first(targets));
});
Essentially, find() will only iterate over the sources collection till there's a match. The first() function gets you the first target to look for.
Keeping it very simple:
var sortedSources = _.sortBy(sources, function(source){
var rank = 0
while(rank < targets.length){
if(source.indexOf(targets[rank]) > -1){
break
}else{
rank++
}
}
return rank
})
Sources are now sorted by target priority, thus sortedSources[0] is your man.

Overriding Ext.util.Format.thousandSeparator except when formatting money

I am trying to override Ext.util.Format.decimalSeparator and thousandSeparator. So, in my app, when I chnage the language to Spanish and I try using this function, Ext.util.Format.number(1234,'$0,000'), still it converts the number to 1.234 instead of 1,234.
I want that, irrespective of what language I choose, it should always format the money to $0,000 format and not using my selected locale, e.g., never $0.000. I observed if I change the thousandSeparator of Ext.util.Format object, it works fine. So, I added the following code in Ext.Loader.loadScript callback function in launch function in Application.js,
var utilFormatObj={};
utilFormatObj.thousandSeparator = ",";
utilFormatObj.decimalSeparator = ".";
Ext.override(Ext.util.Format, utilFormatObj);
BUt, it seems to work only in this place, once it loads the app on webpage, it again gets back to thousandSeparator=".". I can see that ext-lang-es.js file has the function which sets these properties. Can anyone suggest how can I catch whether the app is completely loaded on webapge and then use the above code there. Thank you.
When you call Ext.util.Format.number() you're not specifying what to use as decimal or thousand separator, you're only specifying precision, whether to show thousands separator, and whether to pad precision with zeroes.
The documentation for Ext.util.Format.number states:
The format string must specify separator characters according to US/UK conventions ("," as the thousand separator, and "." as the decimal separator)
Therefore if you want to display numbers in different locales, you have to run the code that changes the default separators before calling Ext.util.Format.number or Ext.util.Format.currency.
var value = 202020.20, format = "0,000.0000";
// Print in Spanish
Ext.util.Format.thousandSeparator = ".";
Ext.util.Format.decimalSeparator = ",";
alert(Ext.util.Format.number(value, format));
// Print in Swedish French
Ext.util.Format.thousandSeparator = "'";
Ext.util.Format.decimalSeparator = ",";
alert(Ext.util.Format.number(value, format));
// Print in English
Ext.util.Format.thousandSeparator = ",";
Ext.util.Format.decimalSeparator = ".";
alert(Ext.util.Format.number(value, format));
Here's a hack you can use if you really want to specify that currency should always use a period as the thousand separator but still have Ext.util.Format.number use the selected locale's separators.
function formatMoney(amount, sign, decimals, end) {
// Save the thousand separator
var thousandSep = Ext.util.Format.thousandSeparator;
Ext.util.Format.thousandSeparator = '.';
var formatted = Ext.util.Format.currency(amount, sign, decimals, end);
// restore the thousand separator
Ext.util.Format.thousandSeparator = thousandSep;
return formatted;
}
Example for the above code snippets: https://fiddle.sencha.com/#fiddle/9vm
I am guessing that you are not using the loader after you build your application for deployment. Typically the dynamic loader is only used for development (so you can see each script individually) and you use a faster method in prod.
You could load your Ext overrides on the callback for Ext.define:
Ext.define( className, data, [createdFn] )
where createdFn is a function that contains your Ext overrides. This approach may lend itself to race conditions if you invoke that Format object before the override is applied. To be confident, you could add another JS file with your overrides (after Ext is loaded, before your app code) and make sure that is included when you load your app.

Resources