Vinculação do método do elemento da diretiva AngularJS - TypeError: não é possível usar o operador 'in' para pesquisar 'functionName' em 1

90

Este é o controlador do template principal:

app.controller('OverviewCtrl', ['$scope', '$location', '$routeParams', 'websiteService', 'helperService', function($scope, $location, $routeParams, websiteService, helperService) {
    ...     
    $scope.editWebsite = function(id) {
        $location.path('/websites/edit/' + id);
    };
}]);

Esta é a diretiva:

app.directive('wdaWebsitesOverview', function() {
    return {
        restrict: 'E',
        scope: {
            heading: '=',
            websites: '=',
            editWebsite: '&'
        },
        templateUrl: 'views/websites-overview.html'
    }
});

É assim que a diretiva é aplicada no modelo principal:

<wda-websites-overview heading="'All websites'" websites="websites" edit-website="editWebsite(id)"></wda-websites-overview>

e este método é chamado a partir do modelo de diretiva (website-overview.html):

<td data-ng-click="editWebsite(website.id)">EDIT</td>

PERGUNTA: Quando EDIT é clicado, este erro aparece no console:

TypeError: Não é possível usar o operador 'in' para pesquisar 'editWebsite' em 1

Alguém sabe o que se passa aqui?

CodeVirtuoso
fonte

Respostas:

178

Como você definiu uma expressão binding ( &), é necessário chamá-la explicitamente com um JSON contendo o idse quiser vinculá-la no HTML como edit-website="editWebsite(id)".

Na verdade, o Angular precisa entender o que isso idestá em seu HTML e, uma vez que não faz parte do seu escopo, você precisa adicionar o que é chamado de "locais" à sua chamada, fazendo:

data-ng-click="editWebsite({id: website.id})"

Ou como alternativa:

data-ng-click="onClick(website.id)"

Com o controlador / código do link:

$scope.onClick = function(id) {
  // Ad "id" to the locals of "editWebsite" 
  $scope.editWebsite({id: id});
}

Isso está documentado aqui, procure o exemplo envolvendo "close({message: 'closing for now'})"

https://docs.angularjs.org/guide/directive

Floribon
fonte
7
Obrigado pela sua resposta e por apontar a localização exata na documentação, foi de uma ajuda inacreditável!
Bruno Belotti
1
@floribon Eu sei que isso é meio antigo, mas você tem um exemplo de digitação do callback?
tcrite
É muito útil, obrigado.
Anurag pareek
ainda útil para pessoas que trabalham em projetos legados .. obrigado
BMWCMW
4

TL; DR; - Você está assumindo que a função associada está sendo passada para o componente filho. Isso está incorreto. Na verdade, o AngularJS está analisando o modelo de string e criando uma nova função, que então chama a função pai.

Esta função precisa receber um objeto com chaves e valores, ao invés de uma variável simples.

Explicação mais longa

Isso acontece quando você vincula uma função usando '&' e tenta chamar essa função de seu controlador, passando uma variável simples em vez de um objeto contendo o nome da variável simples. As chaves de objeto são necessárias para o mecanismo de modelagem descobrir como passar valores para a função vinculada.

por exemplo. você chamou boundFunction('cats')ao invés deboundFunction({value: 'cats'})

Exemplo Trabalhado

Digamos que eu crie um componente como este:

const MyComponent = {
  bindings: {
    onSearch: '&'
  },
  controller: controller
};

Esta função (no pai) tem a seguinte aparência:

onSearch(value) {
  // do search
}

No meu modelo pai, agora posso fazer isso:

<my-component on-search="onSearch(value)"></my-component>

A ligação aqui será analisada a partir da string. Você não está realmente passando a função. O AngularJS está criando uma função para você que a chama. A ligação criada no modelo pode conter muitas outras coisas além da chamada de função.

O AngularJS, de alguma forma, precisa descobrir de onde sair valuee faz isso recebendo um objeto do pai.

No controlador de meu componente, preciso fazer algo como:

handleOnSearch(value) {
  if (this.onSearch) {
    this.onSearch({value: value})
  }
}
superluminário
fonte