How to pass parameters in Polymer 2.0 on-tap function? - javascript

I'm using Polymer 2.0 and I have a dom-repeat for different to-do cards. What I want to do is remove the card when I click on it.
So I tried this on-tap=deleteNote([[index]]) which uses the index from the dom-repeat. However Polymer doesn't execute the function.
What am I doing wrong?

Another solution could be the dataset object within the event.target. You can define your properties with the data- prefix:
<div on-tap="doSomething" data-item$="[[item]]"></div>
And within your doSomething() listener you can get the dataset object:
doSomething(event) {
const item = event.target.dataset.item;
...
}

You can access the model via the event argument's model property.
So you can access the index from event.model.index.

Well, I am aware you can't. There are many discussions about that on the internet.
Of course, there is a way how to pass argument to function. You can save [[index]] in attribute of element and then get the attribute when needed.
Example:
<div on-tap='_deleteNote' indexed$='[[index]]'>
Then in script:
_deleteNote(e) {
var index = e.target.getAttribute('indexed');
...
}
Once you get index, you can do whatever you want with it.
Don't forget to extend Polymer gestures if you are not using hybrid elements in Polymer 2.0.
class Foo extends Polymer.GestureEventListeners(Polymer.Element) {}
More about it: https://www.polymer-project.org/2.0/docs/devguide/gesture-events#using-gesture-events
What is hybrid element: https://www.polymer-project.org/2.0/docs/devguide/hybrid-elements

on-tap does not appear to be implemented in Polymer 2.0. If you instead use on-click then it will work.
EDIT: see below comment.

Related

Add HTML object to an event javascript/meteor

How can I add HTML objects to an event?I would like to do something like this:
Template.Schedule.events({
'dblclick .mycol' (event){
event.target.childNodes.append("<strong>Test</strong>");
}
});
I know I could style it and change the innerHTML and so on for the given example, but I actually want to add other HTML objects, like a select-tag, how can I do that?
The vanilla JS way
You could use innerHTML here to change the html content of the clicked element:
'dblclick .mycol' (event){
const target = event.currentTarget
target.innerHTML = target.innerHTML + "<strong>Test</strong>"
}
If you want to manipulate the parent in the event you can use outerHTML
The jQuery way
Your approach of using append is requiring jQuery:
'dblclick .mycol' (event){
$(event.currentTarget).append($("<strong>Test</strong>"))
}
Bonus: Optimization for using jQuery
In a meteor blaze template-events each event has a reference to the template instance. This template instance keeps a reference to a jQuery object and it's part of the DOM that it manipulates.
template.$ returns a jQuery object of those same elements. jQuery
objects are similar to arrays, with additional methods defined by the
jQuery library.
The template instance serves as the document root for the selector.
Only elements inside the template and its sub-templates can match
parts of the selector.
You can access it via templateInstance.$ if your seconds event parameter is namend templateInstance. With
'dblclick .mycol' (event, templateInstance){
templateInstance.$(event.currentTarget).append($("<strong>Test</strong>"))
}
This saves jQuery the need to traverse the whole DOM, makes it more efficient on large documents.
The Meteor Blaze way
Now these are neat little tricks when there is need for manipulation on a small scale. However, you may want your app to be scalable and profit all the time from the Blaze rendering engine.
In such cases you may rather want to generate a way of dynamically inserting templates.
Consider the following template, that is nowhere imported yet:
rowcontent.html
<template name="rowcontent">
<strong>Test</strong>
<p>someData{{someData}}</p>
</template>
rowcontent.js
import './rowcontent.html' // currently just the import
You can dynamically add it at runtime to an element using Blaze.renderWithData so:
'dblclick .mycol' (event, templateInstance) {
import './rowcontent.js' // use the right path here
Blaze.renderWithData(Template.rowcontent, {someData: 'toBePassedToRowContent'}, event.currentTarget)
}
which will result in:
This is my col Test
someDatatoBePassedToRowContent
The advantage of this approach is that you can pass the data to the template and have all the reactive benefits remaining, thus handle the newly added template like any other template in Meteor.
Alternatives
Declarative dynamic templates using Template.dynamic

KnockoutJs Components - add default class

Knockout gives you two ways of instantiating a component, either with a custom html element or with the component binding.
However I have discovered a slight issue when trying to style the root component element. It's fine if you just use the custom element syntax as you can just assign css styles to that - however, if you then use the component binding, the css rules don't match and so they fail.
Ideally I want to support both scenarios as they both have their uses. If I could get knockout to add a class to the root component element which is just the component name it would solve the issue but reading the documentation it isn't clear where it would be best to do this.
I've already got a custom template loader which retrieves the template from an ajax call, but this template is just the inner html of the root node.
Basically I want this:
<my-custom-element>
...
...
<my-custom-element>
To become this:
<my-custom-element class="my-custom-element">
...
...
<my-custom-element>
Anyone got any ideas?
You can use "createViewModel" method and access element in the component (e.g. to add some class):
ko.components.register('some-component', {
viewModel: {
createViewModel: function(params, componentInfo) {
var $element = $(componentInfo.element.children[0]);
// some other code ...
}
},
template: "<div></div>"
});

controller for knockout.js custom element

Sometimes a component/custom element has some UI logic which requires some UI code, it's something which can't be done by binding to the component's view-model.
For example, let's say the component needs to change the way it looks based on available space, and this requires manipulating elements by JavaScript code.
What I need is a controller for the UI.
For example, imagine we have a component called myGadget for which I have myGadget.html, myGadgetViewModel.js and I also want to have myGadgetView.js
Within the myGadgetView.js I want to have something like this:
function myGadgetView(element)
{
// element is the custom element's node
}
What is the best way to do this in Knockout?
Should I combine component with custom binding?
With a custom binding I could get access to the element, so the HTML of the component would look like this:
<script id="myBar-template">
<div data-bind="myGadget : ...">
</div>
</script>
and I need to put somewhere this:
ko.bindingHandlers.myGadget = {
init: function (element, valueAccessor)
{
// I have access to element node
var myGadgetView = new myGadgetView(element);
},
update: function (element, valueAccessor)
{
// I have access to element node
}
}
I'm not sure about using custom binding for this, I wonder if there's a better approach.
For example, I'm looking to the custom component loading, but I don't have a clear idea yet.
When defining a component, you can specify a createViewModel function. This function will be passed the element the component will be bound to. According the Knockout documentation, it's still preferable to use custom bindings to manipulate the view.
Any manipulation of the view should be done in binding handlers, but that doesn't mean you can't make something like a jQuery plug-in, which your myGadgetView.js would be, and use that in the binding handler. You just wouldn't want your plug-in to be aware of your viewmodel, nor your viewmodel to be aware of the plug-in. The binding handler would mediate, mapping viewmodel elements to plug-in parameters.

Getting the list of polymer published properties

Is there a way of getting the list of all published properties defined in a polymer component? (e.g. get the available properties of component public API)
<polymer-element name="sample-component"
attributes="foo1 foo2" bar="10">
/* ... */
</polymer-element>
<sample-component foo1="5"></sample-component>
document.querySellector('sample-component').attributes;
// returns bar and foo1 (which have a value assigned to them)
// but I want to get foo1 and foo2
It's best to use element.publish to get the list of published properties for an element. (In polymer 1.0 element.properties does the same).
element.getAttribute('attributes) won't include the publish properties that are setup in a publish block.
You can access the element definition (i.e. the polymer-element itself) through the element property. So
document.querySelector('sample-component')
.element.getAttribute('attributes')
gives you 'foo1 foo2' (btw. inside an element you can simply write this.element.)
Be aware that this only works after Polymer has registered and processed all elements, so depending on where you want to use this statement, you may have to put it into a polymer-ready event handler:
<script>
addEventListener('polymer-ready', function() {
console.log(document.querySelector('sample-component')
.element.getAttribute('attributes'));
});
</script>
Update:
If you want to get a list of all published properties (i.e. the ones in the attributes attribute plus the ones of the publish: {} property), you can use
Object.keys(document.querySelector('sample-component').publish)
which gives you ['foo1', 'foo2'].

data-win-bind issues: converter only runs once and unable to bind id of element

I have the following html that is bound to an object containing id and status. I want to translate status values into a specific color (hence the converter function convertStatus). I can see the converter work on the first binding, but if I change status in the binding list I do not see any UI update nor do I see convertStatus being subsequently called. My other issue is trying to bind the id property of the first span does not seem to work as expected (perhaps it is not possible to set this value via binding...)
HTML:
<span data-win-bind="id: id">person</span>
<span data-win-bind="textContent: status converter.convertStatus"></span>
Javascript (I have tried using to modify the status value):
// persons === WinJS.Binding.List
// updateStatus is a function that is called as a result of status changing in the system
function updateStatus(data) {
persons.forEach(function(value, index, array) {
if(value.id === data.id) {
value.status = data.status;
persons.notifyMutated(index);
}
}, this);
}
I have seen notifyMutated(index) work for values that are not using a converter.
Updating with github project
Public repo for sample (not-working) - this is a really basic app that has a listview with a set of default data and a function that is executed when the item is clicked. The function attempts to randomize one of the bound fields of the item and call notifyMutated(...) on the list to trigger a visual updated. Even with defining the WinJS.Binding.List({ binding: true }); I do not see updates unless I force it via notifyReload(), which produces a reload-flicker on the listview element.
To answer your two questions:
1) Why can't I set id through binding?
This is deliberately prevented. The WinJS binding system uses the ID to track the element that it's binding to (to avoid leaking DOM elements through dangling bindings). As such, it has to be able to control the id for bound templates.
2) Why isn't the converter firing more than once?
The Binding.List will tell the listview about changes in the contents of the list (items added, removed, or moved around) but it's the responsibility of the individual items to notify the listview about changes in their contents.
You need to have a data object that's bindable. There are a couple of options:
Call WinJS.Binding.as on the elements as you add them to the collection
Turn on binding mode on the Binding.List
The latter is probably easier. Basically, when you create your Binding.List, do this:
var list = new WinJS.Binding.List({binding: true});
That way the List will call binding.as on everything in the list, and things should start updating.
I've found that if I doing the following, I will see updates to the UI post-binding:
var list = new WinJS.Binding.List({binding: true});
var item = WinJS.Binding.as({
firstName: "Billy",
lastName: "Bob"
});
list.push(item);
Later in the application, you can change some values like so:
item.firstName = "Bobby";
item.lastName = "Joe";
...and you will see the changes in the UI
Here's a link on MSDN for more information:
MSDN - WinJS.Binding.as
Regarding setting the value of id.
I found that I was able to set the value of the name attribute, for a <button>.
I had been trying to set id, but that wouldn't work.
HTH
optimizeBindingReferences property
Determines whether or not binding should automatically set the ID of an element. This property should be set to true in apps that use Windows Library for JavaScript (WinJS) binding.
WinJS.Binding.optimizeBindingReferences = true;
source: http://msdn.microsoft.com/en-us/library/windows/apps/jj215606.aspx

Categories