Are dots allowed in keys in ngx-translate - ngx-translate

I have a problem in the codebase that I'm working on right now related to keys with dots.
Basically, the i18n JSON contains something like:
{
a: {
title: 'Title for a'
},
b: {
title: 'Title for b'
},
'a.b': {
title: 'Title for a.b'
}
}
In the html we are accessing it
{{ 'a.title' | translate }}
In short, these are the results for the existing keys
key
Result
Works
a
{title: 'Title for a'}
✅
a.title
Title for a
✅
b
{title: 'Title for b'}
✅
b.title
Title for b
✅
a.b
undefined
❌
a.b.title
undefined
❌
Tried to search in the documentation and the Github issues but there's nothing that points that this is allowed/disallowed.
Furthermore, I debugged the code and saw that the method responsible to get the value for a given key is getValue in translate.parser.ts.
This specific method isn't able to deal with cases where there's a key with dots for which there's another key with the initial part of said key.
For example, if we have two keys 'a' and 'a.b' but 'a' is a string then the method works. If in this example 'a' was an object then it doesn't work.
I also reckon that it's hard to implement since we can have a case like this:
{
a: {
b: {
title: 'Title for a'
}
},
'a.b': {
title: 'a.b title'
}
}
If you pass the following {{ 'a.b.title' | translate }} which one would be translated?
My possibilities are:
Replace all keys with dots by underscores or something else
Create parser that extends the TranslateDefaultParser and re-implements the getValue method

by default while using {{ 'a.b.title' | translate }} ngx-translate search this
{
a: {
b: {
title: 'Title for a'
}
}
}

To answer my own post, YES dots are allowed in keys BUT it can cause you trouble easily if you have composite keys.
My solution was to flatten all translations which makes the underlying code O(1) to get each key and allows us to use dots even in composite keys.
Flattening all translations means that the following:
{
a: {
b: {
title: 'Title for b',
label: 'Label for b'
}
}
}
Becomes:
'a.b.title': 'Title for b'
'a.b.label': 'Label for b'

Related

Add/modify text between parentheses

I'm trying to make a classified text, and I'm having problem turning
(class1 (subclass1) (subclass2 item1 item2))
To
(class1 (subclass1 item1) (subclass2 item1 item2))
I have no idea to turn text above to below one, without caching subclass1 in memory. I'm using Perl on Linux, so any solution using shell script or Perl is welcome.
Edit: I've tried using grep, saving whole subclass1 in a variable, then modify and exporting it to the list; but the list may get larger and that way will use a lot of memory.
I have no idea to turn text above to below one
The general approach:
Parse the text.
You appear to have lists of space-separated lists and atoms. If so, the result could look like the following:
{
type => 'list',
value => [
{
type => 'atom',
value => 'class1',
},
{
type => 'list',
value => [
{
type => 'atom',
value => 'subclass1',
},
]
},
{
type => 'list',
value => [
{
type => 'atom',
value => 'subclass2',
},
{
type => 'atom',
value => 'item1',
},
{
type => 'atom',
value => 'item2',
},
],
}
],
}
It's possible that something far simpler could be generated, but you were light on details about the format.
Extract the necessary information from the tree.
You were light on details about the data format, but it could be as simple as the following if the above data structure was created by the parser:
my $item = $tree->{value}[2]{value}[1]{value};
Perform the required modifications.
You were light on details about the data format, but it could be as simple as the following if the above data structure was created by the parser:
my $new_atom = { type => 'atom', value => $item };
push #{ $tree->{value}[1]{value} }, $new_atom;
Serialize the data structure.
For the above data structure, you could use the following:
sub serialize {
my ($node) = #_;
return $node->{type} eq 'list'
? "(".join(" ", map { serialize($_) } #{ $node->{value} }).")"
: $node->{value};
}
Other approaches could be available depending on the specifics.

geoChoroplethChart is black with two option of value for one key

I have the next data works with dc.js and geoChoroplethChart,
var data = crossfilter([
{ state: 'one', bar: 'A', num: '1' },
{ state: 'two', bar: 'B', num: '2' },
{ state: 'three', bar: 'A', num: '3' },
{ state: 'one', bar: 'B', num: '3' },
{ state: 'one', bar: 'A', num: '2' },
{ state: 'two', bar: 'B', num: '2' },
]);
var statedim=data.dimension(function(d){return d['state'];})
var my_group=statedim.group();
reducer=reductio();
reducer.value('nbar').exception(function(d){return d['bar']}.exceptionCount(true);
reducer.value('nnum').exception(function(d){return d['num']}.exceptionCount(true);
reducer(my_group);
where state is a region in my country and the color is based on nnum, so I use
.valueAccessor(function (d) {return d.value.nnum.exceptionCount})
but I want it in the title appear state, nnum and nbar.
.title(function(d){
return["state: " +d.key,
"#nnum: "+d.value].join('\n')})
but I do not know how to integrate nbar in title without geoChoroplethChart is black.
I think in use
.valueAccessor(function (d) {return d.value})
.title(function(d){
return["state: " +d.key,
"#nnum: "+d.value.nnum.exceptionCount,
"nbar:" +d.value.nbar.exceptionCount].join('\n')})
It is the solution for the title, but the states fill up black and when I click on another graphic, the map does not react.
The way the geoChoropleth deals with data is definitely pretty weird. All the other charts bind the crossfilter data to the SVG; geoChoropleth binds the map data and hides the crossfilter data in an internal object. Then it fakes bound data for the accessors.
What's more, as you point out, it stores the result of the valueAccessor() in its hidden data, rather than binding the raw data and then using the valueAccessor when needed. I'm pretty sure that's a bad idea.
But enough complaining. Let's find the workaround, as usual.
Turns out you don't need to drill down in the value accessor in order to get the colors right. You can leave the valueAccessor defaulted, and it will fetch d.value. Then use the colorAccessor to get the colors right:
.colorAccessor(function(d){
return d ? d.nbar.exceptionCount : null;
})
Note the guard against undefined data, a result of the same weirdness.
We also need to guard against an undefined value in the title accessor, because the chart is building key/value pairs from the map data, not the crossfilter data:
.title(function(d){
var arr = ["state: " + d.key];
if(d.value) // have to guard against null data for "other" states
arr.push(
"#nnum: "+d.value.nnum.exceptionCount,
"nbar:" +d.value.nbar.exceptionCount
);
return arr.join('\n');
})
Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/7qb3yujj/14/

Creating a GraphQLObjectType with an indexable field signature?

I'm currently in the process of transforming a REST API into GraphQL, but I've hit a bit of a snag in one of the endpoints.
Currently, this endpoint returns an object who's keys can be an unlimited set of strings, and whos values all match a certain shape.
So, as a rudimentary example, I have this situation...
// response
{
foo: { id: 'foo', count: 3 },
bar: { id: 'bar', count: 6 },
baz: { id: 'baz', count: 1 },
}
Again, the keys are not known at runtime and can be an unlimited set of strings.
In TypeScript, for example, this sort of situation is handled by creating an interface using an indexable field signature, like so...
interface Data {
id: string;
count: number;
}
interface Response {
[key: string]: Data;
}
So, my question is: Is this sort of thing possible with graphql? How would I go about creating a type/schema for this?
Thanks in advance!
I think that one solution can be usage of JSON.stringify() method
exampleQuery: {
type: GraphQLString,
resolve: (root, args, context) => {
let obj = {
foo: { id: 'foo', count: 3 },
bar: { id: 'bar', count: 6 },
baz: { id: 'baz', count: 1 }
};
return JSON.stringify(obj);
}
}
Then, after retrieving the result of GraphQL query you could use JSON.parse(result) (in case the part performing the query is also written in JavaScript - otherwise you would have to use equivalent method of other language to parse the incoming JSON response).
Disadvantage of such a solution is that you do not have the possibility to choose what fields of obj you want to retrieve from the query, but, as you said, the returning object can have unlimited set of strings that probably are not known on the front end of the application, so there is no need to choose it's keys, am I right?

How can I compare two rethinkdb objects to create a new object that only contains their differences?

Say I have two objects stored in rethinkdb I wish to compare, let's call them old_val and new_val. As an example, let's say these values represent a TODO task that has changed owner and status:
{
old_val: {
status: 'active',
content: 'buy apples',
owner: 'jordan'
},
new_val: {
status: 'done',
content: 'buy apples',
owner: 'matt'
}
}
When I compare old_val and new_val, I'd like to yield a new object where new_val only contains the fields that differ from old_val. I want to do this in order to save bytes on the wire; and make rendering changes on my client easier. The result of the query should look something like this:
{
old_val: {
content: 'buy apples',
owner: 'jordan',
status: 'active'
},
new_val: {
owner: 'matt',
status: 'done'
}
}
How would I do this?
There are three separate parts to solving this problem:
Generate a list of fields to compare
Compare between common fields, include only fields which differ
Create a new object
(1) A list of fields can be generated by using the keys() method. We can filter these fields to (2) only include those which exist in both old_val and new_val and whose values differ. We can then pass this sequence to concatMap() to build an array of key/value pairs like [key0, value0, key1, value1]. Finally, a new object can be constructed (3) from this sequence by applying it as arguments (using r.args()) to the r.object() function.
It comes together like this:
r.expr({
old_val: {
status: 'active',
content: 'buy apples',
owner: 'jordan'
},
new_val: {
status: 'done',
content: 'buy apples',
owner: 'matt'
}
}).do((diff_raw) =>
r.expr({
old_val: diff_raw('old_val'),
// build an object only containing changes between old_val and new_val:
new_val: r.object(r.args(
diff_raw('new_val')
.keys()
// only include keys existing in old and new that have changed:
.filter((k) =>
r.and(
diff_raw('old_val').hasFields(k),
diff_raw('new_val')(k).ne(diff_raw('old_val')(k))
)
)
// build a sequence of [ k0, v0, k1, v1, ... ]:
.concatMap((k) => [k, diff_raw('new_val')(k)])
))
})
)
This will return:
{
"new_val": {
"owner": "matt" ,
"status": "done"
} ,
"old_val": {
"content": "buy apples" ,
"owner": "jordan" ,
"status": "active"
}
}

Translating JSON into custom dijit objects

I am looking for an example where JSON constructed from the server side is used to represent objects that are then translated into customized widgets in dojo. The JSON would have to be very specific in its structure, so it would not be a very general solution. Could someone point me to an example of this. It would essentially be the reverse of this
http://docs.dojocampus.org/dojo/formToJson
First of all let me point out that JSON produced by dojo.formToJson() is not enough to recreate the original widgets:
{"field1": "value1", "field2": "value2"}
field1 can be literally anything: a checkbox, a radio button, a select, a text area, a text box, or anything else. You have to be more specific what widgets to use to represent fields. And I am not even touching the whole UI presentation layer: placement, styling, and so on.
But it is possible to a certain degree.
If we want to use Dojo widgets (Dijits), we can leverage the fact that they all are created uniformly:
var myDijit = new dijit.form.DijitName(props, node);
In this line:
dijit.form.DijitName is a dijit's class.
props is a dijit-specific properties.
node is an anchor node where to place this dijit. It is optional, and you don't need to specify it, but at some point you have to insert your dijit manually.
So let's encode this information as a JSON string taking this dijit snippet as an example:
var myDijit = new dijit.form.DropDownSelect({
options: [
{ label: 'foo', value: 'foo', selected: true },
{ label: 'bar', value: 'bar' }
]
}, "myNode");
The corresponding JSON can be something like that:
{
type: "DropDownSelect",
props: {
options: [
{ label: 'foo', value: 'foo', selected: true },
{ label: 'bar', value: 'bar' }
]
},
node: "myNode"
}
And the code to parse it:
function createDijit(json){
if(!json.type){
throw new Error("type is missing!");
}
var cls = dojo.getObject(json.type, false, dijit.form);
if(!cls){
// we couldn't find the type in dijit.form
// dojox widget? custom widget? let's try the global scope
cls = dojo.getObject(json.type, false);
}
if(!cls){
throw new Error("cannot find your widget type!");
}
var myDijit = new cls(json.props, json.node);
return myDijit;
}
That's it. This snippet correctly handles the dot notation in types, and it is smart enough to check the global scope too, so you can use JSON like that for your custom dijits:
{
type: "my.form.Box",
props: {
label: "The answer is:",
value: 42
},
node: "answer"
}
You can treat DOM elements the same way by wrapping dojo.create() function, which unifies the creation of DOM elements:
var myWidget = dojo.create("input", {
type: "text",
value: "42"
}, "myNode", "replace");
Obviously you can specify any placement option, or no placement at all.
Now let's repeat the familiar procedure and create our JSON sample:
{
tag: "input",
props: {
type: "text",
value: 42
},
node: "myNode",
pos: "replace"
}
And the code to parse it is straightforward:
function createNode(json){
if(!json.tag){
throw new Error("tag is missing!");
}
var myNode = dojo.create(json.tag, json.props, json.node, json.pos);
return myNode;
}
You can even categorize JSON items dynamically:
function create(json){
if("tag" in json){
// this is a node definition
return createNode(json);
}
// otherwise it is a dijit definition
return createDijit(json);
}
You can represent your form as an array of JSON snippets we defined earlier and go over it creating your widgets:
function createForm(array){
dojo.forEach(array, create);
}
All functions are trivial and essentially one-liners — just how I like it ;-)
I hope it'll give you something to build on your own custom solution.

Resources