Using Polymer 1.0, I am looking for the best approach to showing a login to a user when the app receives a 401 from the app services.
Using Angular I would be looking at using a httpInterceptor to do this, is there an equivalent in Polymer?
Here's an approach which explicitly routes errors from a service element (using iron-ajax)
<template is="dom-bind" id="app">
<values-service values="{{items}}" on-error="onError"></values-service>
<h1>Items <span>{{items}}</span></h1>
<template is="dom-repeat" items="{{items}}">
<p>{{item}}</p>
</template>
</template>
<script src="app.js"></script>
and my app script
(function (document) {
'use strict';
var app = document.querySelector('#app');
app.onError = function (e) {
console.log('app.onError ' + e.detail.request.status);
};
app.addEventListener('error', app.onError);
})(document);
this works, but as the login is something I want to happen without having to wire up elements specifically
To solve this, I created a component that was used for all my Ajax requests. In that component, listened for 401s from the service calls, and called this.fire('401-found') when found.
Then in my other components, I listened for such an event - in my case, in the main document, popping up a dialog asking the user to log in again.
A slightly better approach would be to have the Ajax component take in parameters to say 'yes fire a 401 event' and 'do not give it the standard 401 event, name call it this' then in each component you could listen for such events and react accordingly.
The answer is catching the error on an outermost element as the events bubble up through the dom
<div on-error="onError">
<values-service values="{{items}}"></values-service>
<other-service></other-service>
...
</div>
and some script (as above)
app.onError = function (e) {
console.log('app.onError ' + e.detail.request.status);
};
when any of the contained services, or any contained elements containing services fire an error, the handler will trigger - I do the relevant checks for a request and a 401 and show my login dialog
I recently made a PR to iron-ajax (which was merged a few days ago) that adds an optional bubbles attribute to accomplish exactly this. When the bubbles attribute is present, iron-ajax's request, response, and error events bubble to window. This means you can have global event listeners on window and handle 401s however you want without any more code duplication that adding bubbles to your iron-ajax calls.
What's more, because it's part of iron-ajax, it is officially supported by the Polymer team.
Here's the official docs.
Related
I'm using CodeceptJS and I'm trying to write a custom helper that asserts an text and clicks "OK". This dialog pops up as a iframe modal to consent with cookies.
If I write following steps in my scenario
I.amOnPage('/some-path');
within({frame: '#iframeID'}, () => {
I.see('Headline text for dialog');
I.click('OK');
});
// ...
...my test seems to work just fine.
But when I make an custom helper out of that and configure it properly so I can use it:
const { Helper } = codeceptjs;
class CookieConsent extends Helper {
consentWithCookies() {
const { Puppeteer } = this.helpers;
within({frame: '#iframeID'}, () => {
Puppeteer.see('Headline text for dialog');
Puppeteer.click('OK');
});
}
}
module.exports = CookieConsent;
...and use it as a step:
I.amOnPage('/some-path');
I.consentWithCookies();
// ...
...it doesn't seem to work as the consent dialog doesn't get clicked away as it was when implementing this directly in the scenario. According to some console.log() debugging the within callback doesn't get called at all. Console doesn't throw any errors about undefined within or anything suspicious.
I suspect that using within in a custom helper isn't working or I'm doing something wrong that I can't figure out from the documentation.
This warning at documentation doesn't really clarify when within is being used incorrectly, and using await doesn't help the problem.
within can cause problems when used incorrectly. If you see a weird behavior of a test try to refactor it to not use within. It is recommended to keep within for simplest cases when possible. Since within returns a Promise, it may be necessary to await the result even when you're not intending to use the return value.
iFrames can be a pain to work without when it comes down to automation. There are a number of factors that can make an iFrame unreachable to a framework such as cross-domain iFrames, commonly used for increased security on the content served.
Now to fix your issue, all you have to do is use switchTo() - Docs in CodeceptJS which is a function available for all helpers made available. The order should be
I.switchTo('your iframe');
..... some actions here;
I.switchTo(); // You do this so that you get out of the iFrame context when done
I'm creating a POC using google app maker. I plan on using a JS library that has a dependency on Jquery. I've listed JQuery as an "external resource" to start with and added an H1 element on my html with the following code as part of a client script:
$(document).ready(function(){
$("h1").click(function(){
console.log("jquery works");
});
});
When I preview my app and click on the element, nothing is logged. When I inspect the elements, I can see both the Jquery library and the code above, but the event is not triggering when I click on the element. Any suggestions? The ultimate goal is to be able to use https://querybuilder.js.org/ within the app I'm creating.
My best guess is that when you say that you added the code:
$(document).ready(function(){
$("h1").click(function(){
console.log("jquery works");
});
});
to the client script, what you did was created a client script under the SCRIPTS section of App Maker and then added the code there. If that is so, that is why it's not working.
What you need to do is to use client scripting in the widget event handlers. Each widget has event handlers and the HTML widget is not an exception. What I recommend is to add the code to the onAttach event handler of the HTML widget:
}
Also, you can get rid of the document.ready part and just use the code you see in the image above. That should do the trick.
BONUS: If you will be using classes and ids, for it to work you will need to use the allowUnsafeHtml option:
I hope this helps for now. If you need something else, I'll be happy to help you.
I've got a list of menu items and each is an a tag. Inside those, I've got notification bubbles insides a div. When the notification div is clicked, I'd like to first follow the a tag (the hash is always to a Meteor route, using Iron Router) before it executes the click event attached to the div. Since reverse-propagation hasn't existed since the netscape days, I thought maybe that click event could store a function as a callback when the route changes? Has anyone tried to do something similar? Couldn't find anything in the Iron Router docs about it. I'm currently mitigating the problem with a few Session vars, but would like to clean it up.
Without seeing what your code is currently doing (or even better, a simplified example), I am guessing a bit at what you are trying to do. Maybe you have something like this:
<div id="bubblething">Click Me!</div>
With a click event:
'click #bubblething': function() {
// Do the bubble thing.
}
But the problem is you'd like to trigger the click event after you route but the click is happening to early. Would it be possible to change it to something more like this:
<div id="bubblething">Click Me!</div> //no <a> tag.
With the click event handling the actually routing first then moving on:
'click #bubblething': function(){
Router.go('/yourroute'); // First you route
{ ... } // Code to handle the notification bubble.
}
You may have to update your styling a bit do to the lack of am <a> tag but that should be pretty simple. The routing is handled by the click and then other things happen. You can parameterize this so that instead of calling an id you call a class and inject the route.
I've got this in my routes:
Router.route('/videos/:id', {
name: 'VideoPage',
data: function(){
return Videos.findOne(this.params.id);
}
})
This template shows up at the route above:
Template.VideoPage.helpers({
'videoIds': function(){
var myVideoIds = [
"23456",
"h5e45t",
"f4e4w",
"g6h4h"
];
return myVideoIds;
}
});
HTML:
<template name="VideoPage">
{{videoTitle}}
<p>Click the links below to get to a new video page</p>
{{#each videoIds}}
<a href="/videos/" + {{this}}>
{{/each}}
</template>
When I click on a link, the URL in the browser changes from something like /videos/23456 to something like /videos/f4e4w, but the request never actually goes through Iron Router and the Router.route function. I put a debugger before Router.route and it triggers on initial page load but does NOT trigger when the links are clicked.
I understand that Iron Router's default behavior is NOT to re-render a template the user is currently on, but this is the same template with different url params that are used to change the data in the template, so IMO it should still re-render.
Ok, false alarm. It appears that Router.route DOES fire and updates the data context every time the params._id changes. I was placing the debugger outside of the Route function when I should have been placing it inside of the data context function.
The example that I gave in this answer was also a highy, highly simplified example. It wasn't working in my real-life project due to something else (a complex video iframe generator) that was being refreshed improperly.
But rest assured that going from /videos/f4e4w to /videos/23456 by clicking a link DOES still go through Iron Router and the params does get read and the data does get updated.
Currently meteor supports a limited number of events that we can react to from our template definitions. I would like a way to react to events beyond this predefined list. I want the freedom to add any event, even custom events, to the list of possible events in a template.
One idea I had would be to set up a jquery event handler somewhere that listens for the unsupported event and have it set a session variable:
$(form).submit( ->
Session.set('formSubmitted', true)
And then use that session variable when rendering a template:
Template.confirmation.submitted = ->
return Session.get('formSubmitted')
<template name="confirmation">
{{#if submitted}}
<!-- do whatever -->
{{/if}}
</template>
But this is just a workaround and doesn't really address the issue. Is there a real Meteor-way of doing this? Is this something I can do with the new Spark implementations?
NOTE: Please ignore the fact that I'm using the submit event here. I know I can just bind a click event to the submit button, but that's beside the point.
NOTE 2: The accepted answer to this question is also just a workaround.
The rendered callback is what I use to do this.
http://docs.meteor.com/#template_rendered
The callback gives you template instance you should use to find the dom elements you need: http://docs.meteor.com/#template_inst
Untested example below ;)
Template.foo.rendered = ->
$(this.find("form")).submit ->
Session.set 'formSubmitted', true
Using a Session variable than to switch the view is a matter of taste I think.
I have an app State stored in the Session, that toggles Templates. Additionally the backbone package is very useful to provide some meaningful urls.