getting the value of a code-mirror element - polymer

I am using the code-mirror component from the Polymer Designer, and can set the initial value, but cannot see how to get changes to the code from the user.
I initialise the code-mirror using
<code-mirror id="code_mirror" value="{{code}}">
</code-mirror>
and would like to listen for changes in {{code}}, but codeChanged doesn't seem to fire.
I know I can get the actual value using code_mirror.$.mirror.getValue(), but would like to use data-binding.
I have tried using on-change to no avail.

Assuming you're using https://github.com/PolymerLabs/code-mirror what you need to do is make the CodeMirror instance created in the ready handle some events that the instance itself is emitting, then make the code-mirror element fire any custom event (something which I know is called event relay)
The following example makes the polymer element fire the custom event code-change whenever the editor value is changed
ready: function() {
var me = this;
//...
this.mirror = CodeMirror(this.shadowRoot, { /* ... */ });
this.mirror.on('change', function () {
// me = polymer instance
me.fire('code-change', { value: me.mirror.getValue() })
});
}
Then any instance of the polymer custom element would need to listen to that event using Polymer's declarative event mapping or through addEventListener
1st case (if code-mirror is inside another <polymer-element />):
<code-mirror on-code-change="{{ onCodeChange }}"></code-mirror>
// ...
<script>
Polymer({
onCodeChange: function(event, detail, sender) { ... }
});
</script>
2nd case ():
<code-mirror></code-mirror>
<script>
document
.querySelector('code-mirror')
.addEventListener('code-change', function () { ... });
</script>

Related

Bind Polymer 2.x event handler to a global function?

I have some Polymer elements that are not inside an element/app, and I can't figure out how to attach their event handlers, such as on-click, to a global javascript function.
For example, let's say my code looks something like this
<head>
// Import stuff...
function login(){
// Do stuff
}
</head>
<body unresolved>
<dom-if id="signInItem">
<template>
<paper-button on-tap="login"><iron-icon icon="icons:account-circle"></iron-icon> Log in</paper-button>
</template>
</dom-if>
</body>
This won't work, because on-tap expects to bind to a property of an enclosing element (I guess). What are my options?
give some id for paper-button
<paper-button id="button"></paper-button>
in javascript you can add eventlistener as shown below
this.$.button.addEventListener('click', e => {
console.log("clicked");
// write your code
});
or you can write your code in seperate function
ready() {
super.ready();
this.$.button.addEventListener('click', e => this.handleClick(e));
}
handleClick(e) {
console.log(e);
}
If you like to fire a function not in polymer you may call;
this.dispatchEvent(new CustomEvent('login', { bubbles: true, composed: true, detail: true }));
This will fire your login listen in global js (ie. main.js) as below;
addEventListener('login', function (e) {
// Do something for login
})
EDIT: I think you also need to change dom-if to dom-bind in order to bind this code as its at root level
You can always forward a call to a local class member function (MyElement.login) to a global namespace:
/* 1 */ class MyElement extends Polymer.Element {
/* 2 */ // ...
/* 3 */
/* 4 */ login: (() => (evt) (MyGlobalNamespace || window).login.bind(this))()
/* 5 */ }
You can even omit the surrounding closure function - if you're not using this within the implementation of login -, simplifying line 4 to: login: (MyGlobalNamespace || window).login

Pass data to another Polymer component

I am working on a dashboard, in which I have a search panel at the top (let's call it component A), where users can enter a query. The value of this input will change a lot of other components in the dashboard (not only components that are its direct descendants or siblings). I want to send the search value from component A to component B, which should then respond by performing some action with the input value.
I have tried a few things:
Directly calling the function in component B. Haven't been able to get that to work at all.
Manually setting B's local property value and using an observer to trigger a function call. I manager to set the value, but the observer does not trigger.
Using a global variable, which I can easily access across components, but I still can't trigger functions in specific components.
How can I best do this?
I'm relatively new to Polymer, so forgive me if my ideas aren't completely 'Polymerised' :)
Approach 1
<dom-module id="component-B">
<template>
</template>
<script>
Polymer({
is: 'component-B',
properties: {
id: '',
observer: '_idUpdate'
},
_idUpdate: function(){
console.log("HELLO");
}
});
</script>
</dom-module>
<dom-module id="component-A">
<template>
</template>
<script>
Polymer({
is: 'component-A',
idSearch: function() {
var id = this.$.search.value;
document.querySelector('component-B').properties.id = id;
},
});
</script>
</dom-module>
As you want to send data to multiple elements (which might not be siblings of the firing element) you can use any of these two methods
Use iron-signal to fire the signal and then in all the elements where you want the data use iron-signal tag to listen to the signal
<iron-signals on-iron-signal-<signal-name>="<function>"></iron-signals>
You can also use standard HTML method dispatchEvent to fire a signal and then add eventListeners in all the element where you want data.

Polymer. Way to dynamically add or remove observer

Is there a way to add or remove observer not in the moment of element initing?
I can define observer this way:
observers: ['dataChanged(data.*)']
Can i remove this observer later or can I set this observer different way than that?
You can easily add an observer dynamically, either by:
this._addObserverEffect("property", observerFunction);
or
this._addComplexObserverEffect("dataChanged(data.*)");
Removing is harder and Polymer does not provide a function to do this. Although you could search for it in the _propertyEffects array, I wouldn't recommend it. Maybe just check in your observer function whether it should still be active, and return if not.
you maybe can try this way:
configure your data property in the element with notify: true, so you can add a listener to changes with plain js
var element=document.querySelector("#YOUR-ELE-ID");
element.addEventListener("data-changed", function(e) {
//triggered when data property changes
});
https://www.polymer-project.org/1.0/docs/devguide/properties#notify
and to remove the bound listener you can call removeEventListener
https://developer.mozilla.org/de/docs/Web/API/EventTarget/removeEventListener
Example 1 - plain JS :
document.addEventListener('WebComponentsReady', function(e) {
var element=document.querySelector("#YOUR-ELE-ID");
element.addEventListener("data-changed", function(e) {
//triggered when data property changes
});
});
Example 2 - in custom element:
//...element definition...
ready: function() {
var element=document.querySelector("#YOUR-ELE-ID");
element.addEventListener("data-changed", function(e) {
//triggered when data property changes
});
}

Observing an array that is initialised in the created lifecycle event

I have a working Polymer prototype, a code snippet of which is:
Polymer({
myData: [],
observe:{
myData: 'myDataChange'
},
myDataChange: function(val, newVal){ ... }
...
However, under the the attribute hinting section of the developer API, it states that objects and arrays should be initialised in the created lifecycle callback, not on the prototype. So, I changed the code snippet above to:
Polymer({
created: function(){
this.myData = [];
},
observe:{
myData: 'myDataChange'
},
myDataChange: function(val, newVal){ ... }
...
As soon as I make this change, the change watcher function no long invokes.
The myData property of my element instance is being populated by jQuery in an document ready callback. Moving this code into a 'polymer-ready' callback on the containing page solves this issue.
My concern with this is that my pages are going to be littered with polymer-ready events for the initial data population.
I amended my prototype so that the custom element is added to the DOM after a 5 second timeout, after the polymer-ready event was fired. Injecting the DOM like this doesn't fire the polymer ready event again.
Is this the correct/best approach to initialising properties on a Polymer element? I could manually fire an event from my custom element to say its loaded but this seems a bit crude. Any better ideas?
You shouldn't use any custom element before polymer-ready event (unless you're doing it intentionally), I think the best you could do is to replace every ready callback with polymer-ready.
However if you still want to use ready callback you could call myDataChange inside the element's ready callback:
Polymer({
created: function(){
this.myData = [];
},
observe:{
myData: 'myDataChange'
},
ready: function() {
this.myDataChange([], this.myData);
},
myDataChange: function(val, newVal){ ... }
....

jQuery livequery plug equivalent in jQuery 1.7+

Is there the equivalent of the jQuery livequery plugin for jQuery 1.7+ ?
I'm trying to dynamically bind events, reading the events a DOM element should bind on based on data-* elements.
Test 1
Test 2
.. etc ..
I want to bind all elements with class .js-test but only on the events listed in their data-events attribute.
jQuery.on/live/bind/delegate all require the events to be passed in as params.
This is find for DOM elements that exist on the page when document.ready, however as I update the DOM (AJAX, JS, etc.) I want any new elements with class .js-test to have its events bound as well.
The livequery plugin (which is old, from jQuery 1.3 times) seems to allow this, as it simple requires a selector and a function to run against anything that matches the selector.
As of jQuery 1.7 the on method, supercedes the live method. While it doesn't have an easy method of passing in or matching selectors like you describe, it is possible to accomplish this by passing in the dynamic value of data-events in place of the event type, as long as the data-event value matches that event.
However, since the argument passed into the on method's event parameter -- the first parameter -- is taken from each data-events attribute, from each element in the set of matched elements, we must loop through the collection of matched elements so that we access each elements' individual data-events attribute value separately:
$('.js-test').each(function() {
$(this).on( $(this).attr("data-events"), function() {
// event pulled from data-events attribute
alert("hello - this event was triggered by the " + $(this).attr("data-events") + " action.");
});
});
I want all events to be mapped to the same function, but have different events trigger the function call for different DOM elements.
Since you want to map all of the events to a single function, this solution meets your specific requirements, and solves your problem.
However, should your requirements change and you find you need to map a collection of function events to match each event type, this should get you started:
var eventFnArray = [];
eventFnArray["click"] = function() {
alert("click event fired - do xyz here");
// do xyz
};
eventFnArray["mouseover"] = function() {
alert("mouseover fired - do abc here");
// do abc
};
$('.js-test').each( (function(fn) {
return function() {
$(this).on( $(this).attr("data-events"), function() {
alert("hello - this is the " + $(this).attr("data-events") + " event");
// delegate to the correct event handler based on the event type
fn[ $(this).attr("data-events") ]();
});
}
})(eventFnArray)); // pass function array into closure
UPDATE:
This has been tested and does indeed work for new elements added to the div#container. The problem was in the way the on method functions. The delegating nature of on only works if the parent element is included in the selector, and only if a selector is passed into the second parameter, which filters the target elements by data-events attribute:
HTML:
<div id="container">
Test 1
Test 2
</div>
JavaScript:
$(document).ready(function() {
$('.js-test').each(function() {
var _that = this;
alert($(_that).attr("data-events"));
$(this).parent().on(
$(_that).attr("data-events"),
'.js-test[data-events="'+ $(_that).attr("data-events") +'"]',
function() {
// event pulled from data-events attribute
alert("hello - this event was triggered by the " + $(_that).attr("data-events") + " action.");
}
);
}
);
});
Additionally, use the following jQuery to add an item to the container to test it:
$('#container')
.append("<a href='#' class='js-test' data-events='mouseover'>Test 3</a>");
Try it out:
Here is a jsfiddle that demonstrates the tested and working functionality.