When dynamically creating polymer elements in js, how to initialize attributes before ready() event?
my code below:
var el = document.createElement("my-elem");
el.setAttribute("myAttr", 123); // or el.myAttr = 123.
the ready() event is fired before myAttr is set. actually I want it works like it does in HTML way:
var div = document.createElement("DIV");
div.innerHTML('<my-elem myAttr="123"></my-elem>');
var el = div.children[0];
in this way, myAttr is set when ready() event working, but the code is kinda ugly. is there a way like this:
var el = document.createElement("my-elem", { myAttr: 123});
Related
Using Polymer 1 and Web component tester... testing in shady dom on chrome.
In WCT, trying to stub spToast.display() with stub('sp-toast', { display: ()=> {} }); but I get error with Attempted to wrap undefined property display as function.... what I am doing wrong?
The reason why I am trying to stub it is because I get spToast.display is not a function when the test runs the code base.
original code:
showAgeWarning: function() {
var spApp = Polymer.dom(document).querySelector('sp-app');
var spToast = Polymer.dom(spApp.root).querySelector('sp-toast');
var msg = "foo"
spToast.display('information', msg);
},
test code:
<test-fixture id="sp-veteran">
<template>
<h2>edit veteran</h2>
<sp-app>
<sp-toast></sp-toast>
<sp-veteran>
</sp-veteran>
</sp-app>
</template>
</test-fixture>
setup(function() {
replace('sp-app').with('fake-sp-app');
replace('sp-ajax').with('fake-sp-ajax');
stub('sp-value-dropdown', { setInvalidState: (state)=> {} });
myEl = fixture('sp-veteran');
});
test('it should validate the veteran', function() {
var spApp = Polymer.dom(myEl.root).querySelector('sp-app');
var spToast = Polymer.dom(spApp.root).querySelector('sp-toast');
sinon.stub(spToast, 'display');
When you get Attempted to wrap undefined property display as function it means that it can't replace a method that doesn't exist (yet).
If you actually get a value for var spToast = Polymer.dom(spApp.root).querySelector('sp-toast') in your test, and nothing about your test is going to give display a value, you could just set it, a la spToast.display = function() {}; then you should be able to set a spy on it or what have you as needed.
Put it all together and you could have
test('it should validate the veteran', function() {
var spApp = Polymer.dom(myEl.root).querySelector('sp-app');
var spToast = Polymer.dom(spApp.root).querySelector('sp-toast');
spToast.display = function() {};
sinon.spy(spToast, 'display');
// Trigger the side effect that would lead to `display` being called
assert.equal(
spToast.display.calledOnces,
true
);
});
I have the impression that Firefox is "clearing" the data-* attributes on custom elements.
See the script below works in Chrome with native Web Components support, but in Firefox, it seems that once my click event handler is run, the dataset is empty.
document.addEventListener('DOMContentLoaded', function() {
var popups = document.querySelectorAll('iron-image[data-popup]');
for (var i=0; i < popups.length; i++) {
var img = popups[i];
console.log(img.dataset.popup); // <--- this works
img.addEventListener('click', function(ev) {
var app = document.querySelector('sh-app');
app.showPopup(ev.target.dataset.popup); // <-- This fails, dataset is empty
});
}
});
Side Note: I have also tried the WebComponentsReady event, but it doesn't get fired at all for some reason.
Has anyone run into this issue before and understand the cause?
Is this a bug in Polymer (<iron-image> in this case), the Web Components polyfills, or Firefox's implementation thereof?
Event retargeting
Basically, you have to wrap the event using Polymer.dom to normalize it.
document.addEventListener('DOMContentLoaded', function() {
var popups = document.querySelectorAll('iron-image[data-popup]');
for (var i=0; i < popups.length; i++) {
var img = popups[i];
console.log(img.dataset.popup); // <--- this works
img.addEventListener('click', function(ev) {
var app = document.querySelector('sh-app');
var event = Polymer.dom(ev); // you wrap the event using Polymer.dom
app.showPopup(event.localTarget.dataset.popup); // instead of target you should use localTarget
});
}
});
const googleDiv = function(){
const container = document.createElement('div');
const btnEle = document.createElement('button');
btnEle.type = "button";
btnEle.className = "link-btn";
btnEle.appendChild(document.createTextNode("(Unlink)"));
btnEle.onclick = "unlinkGoogle()";
container.appendChild(btnEle);
container.id = "google-linked-container";
return container;
};
When I create a button via this method, the button appears in the DOM no problem and the button type and classes are as expected, but there is no onclick attribute. Why?
P.S.
btnEle.addEventListener("click", () => { console.log("clicked!"); }); doesn't work either.
Update
I have replicated it on JSFiddle: https://jsfiddle.net/fs1xhgnm/2/
You should assign your handler as a function, instead of string. Also, try to assign onclick handler after the element is appended.
container.appendChild(btnEle);
btnEle.onclick = unlinkGoogle;
You need to pass a reference to the function, either with the onclick, or the better addEventListener
const googleDiv = function() {
const container = document.createElement('div');
const btnEle = document.createElement('button');
btnEle.type = "button";
btnEle.className = "link-btn";
btnEle.appendChild(document.createTextNode("(Unlink)"));
btnEle.addEventListener('click', unlinkGoogle);
container.appendChild(btnEle);
container.id = "google-linked-container";
return container;
};
function unlinkGoogle() {
console.log('clicked');
}
document.body.appendChild(googleDiv());
You are assigning a string as the function. You can use addEventListener.
Here is a JSFiddle to explain what I mean.
I have a problem in AngularJS, to test a html div with a dynamic value when I use controller's alias
Here my code
<div id="title_contract" class="caption">{{ ctrl.detail_title }}</div>
where crtl is the ContractController's alias.
My test is
describe('Testing create new or open detail contract', function() {
var template, ctrl, scope;
beforeEach(inject(function ($controller, $compile) {
scope = $rootScope.$new();
ctrl = $controller('ContractController', {$scope: scope});
var element = angular.element('<div id="title_contract" class="caption">{{ ctrl.detail_title }}</div>');
template = $compile(element)(scope);
}));
it('should prepare variable to create new contract', function () {
ctrl.create_clicked();
scope.$digest();
var templateAsHtml = template.html();
expect(templateAsHtml).toContain('New Contract');
});
}
MyController is
PageApp.controller('ContractController', function($rootScope, $scope ) {
var vm = this;
vm.create_clicked = doCreate;
vm.title_detail = '';
function doCreate() {
vm.detail_title = 'New Contract';
}});
When I call create_clicked the title in vm change its value but test fails 'cos the div value is empty.
I try to use $scope (so without alias) and it works.
But I'd like to use alias approach.
Did somebody encounter this problem?
Thanks' in advance
Try:
ctrl = $controller('ContractController as ctrl', {$scope: scope});
See $controller documentation:
The string can use the controller as property syntax
I'm using the 'Compilation.xml' template from the TVMLCatalog
I'd like to add a button click event to a 'listItemLockup'
<listItemLockup>
<ordinal minLength="2" class="ordinalLayout">0</ordinal>
<title>Intro</title>
<subtitle>00</subtitle>
<decorationLabel>(3:42)</decorationLabel>
</listItemLockup>
I've tried adding:
App.onLaunch = function(options) {
var templateURL = 'http://localhost:8000/hello.tvml';
var doc = getDocument(templateURL);
//doc.addEventListener("select", function() { alert("CLICK!") }, false);
var listItemLockupElement = doc.getElementsByTagName("listItemLockup");
listItemLockupElement.addEventListener("select", function() { alert("CLICK!") }, false);
}
addEventListener
void addEventListener (in String type, in Object listener, in optional Object extraInfo)
Is "select" the correct type?
I've been using the following tutorials
http://jamesonquave.com/blog/developing-tvos-apps-for-apple-tv-with-swift/
http://jamesonquave.com/blog/developing-tvos-apps-for-apple-tv-part-2/
Update
I'm getting an error
ITML <Error>: doc.getElementsByTagName is not a function. (In 'doc.getElementsByTagName("listItemLockup")', 'doc.getElementsByTagName' is undefined) - http://localhost:8000/main.js - line:27:58
I tried adding this to the 'onLaunch'
var listItemLockupElements = doc.getElementsByTagName("listItemLockup");
for (var i = 0; i < listItemLockupElements.length; i++) {
//var ele = listItemLockupElements[i].firstChild.nodeValue;
listItemLockupElements[i].addEventListener("select", function() { alert("CLICK!") }, false);
}
I'll see about the error first
Cross Post: https://forums.developer.apple.com/thread/17859
More common example I have seen by Apple is to define a single overall listener like:
doc.addEventListener("select", Presenter.load.bind(Presenter));
In your xml, assign unique ids to elements, or give them ways to identify them.
For example, the beginning would be something like:
load: function(event) {
var self = this,
ele = event.target,
attr_id = ele.getAttribute("id"),
audioURL = ele.getAttribute("audioURL"),
videoURL = ele.getAttribute("videoURL")
And then you can do whatever you want with your item.
if(audioURL && (event.type === "select" || event.type === "play")) {
//
}
My advice would be to study the Presenter.js file more carefully for this pattern.
Edit:
Answering your "Update" related to doc.getElementsByTagName is not a function. "doc" does not actually exist, but the general pattern is to get it with
var doc = getActiveDocument();
I assumed you would know the above.
Does that fix it?
var listItemLockupElement = doc.getElementsByTagName("listItemLockup”);
In this case, the listItemLockupElement is a NodeList, not an element. You can either iterate through the list and add an event listener to each listItemLockup, or you could add the event listener to the containing element.
When addressing items in a NodeList, you use the item(i) method rather than the standard array access notation:
listItemLockupElements.item(i).addEventListener("select", function() { })
See: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/item
Adding event listeners is straightforward if you're using atvjs framework.
ATV.Page.create({
name: 'mypage',
template: your_template_function,
data: your_data,
events: {
select: 'onSelect',
},
// method invoked in the scope of the current object and
// 'this' will be bound to the object at runtime
// so you can easily access methods and properties and even modify them at runtime
onSelect: function(e) {
let element = e.target;
let elementType = element.nodeName.toLowerCase();
if (elementType === 'listitemlockup') {
this.doSomething();
}
},
doSomething: function() {
// some awesome action
}
});
ATV.Navigation.navigate('mypage');
Disclaimer: I am the creator and maintainer of atvjs and as of writing this answer, it is the only JavaScript framework available for Apple TV development using TVML and TVJS. Hence I could provide references only from this framework. The answer should not be mistaken as a biased opinion.