0

I know the title may make it sound like this should just take a quick Google search to find the answer... but please see my code example below and you will find it's a little trickier than you may have first thought.

I am moving from KnockoutJS to Aurelia with ES6. It's my first time with both Aurelia and ES6... Anyway, the problem I am having is with the this keyword and context. In KnockoutJS, I would just alias it like so at the top of the view model: var self = this;. This does not seem to be possible in ES6 and I am told we should do something like: () => { this.foo ... }. I've also found the following useful info: https://www.sitepoint.com/bind-javascripts-this-keyword-react/

However, I can't seem to figure out how to apply any of those solutions in my case, so I am hoping someone can give me a hand here...

I have a view model as follows (most of the "fluff" has been cut out, to focus on the important parts):

export class MenuItemViewModel {
    constructor(parent) {
        this.parent = parent;
    }

    init() {
        let self = this;

        $("#items-grid").kendoGrid({
            data: null,
            dataSource: {
                //etc
                pageSize: this.parent.gridPageSize,
                //etc
            },
            dataBound: function (e) {
                let body = $('#items-grid').find('tbody')[0];
                if (body) {
                    self.parent.templatingEngine.enhance({ element: body, bindingContext: self.parent });
                }
            },
            //etc
            detailTemplate: kendo.template($("#items-template").html()),
            detailInit: this.detailInit
        });
    }

    detailInit(e) {
        // I can't even alias "this" here, because the context is not what I expected.
        //  I tested by doing a `console.log(this.someProperty)` which should have worked, but didn't
        let self = this;

        var detailRow = e.detailRow;

        detailRow.find(".tabstrip").kendoTabStrip({
            animation: {
                open: { effects: "fadeIn" }
            }
        });

        detailRow.find(".detail-grid").kendoGrid({
            data: null,
            dataSource: {
                //etc
                pageSize: this.parent.gridPageSize, // Error message when breaking here... "this.parent is undefined"
                //etc
            },
            dataBound: function (e) {
                let body = this.element.find("tbody")[0];
                //let body = $('#grid').find('tbody')[0];
                if (body) {
                    self.parent.templatingEngine.enhance({ element: body, bindingContext: self.parent });
                }
            },
            //etc
            detailTemplate: kendo.template($("#items-template").html()),
            detailInit: this.detailInit
        });
    }
}

How can I reference the main context this from inside the detailInit function?

Jesse
  • 3,522
  • 6
  • 25
  • 40
Matt
  • 6,787
  • 11
  • 65
  • 112
  • What do you mean by "I tested by doing a `console.log(this.someProperty)` which should have worked". There is no `someProperty` property in your object. – Guillaume Georges May 11 '18 at 11:38
  • It's same case as canonical dupe question. detailInit is a callback. It should be bound to the context. `detailInit = (e) => {...` or else. `let self = this` is antipattern in ES6, and it won't work there any way because `this` is already lost. – Estus Flask May 11 '18 at 11:39
  • which context exatly this give you? – Panos K May 11 '18 at 11:39
  • @t.niese this.parent.gridPageSize won't have problems in this code, at least not the one that OP refers to. this.detailInit will. – Estus Flask May 11 '18 at 11:41
  • @RogerC`someProperty` does not exist.. it was just an example of a property that I do have `apiUrl` but which I did not include in the code sample above because it is not important. So if `this` was the this that I wanted, then the following would have worked: `console.log("this.apiUrl: " + this.apiUrl)` but it was undefined – Matt May 11 '18 at 11:56
  • @estus, if this is really a dupe, could you please post the link to it for me? – Matt May 11 '18 at 11:58
  • @PanosK If I understand what you're asking, the `this` inside the `detailInit(e)` function likely gives some kind of a `kendoGrid` context – Matt May 11 '18 at 11:59
  • See the link above the question. – Estus Flask May 11 '18 at 12:01
  • @estus I think @t.niese was referring to `this.parent.gridPageSize` inside the `detailInit` (there are 2 locations for that on the page). Anyway, the `parent` is the main view model.. this class is a child model, which is why I pass the parent in the constructor, so I can refer to some of the parent's properties – Matt May 11 '18 at 12:01
  • Use something like this in your detailInit method or anywhere you want to access "this" eg parent: ()=> {this.parent} no need for adding "let self = this;" in the beginning – Afshin Ghazi May 11 '18 at 12:04
  • @estus, that info in the duplicate is pretty much exactly the same as the content of the article I linked to in my question. I have found similar info in other places too. In my question I clearly stated that I don't know how to apply that in my situation. Sorry if that';s obvious to most people, but I spend most of my time in server side code - not so much in JavaScript. Would appreciate some example of how to apply that in my code – Matt May 11 '18 at 12:05
  • As I mentioned earlier, it's either `detailInit = (e) => {...`. Or `this.detailInit = this.detailInit.bind(this)` in constructor. – Estus Flask May 11 '18 at 12:07
  • Thanks @estus. I don't understand the first example: `detailInit = (e) => {...` but the other one (`this.detailInit.bind(this)`) works! If you could please elaborate/expand on on the first example as well, so I can improve my understanding of the different solutions, that would be most appreciated. – Matt May 11 '18 at 12:18
  • @estus.. Oh hold on, I think I just realized what you mean... that the function itself can be modified from `detailInit(e) {` to `detailInit = (e) => {`, right? That's all I have to do? Seems simple. – Matt May 11 '18 at 12:19
  • That's correct. It's https://github.com/tc39/proposal-class-fields . It's supported in Aurelia since you're using Babel or TypeScript to build the project. Glad it worked. Feel free to re-ask the question with a link to this one if you will have further problems with it. – Estus Flask May 11 '18 at 12:21
  • @estus Nah, I should be good now that I know what to do. I just didn't realize that I could modify the class functions that way (with the arrow function syntax). I've got it now. Thanks for the help - much appreciated. – Matt May 11 '18 at 12:24
  • Yes, you can. Both will work in this case. I'd go with `.bind(this)` as a rule of thumb because it's more universal. See https://stackoverflow.com/a/45882417/3731501 for more details. – Estus Flask May 11 '18 at 12:29

0 Answers0