How to set list which is of parent element with this subscription data.?
Don't want to alter or add attribute to subscription element as it's independent of parent.
I got polymer element structure like this:
<polymer-element name="parent">
<polymer-element name="subscription" id="subscription">
<polymer-element>
<div id="list">
<template repeat="{{item in list}}">
<item name="{{item}}">
</item>
</template>
</div>
<script>
Polymer('parent', {
ready: function() {
this.list = this.list || [];
subscribe = this.$.subscription;
//Anytime if there is any change in subscription it send the data.
subscribe.addEventListener('data_from_subscription_element', function(x) {
// How to set list which is of parent element with this subscription data.?
// Don't want to alter or add attribute to subscription element as it's independent of parent.
this.data;
});
}
});
</script>
<polymer-element>
That looks like common problem of changing scope you are in. Try this:
<script>
Polymer('parent', {
ready: function() {
var that = this; // Giving the current scope to that;
this.list = this.list || [];
subscribe = this.$.subscription;
subscribe.addEventListener('data_from_subscription_element', function(x) {
that.list = this.data; // Using that here
});
}
});
</script>
Related
I want to add a behavior after a component/behavior already loaded or a certain function that will add a behevaior to its components.
Something like this:
<script>
// samplebehavior.html file
// this is the behavior file
samplebehavior = {
testAlert: function(){
alert('test');
}
};
</script>
// my-component.html
<script>
Polymer({
is: "my-component",
test: function() {
url = "samplebehavior.html";
var importHTML = new Promise(function(resolve, reject) {
Polymer.Base.importHref(url, function(e) {
resolve(e.target);
}, reject);
});
importHTML.then(function(element) {
// add a behavior here
// I know this script does not work
this.push('behaviors', samplebehavior);
});
}
});
</script>
So that I can access the testAlert() function.
How to I add a behavior dynamically?
To the best of knowledge it's not possible.
Behaviors are mixed in with the element definition when the prototype is built.
What you could do is generate the behaviors array dynamically
var behavior = {
properties: {
smth: {
value: 'initial value'
}
}
}
Polymer({
is: 'my-elem',
behaviors: getBehaviors()
});
function getBehaviors() {
return [ behavior ];
}
Remember though that getBehaviors will only ever be called once. After that you won't be able to change behaviors of your elements.
It's ugly but you can copy all members of your behavior in your object
<script>
// samplebehavior.html file
// this is the behavior file
samplebehavior = {
testAlert: function(){
alert('test');
}
};
</script>
// my-component.html
<script>
Polymer({
is: "my-component",
test: function() {
url = "samplebehavior.html";
var importHTML = new Promise(function(resolve, reject) {
Polymer.Base.importHref(url, function(e) {
resolve(e.target);
}, reject);
});
importHTML.then(function(element) {
for (let member in samplebehavior) {
this[member] = samplebehavior[member];
}
});
}
});
</script>
Or maybe you can call the internal method _prepBehavior() https://github.com/Polymer/polymer/blob/ff6e884ef4f309d41491333860a8bc9c2f178696/src/micro/behaviors.html#L111
But i don't know if that can do side effects
<script>
// samplebehavior.html file
// this is the behavior file
samplebehavior = {
testAlert: function(){
alert('test');
}
};
</script>
// my-component.html
<script>
Polymer({
is: "my-component",
test: function() {
url = "samplebehavior.html";
var importHTML = new Promise(function(resolve, reject) {
Polymer.Base.importHref(url, function(e) {
resolve(e.target);
}, reject);
});
importHTML.then(function(element) {
this.push('behaviors', samplebehavior);
this._prepBehaviors();
});
}
});
</script>
With introduction to new value for lazyRegister setting this has become partially possible.
By partially i mean you can only do this if you can edit the code of the element in which you want to add the behavior.
Three changes you'll require to add behavior dynamically are
Set lazyRegister setting to max
window.Polymer = {
lazyRegister:"max"
};
Just like #tomasz suggested, in the element you want to add behavior dynamically add behaviors using a function. This is so, because we won't be able to access the element from where we'll try to add new behavior dynamically(atleast i was not able to).
Polymer({
.
.
behaviors: getBehavior(),
.
.
});
function getBehavior(){
var myArr = [myBehavior];
document.addEventListener('add-behavior',function(e){
debugger;
myArr.push(e.detail);
});
return myArr;
}
From the element where you want to dynamically add the behavior use beforeRegister callback to dispatch an event adding the new behavior object
beforeRegister: function(){
var event = new CustomEvent('add-behavior',{
detail:{
func: function(){console.log(this.myProp,"!")}
}
});
document.dispatchEvent(event);
}
Here's a plunkr for working example.
I am working on a simple react example that will alter the content of a header on the screen based on what a user enters in a text field. Here's the react code:
var GreeterForm = React.createClass({
onFormSubmit: function(e){
e.preventDefault();
var name = this.refs.name;
this.refs.name.value = '';
this.props.onNewName(name);
},
render: function(){
return(
<form onSubmit={this.onFormSubmit}>
<input type="text" ref="name"/>
<button>Set Name</button>
</form>
);
}
});
var Greeter = React.createClass({
//returns an object of the default properties to be used
//these are used if no properties are passed in
getDefaultProps: function(){
return {
name: 'React!',
message: "this is from a component!"
};
},
//maintains a state for the component. Maintains the state as an object
//this is a default method for react and we override it.
getInitialState: function(){
return {
name: this.props.name
};
},
handleNewName: function(name){
this.setState({
name: name
});
},
//renders the greeter react component
render: function() {
var name = this.state.name;
var message = this.props.message;
return (
<div>
<h1>Hello {name}!</h1>
<p>{message}</p>
<GreeterForm onNewName={this.handleNewName}/>
</div>
);
}
});
var firstName = "DefaultName";
var mess = "This is a message from react."
//note that name and message are passed in as properties
ReactDOM.render(<Greeter name={firstName} message={mess}/>, document.getElementById('app'));
You can see that GreeterForm is nested within Greeter and is supposed to alter the content of the h1 tag when the user submits.
However, the content of the h1 tag is not changing. I sprinkled alert(name.value) along the code to ensure the correct name from the input field was being passed around, and that all checked out.
What could it be? Is something wrong with my setState function?
Here's the HTML if needed:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.js"></script>
</head>
<body>
<div id="app">
</div>
<script type="text/babel" src="app.jsx"></script>
</body>
</html>
You are not sending value of input. you are sending whole html element
in onFormSubmit function change this line
var name = this.refs.name;
to
var name = this.refs.name.value;
I want to execute a JavaScript code on load of the Shadow DOM in my custom element.
I tried the following code but it did not work
x-component.html:
<template id="myTemplate">
<div>I am custom element</div>
</template>
<script>
var doc = this.document._currentScript.ownerDocument;
var XComponent = document.registerElement('x-component', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
var root = this.createShadowRoot();
var template = doc.querySelector('#myTemplate');
var clone = document.importNode(template.content, true);
clone.addEventListener('load', function(e) {
alert('Shadow DOM loaded!');
});
root.appendChild(clone);
}
}
})
});
</script>
Then I use it in another html as follows -
index.html:
<!doctype html>
<html >
<head>
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="import" href="x-component.html">
</head>
<body>
<x-component></x-component>
</body>
</html>
The doc variable is used as I am using Polymer webcomponents.js polyfill and the polyfill needs it.
What is the right syntax to listen to load event of Shadow DOM?
AFAIK, the only way to achieve this is to use MutationObserver:
attachedCallback: {
value: function() {
var root = this.createShadowRoot();
var template = document.querySelector('#myTemplate');
var clone = document.importNode(template.content, true);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes) { // this is definitely a subject to change
alert('Shadow is loaded');
};
});
})
observer.observe(root, { childList: true });
root.appendChild(clone);
}
}
I would be glad to know if there is more elegant way, but for now I use this one.
Live preview: http://plnkr.co/edit/YBh5i2iCOwqpgsUU6En8?p=preview
I need to access the custom element and call its method from the click event callback.
<polymer-element name="my-element">
<template>
<style type="text/css" media="screen">
...
</style>
<ul id="my_data"></ul>
</template>
<script>
Polymer('my-element', {
dataSelected: function(selectedText) {
//...
},
setData: function(data) {
for (var i = 0; i < data.length; i++) {
var li = document.createElement('li');
li.addEventListener('click', function(e) {
// how can I call dataSelected() from here?
});
li.innerText = data[i];
this.$.my_data.appendChild(li);
}
}
});
</script>
</polymer-element>
How can I call the custom element's dataSelected() method from the callback?
You can use bind to attach a this context to any function, so:
li.addEventListener('click', function(e) {
this.dataSelected(e.target.innerText);
}.bind(this));
http://jsbin.com/xorex/4/edit
But you can make things easier by using more Polymer sugaring. For example, you can publish data and use the observation system, like so:
<polymer-element name="my-element" attributes="data">
...
data: [], // type hint that data is an array
...
dataChanged: function() { // formerly setData
http://jsbin.com/xorex/5/edit
Also, you can use the built-in event system instead of addEventListener
<polymer-element name="my-element" attributes="data">
...
<ul id="my_data" on-tap="{{dataTap}}"></ul>
...
dataTap: function(e) { // `tap` supports touch and mouse
if (e.target.localName === 'li') {
this.dataSelected(e.target.textContent);
}
}
http://jsbin.com/xorex/6/edit
But the biggest win is using <template repeat> instead of creating elements in JavaScript. At that point, the complete element can look like this:
<polymer-element name="my-element" attributes="data">
<template>
<ul id="my_data">
<template repeat="{{item in data}}">
<li on-tap="{{dataTap}}">{{item}}</li>
</template>
</ul>
</template>
<script>
Polymer('my-element', {
data: [],
dataTap: function(e) {
console.log('dataSelected: ' + e.target.textContent);
}
});
</script>
</polymer-element>
http://jsbin.com/xorex/7/edit
You could insert element = this; at the beginning of your setData() function and call element.dataSelected(); in the event handler.
But i think for what you want to achieve, you'd better use a repeat template (Iterative templates) and a direct binding to your click handler function (Declarative event mapping).
TL;DR: Is there a way to dynamically set a directive based on a parameter value? Something similar to ng-class for setting css elements, but a way to set the directive based on the value in the scope. I would have the value in the scope so I could call:
<div class="data.directiveType"></div>
When
data.directiveType = "my-directive"
the div would become
<div class="my-directive"></div>
and myDirective would be invoked?
Detailed Question:
What I am trying to do is allow the user to add elements to the web application and I wanted the directive for each element to be added based on what the user clicks.
I have the following Directives:
app.directive("mySuperman", function(){
//directive logic
});
app.directive("myBatman", function(){
//directive logic
});
app.directive("myWonderWoman", function(){
//directive logic
});
I have the following controller
app.controller("ParentCtrl", function($scope){
$scope.superHeros = [];
var superman = {directiveType: "my-superman"};
var batman = {directiveType: "my-batman"};
var wonderWoman = {directiveType: "my-wonder-woman"}
$scope.addBatman = function()
{
var batmanInstance = {}
angular.copy(batman, batmanInstance);
$scope.superHeros.push(batmanInstance);
}
$scope.addSuperman = function()
{
var supermanInstance = {}
angular.copy(superman, supermanInstance);
$scope.superHeros.push(supermanInstance);
}
$scope.addWonderWoman = function()
{
var wonderwomanInstance = {}
angular.copy(wonderWoman, wonderwomanInstance);
$scope.superHeros.push(wonderwomanInstance);
}
});
In the index.html I have
<body ng-controller="ParentCtrl>
<a ng-click="addBatman()">Add Batman</a>
<a ng-click="addSuperman()">Add Superman</a>
<a ng-click="addWonderWoman()">Add WonderWoman</a>
<div ng-repeat="hero in superHeros">
<!-- The following doesn't work, but it is the functionality I am trying to achieve -->
<div class={{hero.directiveType}}></div>
<div>
</body>
The other way I thought of doing this was just using ng-include in the ng-repeat and adding the template url to the hero object instead of the directive type, but I was hoping there was a cleaner way that I could make better use of the data binding and not have to call ng-include just to call another directive.
You can create a directive that takes the directive to add as a parameter, adds it to the element and compiles it. Then use it like this:
<div ng-repeat="hero in superHeros">
<div add-directive="hero.directiveType"></div>
</div>
Here is a basic example:
app.directive('addDirective', function($parse, $compile) {
return {
compile: function compile(tElement, tAttrs) {
var directiveGetter = $parse(tAttrs.addDirective);
return function postLink(scope, element) {
element.removeAttr('add-directive');
var directive = directiveGetter(scope);
element.attr(directive, '');
$compile(element)(scope);
};
}
};
});
Demo: http://plnkr.co/edit/N4WMe8IEg3LVxYkdjgAu?p=preview