Polymer compute and set property value with this.set() not working - polymer

I'm trying to display a list of names in an array through a custom element name x-names. The code to display the list of names is as follows:
<x-names names="{{names}}"></x-names>
<template is="dom-repeat" items="[[names]]" as="name">
<h5>Name: [[name]]</h5>
</template>
The x-names element is defined as follows:
<dom-module id="x-names">
<template>
<akc-meta-query key="names" value="{{_namesObject}}"></akc-meta-query>
</template>
<script>
Polymer({
is: 'x-names',
properties: {
names: {
type: Array,
computed: '_namesObjectToArray(_namesObject)',
value: [],
notify: true
},
_namesObject: {
type: Object
}
},
_namesOjbectToArray: function(obj) {
if (obj) {
var keys = Object.keys(obj);
this.set('names', keys);
}
}
});
</script>
</dom-module>
The keys of the object are the names, so I simply want to get the keys and set that array to the names property, but, I get the following error:
Uncaught TypeError: Cannot set property names of #<x-names> which has only a getter
I'm fairly new to Polymer, so I'm sure it's a quick fix, but it's 100% escaping me. I've looked through the documentation (which I'm still getting used to), and have tried a couple other ways with no luck:
this.names = keys and return keys don't generate an error, but they also doesn't reflect the names to the dom-repeat.

You shouldn't set in the function you defined for computed. This function should return the values
_namesOjbectToArray: function(obj) {
if (obj) {
return keys = Object.keys(obj);
}
// else {
// return [];
// }
}

Related

Databinding function not detecting change in array of objects

I have:
<template is="dom-repeat"
initial-count="1"
index-as="index"
items="{{uploadState}}">
<div hidden="[[getState(index)]]" class="layout vertical center-center">
and I have:
properties: {
uploadState: {
type: Array,
value: function() {
var arr = Array.apply(null, Array(1));
var newArray = arr.map(()=> {
return { value: false };
});
return newArray;
},
notify: true
},
getState: function(index) {
var uploaded = this.get(['uploadState', index]);
// use this.linksPath??
return uploaded.value;
}
And:
changeState: function(index) {
this.uploadState[index].value = true;
}
When I change this.uploadState[0].value to false the hidden="[[getState(index)]]" does not detect the change.
How would I make hidden detect the change? I read this similar issue but not sure how I would use linkPath on this.
You can't work with native arrays in Polymer with data bindings. Polymer has it's own way of mapping the array elements in memory and it won't be able to detect a change to a array of elements.
Instead, I had to use the Polymer api's to mutate the arrays..this.get and this.set. This takes some getting use to since you will need to treat array elements as paths as I did with uploadState.*.
Documentation is a little rough since since there is no api documentation for array.base()(at least that I found ....not Polymer.base()).
Solution:
See Bind to an array item
<div hidden$="{{getState(uploadState.*, index, 'value')}}">
...
uploadState: {
type: Array,
value: function() {
var arr = Array.apply(null, Array(1));
var newArray = arr.map(()=> {
return { value: false };
});
return newArray;
},
notify: true
}
},
getState: function(change, index, path) {
var uploaded = this.get(path, change.base[index]);
return uploaded;
},
changeState: function() {
this.set('uploadState.0.value', true);
}

Best way to communicate between instances of the same web component with Polymer?

I'm trying to sync some of my web component properties between instances of the same element so if one of this properties changes then the same property gets updated in all the instances with the corresponding binding and events.
Note: I want to use the Polymer Data System Concepts for the communications between instances.
Example
my-element.html
<dom-module id="my-element">
<script>
Polymer({
is: 'my-element',
properties: {
myProp: {
type: String,
notify: true
}
});
</script>
</dom-module>
my-other-element.html
<dom-module id="my-other-element">
<template>
<my-element my-prop="{{otherProp}}"></my-element>
</template>
<script>
Polymer({
is: 'my-other-element',
properties: {
otherProp: {
type: String,
notify: true,
readOnly: true
}
}
})
</script>
</dom-module>
my-app.html
<dom-module id="my-app">
<template>
<my-element id="element"></my-element>
<my-other-element id="otherElement"
on-other-prop-changed="onPropChanged"
></my-other-element>
</template>
<script>
Polymer({
is: 'my-app',
attached: function () {
// should set 'myProp' to 'test' and trigger
// the event 'my-prop-changed' in all my-element instances
this.$.element.myProp = 'test'
},
onPropChanged: function (ev, detail) {
console.log(detail.value); // should print 'test'
console.log(this.$.element.myProp); // should print 'test'
console.log(this.$.otherElement.otherProp); // should print 'test'
}
});
</script>
</dom-module>
PD: Would be good to use standard like patterns and good practices.
tl;dr
I have created a custom behaviour that syncs all elements' properties that have notify: true. Working prototype: JSBin.
Currently, this prototype does not distinguish between different kinds of elements, meaning that it can only sync instances of the same custom element - but this can be changed without much effort.
You could also tailor the behaviour so that is syncs only the desired properties and not just all with notify: true. However, if you take this path, be advised that all the properties you want to sync must have notify: true, since the behaviour listens to the <property-name>-changed event, which is fired only if the property has notify: true.
The details
Let's start with the custom SyncBehavior behaviour:
(function() {
var SyncBehaviorInstances = [];
var SyncBehaviorLock = false;
SyncBehavior = {
attached: function() {
// Add instance
SyncBehaviorInstances.push(this);
// Add listeners
for(var property in this.properties) {
if('notify' in this.properties[property] && this.properties[property].notify) {
// Watch all properties with notify = true
var eventHanler = this._eventHandlerForPropertyType(this.properties[property].type.name);
this.listen(this, Polymer.CaseMap.camelToDashCase(property) + '-changed', eventHanler);
}
}
},
detached: function() {
// Remove instance
var index = SyncBehaviorInstances.indexOf(this);
if(index >= 0) {
SyncBehaviorInstances.splice(index, 1);
}
// Remove listeners
for(var property in this.properties) {
if('notify' in this.properties[property] && this.properties[property].notify) {
// Watch all properties with notify = true
var eventHanler = this._eventHandlerForPropertyType(this.properties[property].type.name);
this.unlisten(this, Polymer.CaseMap.camelToDashCase(property) + '-changed', eventHanler);
}
}
},
_eventHandlerForPropertyType: function(propertyType) {
switch(propertyType) {
case 'Array':
return '__syncArray';
case 'Object':
return '__syncObject';
default:
return '__syncPrimitive';
}
},
__syncArray: function(event, details) {
if(SyncBehaviorLock) {
return; // Prevent cycles
}
SyncBehaviorLock = true; // Lock
var target = event.target;
var prop = Polymer.CaseMap.dashToCamelCase(event.type.substr(0, event.type.length - 8));
if(details.path === undefined) {
// New array -> assign by reference
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
instance.set(prop, details.value);
}
});
} else if(details.path.endsWith('.splices')) {
// Array mutation -> apply notifySplices
var splices = details.value.indexSplices;
// for all other instances: assign reference if not the same, otherwise call 'notifySplices'
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
var instanceReference = instance.get(prop);
var targetReference = target.get(prop);
if(instanceReference !== targetReference) {
instance.set(prop, targetReference);
} else {
instance.notifySplices(prop, splices);
}
}
});
}
SyncBehaviorLock = false; // Unlock
},
__syncObject: function(event, details) {
var target = event.target;
var prop = Polymer.CaseMap.dashToCamelCase(event.type.substr(0, event.type.length - 8));
if(details.path === undefined) {
// New object -> assign by reference
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
instance.set(prop, details.value);
}
});
} else {
// Property change -> assign by reference if not the same, otherwise call 'notifyPath'
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
var instanceReference = instance.get(prop);
var targetReference = target.get(prop);
if(instanceReference !== targetReference) {
instance.set(prop, targetReference);
} else {
instance.notifyPath(details.path, details.value);
}
}
});
}
},
__syncPrimitive: function(event, details) {
var target = event.target;
var value = details.value;
var prop = Polymer.CaseMap.dashToCamelCase(event.type.substr(0, event.type.length - 8));
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
instance.set(prop, value);
}
});
},
};
})();
Notice that I have used the IIFE pattern to hide the variable that holds all instances of the custom element my-element. This is essential, so don't change it.
As you can see, the behaviour consists of six functions, namely:
attached, which adds the current instance to the list of instances and registers listeners for all properties with notify: true.
detached, which removes the current instance from the list of instances and removes listeners for all properties with notify: true.
_eventHandlerForPropertyType, which returns the name of one of the functions 4-6, depending on the property type.
__syncArray, which syncs the Array type properties between the instances. Notice that I ignore the current target and implement a simple locking mechanism in order to avoid cycles. The method handles two scenarios: assigning a new Array, and mutating an existing Array.
__syncObject, which syncs the Object type properties between the instances. Notice that I ignore the current target and implement a simple locking mechanism in order to avoid cycles. The method handles two scenarios: assigning a new Object, and changing a property of an existing Object.
__syncPrimitive, which syncs the primitive values of properties between the instances. Notice that I ignore the current target in order to avoid cycles.
In order to test-drive my new behaviour, I have created a sample custom element:
<dom-module id="my-element">
<template>
<style>
:host {
display: block;
}
</style>
<h2>Hello [[id]]</h2>
<ul>
<li>propString: [[propString]]</li>
<li>
propArray:
<ol>
<template is="dom-repeat" items="[[propArray]]">
<li>[[item]]</li>
</template>
</ol>
</li>
<li>
propObject:
<ul>
<li>name: [[propObject.name]]</li>
<li>surname: [[propObject.surname]]</li>
</ul>
</li>
</ul>
</template>
<script>
Polymer({
is: 'my-element',
behaviors: [
SyncBehavior,
],
properties: {
id: {
type: String,
},
propString: {
type: String,
notify: true,
value: 'default value',
},
propArray: {
type: Array,
notify: true,
value: function() {
return ['a', 'b', 'c'];
},
},
propObject: {
type: Object,
notify: true,
value: function() {
return {'name': 'John', 'surname': 'Doe'};
},
},
},
pushToArray: function(item) {
this.push('propArray', item);
},
pushToNewArray: function(item) {
this.set('propArray', [item]);
},
popFromArray: function() {
this.pop('propArray');
},
setObjectName: function(name) {
this.set('propObject.name', name);
},
setNewObjectName: function(name) {
this.set('propObject', {'name': name, 'surname': 'unknown'});
},
});
</script>
</dom-module>
It has one String property, one Array property, and one Object property; all with notify: true. The custom element also implements the SyncBehavior behaviour.
To combine all of the above in a working prototype, you simply do this:
<template is="dom-bind">
<h4>Primitive type</h4>
propString: <input type="text" value="{{propString::input}}" />
<h4>Array type</h4>
Push to propArray: <input type="text" id="propArrayItem" /> <button onclick="_propArrayItem()">Push</button> <button onclick="_propNewArrayItem()">Push to NEW array</button> <button onclick="_propPopArrayItem()">Delete last element</button>
<h4>Object type</h4>
Set 'name' of propObject: <input type="text" id="propObjectName" /> <button onclick="_propObjectName()">Set</button> <button onclick="_propNewObjectName()">Set to NEW object</button> <br />
<script>
function _propArrayItem() {
one.pushToArray(propArrayItem.value);
}
function _propNewArrayItem() {
one.pushToNewArray(propArrayItem.value);
}
function _propPopArrayItem() {
one.popFromArray();
}
function _propObjectName() {
one.setObjectName(propObjectName.value);
}
function _propNewObjectName() {
one.setNewObjectName(propObjectName.value);
}
</script>
<my-element id="one" prop-string="{{propString}}"></my-element>
<my-element id="two"></my-element>
<my-element id="three"></my-element>
<my-element id="four"></my-element>
</template>
In this prototype, I have created four instances of my-element. One has propString bound to an input, while the others don't have any bindings at all. I have created a simple form, that covers every scenario I could think of:
Changing a primitive value.
Pushing an item to an array.
Creating a new array (with one item).
Deleting an item from the array.
Setting object property.
Creating a new object.
EDIT
I have updated my post and the prototype in order to address the following issues:
Syncing of non-primitive values, namely Array and Object.
Properly converting property names from Dash case to Camel case (and vice-versa).
We have created a component to synchronize data among different instances. Our component is:
<dom-module id="sync-data">
<template>
<p>Debug info: {scope:[[scope]], key:[[key]], value:[[value]]}</p>
</template>
<script>
(function () {
var items = []
var propagateChangeStatus = {}
var togglePropagationStatus = function (status) {
propagateChangeStatus[this.scope + '|' + this.key] = status
}
var shouldPropagateChange = function () {
return propagateChangeStatus[this.scope + '|' + this.key] !== false
}
var propagateChange = function (key, scope, value) {
if (shouldPropagateChange.call(this)) {
togglePropagationStatus.call(this, false)
var itemsLength = items.length
for (var idx = 0; idx < itemsLength; idx += 1) {
if (items[idx] !== this && items[idx].key === key && items[idx].scope === scope) {
items[idx].set('value', value)
}
}
togglePropagationStatus.call(this, true)
}
}
Polymer({
is: 'sync-data',
properties: {
key: {
type: String,
value: ''
},
scope: {
type: String,
value: ''
},
value: {
type: String,
notify: true,
observer: '_handleValueChanged',
value: ''
}
},
created: function () {
items.push(this)
},
_handleValueChanged: function (newValue, oldValue) {
this.typeof = typeof newValue
propagateChange.call(this, this.key, this.scope, newValue)
}
})
})()
</script>
</dom-module>
And we use it in a component like this:
<sync-data
key="email"
scope="user"
value="{{email}}"></sync-data>
And in another component like this:
<sync-data
key="email"
scope="user"
value="{{userEmail}}"></sync-data>
In this way we get the native behavior of polymer for events and bindings
My personal opinion on problems like this is to use flux architecture.
you create a wrapper Element which is distributing all the information to the children. All changes a going via the main component.
<app-wrapper>
<component-x attr="[[someParam]]" />
<component-x attr="[[someParam]]" />
<component-x attr="[[someParam]]" />
</app-wrapper>
the component-x is firing an change value event on app-wrapper and the app-wrapper is updating someValue, note it's a one-way-binding.
There is a component for this, which is implementing the reduxarchitecture, but its also possible to code your own. It's more or less the observer pattern
Try this for my-app.html. I don't see any reason to not use two-way bindings here.
<dom-module id="my-app">
<template>
<my-element my-prop="{{myProp}}"></my-element>
<my-element my-prop="{{myProp}}"></my-element>
</template>
<script>
Polymer({
is: 'my-app',
ready: function() {
this.myProp = 'test';
}
});
</script>
</dom-module>
Although it's probably a better practice to give myProp a default value by using the properties object rather than the ready callback. Example:
Polymer({
is: 'my-app',
properties: {
myProp: {
type: String,
value: 'test'
}
});

Polymer 1.0: how to correctly bind to (inject / override) a function of a dom-module

I have a Polymer element of which the public API should allow to bind to a function which the user can define, i.e. it should allow to pass in a function implementation. I have tried quite a few approaches but only 1 worked. Now I'm wondering if this is the proper / correct way or not.
To rephrase: what is the proper way to bind a function as part of a dom-module's public API? The only way I have this achieved somewhat is as follows:
<dom-module id="channel-search">
<template>
<remote-dropdown
id="dropdown"
label-text="Type channel name"
url='{{_findRecordUrl}}'
url-transformer='{{urlTransformer}}'
result-to-list-transformer='{{resultToListTransformer}}'
class="layout horizontal"
style='width: 100%'>
</remote-dropdown>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'channel-search',
properties: {
_findRecordUrl: {
type: String,
value: 'http://127.0.0.1:9292/epics-boot-info.psi.ch/find-channel.aspx'
}
},
/*
* Here in the attached function, I define the methods which will then be bound to
* the respective properties of the remote-dropdown element
*/
attached: function() {
this.urlTransformer = function(baseUrl, currentInput) {
return baseUrl + '/' + currentInput;
};
this.resultToListTransformer = function(findRecordList) {
var responseList = findRecordList.map(function(res) {
return res.Channel;
});
return responseList;
};
}
});
})();
</script>
So, I needed to define the functions in the attached callback in order for them to be properly bound to the remote-dropdown element's public API.
I hoped it would be a bit clearer / easier, maybe like so:
<script>
(function() {
Polymer({
is: 'channel-search',
properties: {
_findRecordUrl: {
type: String,
value: 'http://127.0.0.1:9292/find-channel.aspx'
}
},
urlTransformer: function(baseUrl, currentInput) {
return baseUrl + '/' + currentInput;
};
resultToListTransformer: function(findRecordList) {
var responseList = findRecordList.map(function(res) {
return res.Channel;
});
return responseList;
};
});
})();
</script>
i.e. simply define the function implementation as part of the element's definition and then bind those to the embedded remote-dropdown element. However, that never seemed to work as I thought it would (also not variations thereof) - surely also because of my limited knowledge of Polymer/Javascript internals.
My question is: is the solution using the attached callback the proper way to achieve what I am trying to do? If not, what would be the correct way to implement this?
You can probably do that using computed properties or computed bindings. Your first answer looks like a variation of the latter.

Observe changes for an object in Polymer JS

I have an element with a model object that I want to observe like so:
<polymer-element name="note-editor" attributes="noteTitle noteText noteSlug">
<template>
<input type="text" value="{{ model.title }}">
<textarea value="{{ model.text }}"></textarea>
<note-ajax-button url="/api/notes/" method="POST" model="{{model}}">Create</note-ajax-button>
</template>
<script>
Polymer('note-editor', {
attached: function() {
this.model = {
title: this.noteTitle,
text: this.noteText,
slug: this.noteSlug
}
},
});
</script>
</polymer-element>
I want to observe changes in the model but apparently it's not possible to use modelChanged callback in the element and neither in the note-ajax-button element. What is wrong? How can I do that?
I've tried observing the fields separately, but it's not clean at all. The state of the button element you see there should change depending on the model state, so I need to watch changes for the object, not the properties.
Thanks!
To observe paths in an object, you need to use an observe block:
Polymer('x-element', {
observe: {
'model.title': 'modelUpdated',
'model.text': 'modelUpdated',
'model.slug': 'modelUpdated'
},
ready: function() {
this.model = {
title: this.noteTitle,
text: this.noteText,
slug: this.noteSlug
};
},
modelUpdated: function(oldValue, newValue) {
var value = Path.get('model.title').getValueFrom(this);
// newValue == value == this.model.title
}
});
http://www.polymer-project.org/docs/polymer/polymer.html#observeblock
Or you can add an extra attribute to your model called for example 'refresh' (boolean) and each time you modify some of the internal values also modify it simply by setting refresh = !refresh, then you can observe just one attribute instead of many. This is a good case when your model include multiple nested attributes.
Polymer('x-element', {
observe: {
'model.refresh': 'modelUpdated'
},
ready: function() {
this.model = {
title: this.noteTitle,
text: this.noteText,
slug: this.noteSlug,
refresh: false
};
},
modelUpdated: function(oldValue, newValue) {
var value = Path.get('model.title').getValueFrom(this);
},
buttonClicked: function(e) {
this.model.title = 'Title';
this.model.text = 'Text';
this.model.slug = 'Slug';
this.model.refresh = !this.model.refresh;
}
});
what I do in this situation is use the * char to observe any property change in my array, here an example of my JSON object:
{
"config": {
"myProperty":"configuraiont1",
"options": [{"image": "" }, { "image": ""}]
}
};
I create a method _myFunctionChanged and I pass as parameter config.options.* then every property inside the array options is observed inside the function _myFunctionChanged
Polymer({
observers: ['_myFunctionChanged(config.options.*)']
});
You can use the same pattern with a object, instead to use an array like config.options. you can just observe config.

angularjs directive call function specified in attribute and pass an argument to it

I want to create a directive that links to an attribute. The attribute specifies the function that should be called on the scope. But I also want to pass an argument to the function that is determined inside the link function.
<div my-method='theMethodToBeCalled'></div>
In the link function I bind to a jQuery event, which passes an argument I need to pass to the function:
app.directive("myMethod",function($parse) {
restrict:'A',
link:function(scope,element,attrs) {
var expressionHandler = $parse(attrs.myMethod);
$(element).on('theEvent',function( e, rowid ) {
id = // some function called to determine id based on rowid
scope.$apply(function() {expressionHandler(id);});
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
Without passing the id I can get it working, but as soon as I try to pass an argument, the function is not called anymore
Marko's solution works well.
To contrast with recommended Angular way (as shown by treeface's plunkr) is to use a callback expression which does not require defining the expressionHandler. In marko's example change:
In template
<div my-method="theMethodToBeCalled(myParam)"></div>
In directive link function
$(element).click(function( e, rowid ) {
scope.method({myParam: id});
});
This does have one disadvantage compared to marko's solution - on first load theMethodToBeCalled function will be invoked with myParam === undefined.
A working exampe can be found at #treeface Plunker
Just to add some info to the other answers - using & is a good way if you need an isolated scope.
The main downside of marko's solution is that it forces you to create an isolated scope on an element, but you can only have one of those on an element (otherwise you'll run into an angular error: Multiple directives [directive1, directive2] asking for isolated scope)
This means you :
can't use it on an element hat has an isolated scope itself
can't use two directives with this solution on the same element
Since the original question uses a directive with restrict:'A' both situations might arise quite often in bigger applications, and using an isolated scope here is not a good practice and also unnecessary. In fact rekna had a good intuition in this case, and almost had it working, the only thing he was doing wrong was calling the $parsed function wrong (see what it returns here: https://docs.angularjs.org/api/ng/service/$parse ).
TL;DR; Fixed question code
<div my-method='theMethodToBeCalled(id)'></div>
and the code
app.directive("myMethod",function($parse) {
restrict:'A',
link:function(scope,element,attrs) {
// here you can parse any attribute (so this could as well be,
// myDirectiveCallback or multiple ones if you need them )
var expressionHandler = $parse(attrs.myMethod);
$(element).on('theEvent',function( e, rowid ) {
calculatedId = // some function called to determine id based on rowid
// HERE: call the parsed function correctly (with scope AND params object)
expressionHandler(scope, {id:calculatedId});
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
Not knowing exactly what you want to do... but still here's a possible solution.
Create a scope with a '&'-property in the local scope.
It "provides a way to execute an expression in the context of the parent scope" (see the directive documentation for details).
I also noticed that you used a shorthand linking function and shoved in object attributes in there. You can't do that. It is more clear (imho) to just return the directive-definition object. See my code below.
Here's a code sample and a fiddle.
<div ng-app="myApp">
<div ng-controller="myController">
<div my-method='theMethodToBeCalled'>Click me</div>
</div>
</div>
<script>
var app = angular.module('myApp',[]);
app.directive("myMethod",function($parse) {
var directiveDefinitionObject = {
restrict: 'A',
scope: { method:'&myMethod' },
link: function(scope,element,attrs) {
var expressionHandler = scope.method();
var id = "123";
$(element).click(function( e, rowid ) {
expressionHandler(id);
});
}
};
return directiveDefinitionObject;
});
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) {
alert(id);
};
});
</script>
You can create a directive that executes a function call with params by using the attrName: "&" to reference the expression in the outer scope.
We want to replace the ng-click directive with ng-click-x:
<button ng-click-x="add(a,b)">Add</button>
If we had this scope:
$scope.a = 2;
$scope.b = 2;
$scope.add = function (a, b) {
$scope.result = parseFloat(a) + parseFloat(b);
}
We could write our directive like so:
angular.module("ng-click-x", [])
.directive('ngClickX', [function () {
return {
scope: {
// Reference the outer scope
fn: "&ngClickX",
},
restrict: "A",
link: function(scope, elem) {
function callFn () {
scope.$apply(scope.fn());
}
elem[0].addEventListener('click', callFn);
}
};
}]);
Here is a live demo:
http://plnkr.co/edit/4QOGLD?p=info
Here's what worked for me.
Html using the directive
<tr orderitemdirective remove="vm.removeOrderItem(orderItem)" order-item="orderitem"></tr>
Html of the directive: orderitem.directive.html
<md-button type="submit" ng-click="remove({orderItem:orderItem})">
(...)
</md-button>
Directive's scope:
scope: {
orderItem: '=',
remove: "&",
My solution:
on polymer raise an event (eg. complete)
define a directive linking the event to control function
Directive
/*global define */
define(['angular', './my-module'], function(angular, directives) {
'use strict';
directives.directive('polimerBinding', ['$compile', function($compile) {
return {
restrict: 'A',
scope: {
method:'&polimerBinding'
},
link : function(scope, element, attrs) {
var el = element[0];
var expressionHandler = scope.method();
var siemEvent = attrs['polimerEvent'];
if (!siemEvent) {
siemEvent = 'complete';
}
el.addEventListener(siemEvent, function (e, options) {
expressionHandler(e.detail);
})
}
};
}]);
});
Polymer component
<dom-module id="search">
<template>
<h3>Search</h3>
<div class="input-group">
<textarea placeholder="search by expression (eg. temperature>100)"
rows="10" cols="100" value="{{text::input}}"></textarea>
<p>
<button id="button" class="btn input-group__addon">Search</button>
</p>
</div>
</template>
<script>
Polymer({
is: 'search',
properties: {
text: {
type: String,
notify: true
},
},
regularSearch: function(e) {
console.log(this.range);
this.fire('complete', {'text': this.text});
},
listeners: {
'button.click': 'regularSearch',
}
});
</script>
</dom-module>
Page
<search id="search" polimer-binding="searchData"
siem-event="complete" range="{{range}}"></siem-search>
searchData is the control function
$scope.searchData = function(searchObject) {
alert('searchData '+ searchObject.text + ' ' + searchObject.range);
}
This should work.
<div my-method='theMethodToBeCalled'></div>
app.directive("myMethod",function($parse) {
restrict:'A',
scope: {theMethodToBeCalled: "="}
link:function(scope,element,attrs) {
$(element).on('theEvent',function( e, rowid ) {
id = // some function called to determine id based on rowid
scope.theMethodToBeCalled(id);
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}