3

For an I18n-like component, I want to get a component content as string to have a fallback value in case my I18n service gets nothing, this value is supposed to be a fallback one.

I18n service get method:

public get(key:string,
               defaultValue?:string,
               variables:Variables = {},
               domain?:string):string {

        for (let catalogue of this.catalogues) {
            if (catalogue.getDomains().indexOf(domain) >= 0
                && catalogue.getLocale() == this.locale
                && catalogue.hasKey(key)) {
                return I18n.setVariables(catalogue.get(key, domain, defaultValue).value, variables);
            }
        }
        return defaultValue;
    }

I18nComponent:

@Component({
    selector: "i18n",
    template: "{{text}}",
})
export class I18nComponent implements OnInit {

    constructor(private i18n:I18n) {
    }

    @ContentChild() content:string; //Here, I want to store actual value as fallback one.

    @Input('key') key:string;

    @Input('domain') domain:string;

    @Input('variables')
    variables:Variables = [];

    @Input("plural")
    plural:number;

    text:string;

    ngOnInit():any {
        console.log(this.content); //Here I get 'undefined'
        if (this.plural !== undefined && this.plural != null) {
            this.text = this.i18n.get(this.key, this.content, this.variables, this.domain);
        } else {
            this.text = this.i18n.getPlural(this.key, this.plural, this.content, this.variables, this.domain);
        }
    }
}

usage example:

<i18n key="FOO_KEY" domain="stackoverflow">I'm the default value !</i18n>

I know <ng-content></ng-content> works but only on the template logic, I need a way to get child as string in typescript.

Already tried @ContentChild(String) but I got undefined.

Another way would be to do it like "Loading..." string you can have in the base app component in the index, acting like a placeholder until you get everything loaded. I could do the same for I18n, let the laceholder here until I get something from service to replace it, but I don't know how to achieve this.

4
  • I don't get what you try to accomplish. How is this related to your I18n service? Why don't just just put the fallback value into the class as well and then bind the one from the service and if not available, the fallback? Commented Jun 1, 2016 at 15:57
  • I can't set the fallback value in the class because it's not the same everytime. I have one fallback for each i18n component usage, and it may include html tags. (example: <i18n key="WIN"><b>You</b> won</i18n> -> fallback = <b>You</b> won). The fallback is here "just in case", then, if I don't get the value throught the service, I'll display the english one (the fallback). Commented Jun 1, 2016 at 16:36
  • Can you please add a full example. I don't get from your question what exactly you try to accomplish. Commented Jun 1, 2016 at 16:38
  • @GünterZöchbauer I updated question to provide a full example. Commented Jun 2, 2016 at 7:48

1 Answer 1

4

You can't use @ContentChildren() to get the whole content. You can either add a template variable and query for its name:

<i18n key="FOO_KEY" domain="stackoverflow"><span #myVar>I'm the default value !</span></i18n>
@ContentChild('myVar') myVar;

ngAfterContentInit() {
  console.log(this.myVar.nativeElement.innerHTML);
}

myVar will not be initialized before ngAfterContentInit() is called.

Alternatively @ContentChild() (or @ContentChildren()) you can query for a components type like

    <i18n key="FOO_KEY" domain="stackoverflow"><my-comp>I'm the default value !</my-comp></i18n>

@ContentChild(MyComponent, {read: ElementRef}) mycomp;

ngAfterContentInit() {
  console.log(this.myVar.nativeElement.innerHTML);
}

I think this approach will work better for you approach

@Component({
    selector: "i18n",
    template: "<div #wrapper hidden="true"><ng-content></ng-content><div>{{text}}",
})
export class I18nComponent implements OnInit {

    constructor(private i18n:I18n) {
    }

    @ViewChild('wrapper') content:ElementRef; //Here, I want to store actual value as fallback one.

    @Input('key') key:string;

    @Input('domain') domain:string;

    @Input('variables')
    variables:Variables = [];

    @Input("plural")
    plural:number;

    text:string;

    ngAfterViewInitInit():any {
        console.log(this.content.nativeElement.innerHTML);
        if (this.plural !== undefined && this.plural != null) {
            this.text = this.i18n.get(this.key, this.content, this.variables, this.domain);
        } else {
            this.text = this.i18n.getPlural(this.key, this.plural, this.content, this.variables, this.domain);
        }
    }
}

If you want the users of your i18n component to be able to use Angular bindings, components, and directives, in the content they pass to <i18n>, then he needs to wrap it in a template.

    <i18n key="FOO_KEY" domain="stackoverflow">
      <template>
        I'm the {{someName}}!
        <my-comp (click)="doSomething()"></my-comp>
      </template>
    </i18n>

like explained in https://stackoverflow.com/a/37229495/217408

Sign up to request clarification or add additional context in comments.

1 Comment

Weirdly, I had to put {{text}} at the front of the template instead of the end.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.