Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aurelia: Accessing parent VM method from within child VM

A common use case working with lists is accessing a method of the list from within a list-item. For example: a project-item has an option to delete itself from the containing list. I would like to know if the pattern I describe below for Aurelia is valid or maybe there are better solutions.

In Aurelia I have the following setup:

The containing list: (project-list.html and projectList.js)

<template>      
  <div class="projects">
    <input value.bind="newProjectName" placeholder="New project name"/>
    <project-item repeat.for="project of projects" project.bind="project"></project-item>
  </div>
</template>

and the child item: (project-item and projectItem.js)

<template>
  <span class="title">
    ${project.name} <i click.delegate="deleteProject(project)" class="icon-trash"></i>
  </span>
</template>

In this case deleteProject(project) is a member of projectList VM:

function deleteProject(project){
    var index = this.projects.indexOf(project);
    if (index>-1){
        this.projects.splice(index,1)
    }
}

Unfortunately, as I understand from this issue https://github.com/aurelia/framework/issues/311, that wil not work (anymore).

As a work-around I can bind a function on the project-item VM:

@bindable delete: Function;

and in the project-list template:

<project-item repeat.for="project of projects" project.bind="project" delete.bind="deleteProject"></project-item>

That does work, provide the bound function is a assigned property with a closure:

deleteProject = function(project : Projects.Project){
        var index = this.projects.indexOf(project);
        if (index>-1){
            _.remove(this.projects,(v,i)=>i==index);
        }
    }

The closure is needed to acces the correct context (this being the project-list). Using

function deleteProject(project)

this would refer to the context of the project-item.

Even though this construction works and has not much overhead in plumbing, it feels to me a bit fragile:

  • The assigned function is hard to distinguish form a regular class function
  • binding the function as well as the list-item seems a bit "overdone"

Or maybe I'm missing an Aurelia bubbling mechanism that makes accessing a parent VM handled by the framework?

Edit after answer: Based on the answer by @Sylvain I made a GistRun that implements a skeleton list and list-item implementation with add and remove:

Aurelia Skeleton list list-item implementation

like image 405
Arjan Avatar asked Mar 31 '16 14:03

Arjan


1 Answers

Here are a few alternatives to passing a reference to the function:

  • Have the child component broadcast a public event using the EventAggregator singleton instance and have the parent component react to the event

  • Have the child component broadcast a private event using a private EventAggregator instance and have the parent component react to the event

  • Have the child component broadcast a DOM event and bind it to the parent with delete.call like this <project-item repeat.for="project of projects" project.bind="project" delete.call="deleteProject($even t)"></project-item>

My personal preference is the third option. It feels more like "Web Components" to me.

like image 146
Sylvain Avatar answered Nov 02 '22 22:11

Sylvain