Refresh core-ajax periodically using setTimeout and go(); - ajax

I have a core-ajax component that is working fine (I see all the expected data):
<template is="auto-binding">
<core-ajax id="ds" auto url="https://server/data.json" response="{{data}}"></core-ajax>
<my-alarms alarms="{{data}}"></mu-alarms>
</template>
I'm attempting to refresh it periodically just for a proof of concept demo using the following javascript:
function f() {
$('#ds').go();
}
setTimeout(f, 1000)
This results in the following error on the line with go()
Uncaught TypeError: undefined is not a function
Here is the documentation. What am I doing wrong?

that function looks like jquery. try this.
function f() {
document.querySelector("#ds").go();
}
setTimeout(f, 1000);
not real sure about using jquery with polymer. i think it can't find elements in shadowdom like using document.querySelector can.

$ is a Map not a function in Polymer. It available only in Polymer elements and you can use it like
function f() {
$.ds.go();
}
setTimeout(f, 1000)
to access elements inside the elements shadowDOM.

Related

How do I locate dom elements within nested shadow doms using cypress when `find(...).shadow` is not a function`

I am working with Cypress v9.6.1 and custom elements with shadow dom. We have several custom elements which are deeply nested - I am able to query for an input that is deeply nested with several web components successfully with something like this:
// WORKS!
cy.get(‘custom-element-1’)
.shadow()
.find('custom-element-2’)
.shadow()
.find('custom-element-3’)
.shadow()
.find(`custom-element-4[id=“ce”4]`)
.shadow()
.find('input');
What I'd like to do is determine the presence of that input before moving forward in the test.
Pseudo code -
if (cy.('.deeply-nested-element').exists() ){
do.not.click(button-a)
} else {
click(button-a)
}
Following the cypress docs for conditional testing -> element presence I attempted to express the above in a conditional and use on the length attribute to determine presence
//DOES NOT WORK
cy.get(‘custom-element-1').then(($ce) => {
if($ce.shadow()
.find('custom-element-2’)
.shadow()
.find('custom-element-3’)
.shadow()
.find(`custom-element-4[id=“ce”4]`)
.shadow()
.find('input')) // then do something
}
This gave me an error. Property 'shadow' does not exist on type 'JQuery<HTMLElement>'. Did you mean 'show'?ts(2551)
Ignoring the ts error resulted in cypress test failing with:
$ce.find(...).shadow is not a function
I switched it up a little by chaining off of shadow() with the same result
//ALSO DOES NOT WORK
cy.get(‘custom-element-1').shadow().then(($ceshadow) => {
$ce
.find('custom-element-2’)
.shadow()
.find('custom-element-3’)
.shadow()
.find(`custom-element-4[id=“ce”4]`)
.shadow()
.find('input');
}
And for this one:
$ce.find(...).shadow is not a function
It looks to me like the promise off of the get method does not pass to the callback an element with a shadow dom (JQuery). The small problem I'm trying to figure out is a workaround to that. The larger problem is how to set up a conditional that is determined by the presence of an element that is deeply nested within custom element shadow doms. Any advice would be much appreciated.
The .shadow() command is a Cypress command, but $ce is a jQuery object that can't call Cypress commands directly.
$ce.find(...).shadow is not a function occurs because both jQuery and Cypress have .find(), but only Cypress has .shadow().
By making includeShadowDom:true global either in config or the test options, Cypress commands can be used without needing to chain .shadow() at every step.
If it's custom-element-1 that is conditional, this should work
it('tests deeply-nested shadow elements', {includeShadowDom:true}, () => {
cy.get('body').then($body => {
const ce1 = $body.find('custom-element-1');
if (ce1.length) {
cy.wrap(ce1) // wrap jQuery element into Cypress result
// so that includeShadowDom:true is effective
.find('custom-element-2')
.find('custom-element-3')
.find('custom-element-4[id="ce4"]')
.find('input'))
}
})
})

Find element inside Iframe fails in cypress

I am trying to click an element inside frame. When i use cy.wrap i get Error
" Timed out retrying: Expected to find element: '[name="border_top"].contents() body #acceptInvitation', but never found it.
Code
it('Automate C# Corner Page', function() {
cy.get('[name="border_top"]').then(function ($iframe) {
const doc = $iframe.contents().find('body')
// doc.find('#acceptInvitation').click({force:true})
cy.wrap(doc.find('#acceptInvitation')).click({force:true})
})
After you add a helper function to your cypress/support/commands.js as I describe in my answer here (Cypress - run test in iframe), you can evaluate whether the button exists with:
cy.get('#iframe-id')
.iframe('body #yourButtonId')
.should('exist')
I think clicking the element in the iframe could be done like this.
cy.get('#iframe-id')
.iframe('body #yourButtonId')
.click({force:true})
install cypress-iframe()
import cypress-iframe in the file
cy.iframe().find('selector');
you can declare one method into base js file like shown below:
getIframeBody() {
return cy
.get('object')
.its('0.contentDocument').
its('body')
.then(cy.wrap)
};
Then use this method as:
getIframeBody().find("cssLocater).click(); OR getIframeBody().xpath("xpath).click();
This will work for sure.

How to use d3 v4 with Polymer 2.0 classes?

How can d3.js (v4) be used with a Polymer 2.0 element?
Or how to use a library inside a class that already extends another class?
Trying to create a polymer d3 element to take advantage of polymer's two-way data binding and d3's syntax and functions. So that the data can be bound to a polymer property and passed to the d3.data() function?
Currently declaring d3 in the class returns undefined. Does the class need to be instantiated with d3 as a parameter? It seemed to work with Polymer 1.0. Another approach was to create a function outside of the class and call that but it's ugly. It would be nice to just use d3 inside the class.
Or is there a cleaner better way?
eg.
<script src="../../bower_components/d3/d3.js"></script>
var d3 = d3;
debugger; // hits this breakpoint first, and d3 is defined here and below
<dom-module id="my-app">
<template>
<svg id="svg"></svg>
</template>
<script>
class MyApp extends Polymer.Element {
static get is() { return 'my-app'; }
static get properties() {
return {
data: {
type: Object,
observer: '_dataChanged'
}
}
}
ready: {
var d3 = d3; // when it breaks here on the above breakpoint this is defined
debugger; // when it hits this breakpoint 2nd, d3 is undefined here and outside the class; what happened? how to scope it in?
}
_dataChanged(newValue, oldValue): {
var circle = d3.select(this.$.svg).data(newValue).enter().append(circle); //d3 undefined(! how to define?)
}
}
window.customElements.define(MyApp.is, MyApp);
</script>
</dom-module>
window.d3 is the way as you're loading the script in the global scope. You can load any external script asynchronously or synchronously.
To load it synchronously, just place the <script> tag in the <head>.
To load it asynchronously, you can add an load event listener to the <script> tag to do subsequent stuff when the load is completed.

Sinon JQuery selectors mock

I am trying to test a function using mocha/sinonjs. The function I want to test is responsible for showing or hiding some element in my DOM.
This is my function
var updateUI = function() {
$('#login').show();
$('#logout').hide();
};
I tried to mock using sinon but I'm not sure if it is possible or the correct thing to do in this case.
This is what I have tried but I keep getting an error "TypeError: undefined is not a function" during the expect call
var mockLogin = sinon.mock($);
mockLogin.withArgs('#login').expects("show").once();
I simple want to test my 2 jquery calls have been called. I tried to use spies but continue to get exceptions
Looking further into the sinon doc I found that the following worked for me.
var jQueryShow = sinon.stub($.fn, 'show');
var jQueryHide = sinon.stub($.fn, 'hide');
jQueryShow.callCount.should.be.equal(1);
jQueryShow.thisValues[0].selector.should.be.equal("#login");
jQueryHide.callCount.should.be.equal(1);
jQueryHide.thisValues[0].selector.should.be.equal("#logout");
I'm not sure if there an easier way but it checks for the selectors I need

Calling a function when ng-repeat has finished

What I am trying to implement is basically a "on ng repeat finished rendering" handler. I am able to detect when it is done but I can't figure out how to trigger a function from it.
Check the fiddle:http://jsfiddle.net/paulocoelho/BsMqq/3/
JS
var module = angular.module('testApp', [])
.directive('onFinishRender', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
element.ready(function () {
console.log("calling:"+attr.onFinishRender);
// CALL TEST HERE!
});
}
}
}
});
function myC($scope) {
$scope.ta = [1, 2, 3, 4, 5, 6];
function test() {
console.log("test executed");
}
}
HTML
<div ng-app="testApp" ng-controller="myC">
<p ng-repeat="t in ta" on-finish-render="test()">{{t}}</p>
</div>
Answer:
Working fiddle from finishingmove: http://jsfiddle.net/paulocoelho/BsMqq/4/
var module = angular.module('testApp', [])
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit(attr.onFinishRender);
});
}
}
}
});
Notice that I didn't use .ready() but rather wrapped it in a $timeout. $timeout makes sure it's executed when the ng-repeated elements have REALLY finished rendering (because the $timeout will execute at the end of the current digest cycle -- and it will also call $apply internally, unlike setTimeout). So after the ng-repeat has finished, we use $emit to emit an event to outer scopes (sibling and parent scopes).
And then in your controller, you can catch it with $on:
$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
//you also get the actual event object
//do stuff, execute functions -- whatever...
});
With html that looks something like this:
<div ng-repeat="item in items" on-finish-render="ngRepeatFinished">
<div>{{item.name}}}<div>
</div>
Use $evalAsync if you want your callback (i.e., test()) to be executed after the DOM is constructed, but before the browser renders. This will prevent flicker -- ref.
if (scope.$last) {
scope.$evalAsync(attr.onFinishRender);
}
Fiddle.
If you really want to call your callback after rendering, use $timeout:
if (scope.$last) {
$timeout(function() {
scope.$eval(attr.onFinishRender);
});
}
I prefer $eval instead of an event. With an event, we need to know the name of the event and add code to our controller for that event. With $eval, there is less coupling between the controller and the directive.
The answers that have been given so far will only work the first time that the ng-repeat gets rendered, but if you have a dynamic ng-repeat, meaning that you are going to be adding/deleting/filtering items, and you need to be notified every time that the ng-repeat gets rendered, those solutions won't work for you.
So, if you need to be notified EVERY TIME that the ng-repeat gets re-rendered and not just the first time, I've found a way to do that, it's quite 'hacky', but it will work fine if you know what you are doing. Use this $filter in your ng-repeat before you use any other $filter:
.filter('ngRepeatFinish', function($timeout){
return function(data){
var me = this;
var flagProperty = '__finishedRendering__';
if(!data[flagProperty]){
Object.defineProperty(
data,
flagProperty,
{enumerable:false, configurable:true, writable: false, value:{}});
$timeout(function(){
delete data[flagProperty];
me.$emit('ngRepeatFinished');
},0,false);
}
return data;
};
})
This will $emit an event called ngRepeatFinished every time that the ng-repeat gets rendered.
How to use it:
<li ng-repeat="item in (items|ngRepeatFinish) | filter:{name:namedFiltered}" >
The ngRepeatFinish filter needs to be applied directly to an Array or an Object defined in your $scope, you can apply other filters after.
How NOT to use it:
<li ng-repeat="item in (items | filter:{name:namedFiltered}) | ngRepeatFinish" >
Do not apply other filters first and then apply the ngRepeatFinish filter.
When should I use this?
If you want to apply certain css styles into the DOM after the list has finished rendering, because you need to have into account the new dimensions of the DOM elements that have been re-rendered by the ng-repeat. (BTW: those kind of operations should be done inside a directive)
What NOT TO DO in the function that handles the ngRepeatFinished event:
Do not perform a $scope.$apply in that function or you will put Angular in an endless loop that Angular won't be able to detect.
Do not use it for making changes in the $scope properties, because those changes won't be reflected in your view until the next $digest loop, and since you can't perform an $scope.$apply they won't be of any use.
"But filters are not meant to be used like that!!"
No, they are not, this is a hack, if you don't like it don't use it. If you know a better way to accomplish the same thing please let me know it.
Summarizing
This is a hack, and using it in the wrong way is dangerous, use it only for applying styles after the ng-repeat has finished rendering and you shouldn't have any issues.
If you need to call different functions for different ng-repeats on the same controller you can try something like this:
The directive:
var module = angular.module('testApp', [])
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit(attr.broadcasteventname ? attr.broadcasteventname : 'ngRepeatFinished');
});
}
}
}
});
In your controller, catch events with $on:
$scope.$on('ngRepeatBroadcast1', function(ngRepeatFinishedEvent) {
// Do something
});
$scope.$on('ngRepeatBroadcast2', function(ngRepeatFinishedEvent) {
// Do something
});
In your template with multiple ng-repeat
<div ng-repeat="item in collection1" on-finish-render broadcasteventname="ngRepeatBroadcast1">
<div>{{item.name}}}<div>
</div>
<div ng-repeat="item in collection2" on-finish-render broadcasteventname="ngRepeatBroadcast2">
<div>{{item.name}}}<div>
</div>
The other solutions will work fine on initial page load, but calling $timeout from the controller is the only way to ensure that your function is called when the model changes. Here is a working fiddle that uses $timeout. For your example it would be:
.controller('myC', function ($scope, $timeout) {
$scope.$watch("ta", function (newValue, oldValue) {
$timeout(function () {
test();
});
});
ngRepeat will only evaluate a directive when the row content is new, so if you remove items from your list, onFinishRender will not fire. For example, try entering filter values in these fiddles emit.
If you’re not averse to using double-dollar scope props and you’re writing a directive whose only content is a repeat, there is a pretty simple solution (assuming you only care about the initial render). In the link function:
const dereg = scope.$watch('$$childTail.$last', last => {
if (last) {
dereg();
// do yr stuff -- you may still need a $timeout here
}
});
This is useful for cases where you have a directive that needs to do DOM manip based on the widths or heights of the members of a rendered list (which I think is the most likely reason one would ask this question), but it’s not as generic as the other solutions that have been proposed.
I'm very surprised not to see the most simple solution among the answers to this question.
What you want to do is add an ngInit directive on your repeated element (the element with the ngRepeat directive) checking for $last (a special variable set in scope by ngRepeat which indicates that the repeated element is the last in the list). If $last is true, we're rendering the last element and we can call the function we want.
ng-init="$last && test()"
The complete code for your HTML markup would be:
<div ng-app="testApp" ng-controller="myC">
<p ng-repeat="t in ta" ng-init="$last && test()">{{t}}</p>
</div>
You don't need any extra JS code in your app besides the scope function you want to call (in this case, test) since ngInit is provided by Angular.js. Just make sure to have your test function in the scope so that it can be accessed from the template:
$scope.test = function test() {
console.log("test executed");
}
A solution for this problem with a filtered ngRepeat could have been with Mutation events, but they are deprecated (without immediate replacement).
Then I thought of another easy one:
app.directive('filtered',function($timeout) {
return {
restrict: 'A',link: function (scope,element,attr) {
var elm = element[0]
,nodePrototype = Node.prototype
,timeout
,slice = Array.prototype.slice
;
elm.insertBefore = alt.bind(null,nodePrototype.insertBefore);
elm.removeChild = alt.bind(null,nodePrototype.removeChild);
function alt(fn){
fn.apply(elm,slice.call(arguments,1));
timeout&&$timeout.cancel(timeout);
timeout = $timeout(altDone);
}
function altDone(){
timeout = null;
console.log('Filtered! ...fire an event or something');
}
}
};
});
This hooks into the Node.prototype methods of the parent element with a one-tick $timeout to watch for successive modifications.
It works mostly correct but I did get some cases where the altDone would be called twice.
Again... add this directive to the parent of the ngRepeat.
Very easy, this is how I did it.
.directive('blockOnRender', function ($blockUI) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
if (scope.$first) {
$blockUI.blockElement($(element).parent());
}
if (scope.$last) {
$blockUI.unblockElement($(element).parent());
}
}
};
})
Please have a look at the fiddle, http://jsfiddle.net/yNXS2/. Since the directive you created didn't created a new scope i continued in the way.
$scope.test = function(){... made that happen.

Resources