Сложный объект с rxjs.Observable свойствами - PullRequest
0 голосов
/ 26 апреля 2018

У меня есть сложный объект JavaScript, такой как:

{
  "@context": "http://schema.org",
  "@type": "Celebrity",
  "name": "Julius Caesar",
  "description": "translate:julius.amazing.bio",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Roma",
    "postalCode": "9999",
    "streetAddress": "Coliseum Street 1",
    "addressCountry": "IT",
    "description": "translate:coliseum.amazing.bio"
  }
}

Глубина объекта практически не ограничена. Теперь некоторые поля должны быть переведены. В приведенном выше примере это будет name и address.description.

Моя функция перевода имеет следующую подпись:

get(key: string): Observable<string>;

Вывод заключается в том, что он возвращает Наблюдаемое. Цель состоит в том, чтобы перевести поля, начинающиеся с translate:, и вернуть объект с точно такой же структурой.

Решение, которое я мог бы найти, состоит в том, чтобы forkJoin все переведенные поля и обновить объект, как только будет сделано.

const obj = {
    '@context': 'http://schema.org',
    '@type': 'Celebrity',
    'name': 'Julius Caesar',
    'description': 'translate:julius.amazing.bio',
    'address': {
        '@type': 'PostalAddress',
        'addressLocality': 'Roma',
        'postalCode': '9999',
        'streetAddress': 'Coliseum Street 1',
        'addressCountry': 'IT',
        'description': 'translate:coliseum.amazing.bio'
    }
};

const cesar$ = this.translate.get('julius.amazing.bio');
const coliseum$ = this.translate.get('coliseum.amazing.bio');

forkJoin([cesar$, coliseum$]).subscribe(res => {
    obj.description = res[0];
    obj.address.description = res[1];
});

Это не элегантно, не гибко, и код должен быть переписан для каждого объекта, который у меня есть. Любые предложения о том, как сделать это элегантным и многократно используемым способом (с любой глубиной объекта), что позволяет мне переводить любое поле, начинающееся с translate:?

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

Как прокомментировал @estus, вы можете рекурсивно пройти исходный объект, чтобы собрать все строки для перевода, а затем перевести все из них, что-то вроде этого:

const translatePrefix = 'translate:';

// to return array of pairs:
// [string, function to apply result on object]

function getFields(obj) {
  const fields = [];
  Object.keys(obj).forEach(key => {
    const val = obj[key];
    if (typeof val === 'string' && val.startsWith(translatePrefix)) {
      fields.push([
        val.substr(translatePrefix.length),
        (translation, resultObj) => {resultObj[key] = translation;},
      ]);
    } else if (typeof val === 'object') {
      fields.push(... getFields(val).map(([toTranslate, apply]) => [
        toTranslate,
        (translation, resultObj) => {apply(translation, resultObj[key]);},
      ]));
    }
  });
  return fields;
}

function translateObj(obj) {
  return forkJoin(
    getFields(obj).map(
      ([str, apply]) => translate.get(str)
        .pipe(map(translation => apply.bind(translation)))
    )
  ).pipe(
    map(translationUpdates => {
      // clone obj to not modify source object (quick and dirty way)
      const res = JSON.parse(JSON.stringify(obj));
      // apply translations to it
      translationUpdates.forEach(apply => apply(res));
      return res;
    })
  );
} 
0 голосов
/ 26 апреля 2018

Вы можете рекурсивно пройти весь объект и перевести свойства, которые необходимо перевести. Затем вам нужно будет собрать все наблюдаемые в массиве, чтобы иметь возможность использовать forkJoin, чтобы иметь наблюдаемое, когда все переводы завершатся.

Мой быстрый удар в это, вероятно, может быть улучшен:

import { delay, map} from 'rxjs/operators';
import { forkJoin } from 'rxjs/observable/forkJoin';
import { of } from 'rxjs/observable/of';
import { Observable } from 'rxjs/Observable';


const obj = {
    '@context': 'http://schema.org',
    '@type': 'Celebrity',
    'name': 'Julius Caesar',
    'description': 'translate:julius.amazing.bio',
    'address': {
        '@type': 'PostalAddress',
        'addressLocality': 'Roma',
        'postalCode': '9999',
        'streetAddress': 'Coliseum Street 1',
        'addressCountry': 'IT',
        'description': 'translate:coliseum.amazing.bio'
    }
};


let translate = {
    get(value: string) {
        return of("transalted " + value).pipe(delay(1000));
    }
}
const translatePrefix = "translate:"
function translateAll<T>(obj:T)  {
    let obs : Observable<void>[] = []
    for(let [key, value] of Object.entries(obj)) {
        if(typeof value === "object") {
            let o = translateAll(value).pipe(map(v=> { obj[key] = v }))
            obs.push(o);
        }
        else if(typeof value === "string") {
            if(!value.startsWith(translatePrefix)) continue;

            var translationKey = value.substr(translatePrefix.length);

            let o = translate.get(translationKey).pipe(map(v=> { obj[key] = v }));

            obs.push(o);
        }
    }

    return forkJoin(obs).pipe(map(v=> obj));
}

translateAll(obj).subscribe(v=> 
{
    console.log(v)
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...