MDC-Web: Multiple dialogs - javascript

I use google MATERIAL COMPONENTS FOR THE WEB and have problems with the "Dialogs".
Check my codepen: Dialog
What do I have to do to have multiple dialogs per page?
JS:
// Find all the dialogs on the page
const dialogEls = Array.from(document.querySelectorAll('.mdc-dialog'));
dialogEls.forEach((ele) => {
const dialog = new mdc.dialog.MDCDialog(ele);
dialog.listen('MDCDialog:accept', function() {
console.log('accepted');
})
dialog.listen('MDCDialog:cancel', function() {
console.log('canceled');
})
// From here I do not know how to continue....
// Here the selector '#dialog-login' should still be dynamic
document.querySelector('#dialog-login').addEventListener('click', function (evt) {
event.preventDefault(evt);
dialog.lastFocusedTarget = evt.target;
// This shows all dialogs, which is wrong.
dialog.show();
})
});

I have updated answer from #Jodo.
I suggest for dynamic approach using data attribute on dialog tags with value of opening button.
// Find all the dialogs on the page
const dialogEls = Array.from(document.querySelectorAll('.mdc-dialog'));
dialogEls.forEach((ele) => {
const dialog = new mdc.dialog.MDCDialog(ele);
dialog.listen('MDCDialog:accept', function() {
console.log('accepted');
})
dialog.listen('MDCDialog:cancel', function() {
console.log('canceled');
})
document.querySelector('#' + ele.dataset.dialog).addEventListener('click', function (evt) {
dialog.show();
});
});
https://codepen.io/woytam/pen/abvdZBQ?editors=1010
Simply add data-dialog attribute to each dialog with value of your button/link. JavaScript function then will use ele.dataset.dialog in foreach.

The Dialog opens twice because you create two event listeners for #dialog-login.
One of them opens the Login Dialog the other one opens the Delivery Dialog.
Since you have two different distinct Dialogs, I would suggest a more static way and declare both dialogs independently:
const dialogLoginEle = document.getElementById('mdc-dialog-login');
const dialogLogin = new mdc.dialog.MDCDialog(dialogLoginEle);
dialogLogin.listen('MDCDialog:accept', function() {
console.log('accepted login');
});
dialogLogin.listen('MDCDialog:cancel', function() {
console.log('canceled login');
});
const dialogDeliveryEle = document.getElementById('mdc-dialog-delivery-condition');
const dialogDelivery = new mdc.dialog.MDCDialog(dialogDeliveryEle);
dialogDelivery.listen('MDCDialog:accept', function() {
console.log('accepted delivery');
});
dialogDelivery.listen('MDCDialog:cancel', function() {
console.log('canceled delivery');
});
document.querySelector('#dialog-login').addEventListener('click', function (evt) {
dialogLogin.show();
});
document.querySelector('#dialog-delivery').addEventListener('click', function (evt) {
dialogDelivery.show();
});
https://codepen.io/j-o-d-o/pen/XZqNYy?editors=1010
Here a "dynamic" approach as requested, but IMHO this is not very readable and error prone.
// Find all the dialogs on the page
const dialogEls = Array.from(document.querySelectorAll('.mdc-dialog'));
// First one is the Login, Second one is the Delivery
var dialogArr = [];
dialogEls.forEach((ele) => {
const dialog = new mdc.dialog.MDCDialog(ele);
dialog.listen('MDCDialog:accept', function() {
console.log('accepted');
})
dialog.listen('MDCDialog:cancel', function() {
console.log('canceled');
})
dialogArr.push(dialog);
});
document.querySelector('#dialog-login').addEventListener('click', function (evt) {
dialogArr[0].show();
});
document.querySelector('#dialog-delivery').addEventListener('click', function (evt) {
dialogArr[1].show();
});
https://codepen.io/j-o-d-o/pen/jZxmxa?editors=1010

Related

How to attach click events to iframe content?

I have a scenario where I wanted to load a page on inital load. I found that this would do the trick:
<main id="mainContent">
<iframe id="InitalIframe" src="./Pages/Start/index.html" onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"></iframe>
</main>
I have some links in my header which I attach click listners to:
(function() {
document.querySelectorAll(".link").forEach((item) => {
item.addEventListener("click", (event) => {
event.preventDefault();
const url = event.target.dataset["url"];
getHtmlFile(`./Pages/${url}/`, (data) => {
document.getElementById("mainContent").innerHTML = data;
executeScripts();
});
return false;
});
});
})();
This worked untill I added a few links inside of the Start/Index.html file which gets renderd via the iframe.
I have these two buttons inside of that html.
<button type="button" class="link refbtn" data-Url="One">
One
</button>
<button type="button" class="link refbtn" data-Url="Two">
Two
</button>
Since I attached my listners before the iframe has loaded they never get picked up.
But when I waited for the iframe to load:
document.getElementById("InitalIframe").onload = function() {
document.querySelectorAll(".link").forEach((item) => {
item.addEventListener("click", (event) => {
event.preventDefault();
const url = event.target.dataset["url"];
getHtmlFile(`./Pages/${url}/`, (data) => {
document.getElementById("mainContent").innerHTML = data;
executeScripts();
});
return false;
});
});
};
the click events did not get attached and I got a weird looking result on the page.
Question is how do I accomplish this?
For anyone struggling with the same:
I made my life easier by listening for document click events. when I found that an element with a certain class was clicked I triggered desierd functions:
document.addEventListener(
"click",
function(event) {
event.preventDefault();
const classes = event.target.classList;
if (classes.contains("link")) {
const url = event.target.dataset["url"];
app.fetcHtml(`./Pages/${url}/`, (data) => {
document.getElementById("mainContent").innerHTML = data;
});
}
return false;
},
false
);
You can get to work this with a different approach also. In other words, I had the method closest() in pure Javascript finding me the closest event target that is trigerred when I click inside the container. It will always get me the nearest <a> element which I clicked when I had wandered throught the clickable div/container area.
let base; // the container for the variable content
let dataSet;
base = document.getElementById(base.id); // target your Iframe in this case.
base.addEventListener('click', function (event) {
let selector = '.link'; // any css selector for children
// find the closest parent of the event target that
// matches the selector
let closest = event.target.closest(selector);
if (closest !== undefined && base.contains(closest)) {
dataSet= event.target.dataset["url"];
app.fetcHtml(`./Pages/${url}/`, (data) => {
document.getElementById("mainContent").innerHTML = data;
});
}
});
You can try and see if it works like this also.
Cheers

How to use event delegation to execute a function based on clicked element's id?

I've worked with event delegation in the past but for some reason I'm having trouble setting up a single event listener that executes one of three functions depending on the ID of the element clicked.
Here's the code without event delegation:
eventListeners: function() {
document.getElementById("button-1").addEventListener('click',
function() {
shuffler.reset1();
shuffler.displayCards();
});
document.getElementById("button-2").addEventListener('click', function() {
shuffler.reset2();
shuffler.displayCards();
});
document.getElementById("button-3").addEventListener('click',
function() {
shuffler.reset3();
shuffler.displayCards();
});
I've tried using something along the lines of:
document.getElementsByClass("button").addEventListener('click', function
() {
if (event.target.id == "button-1") {
shuffler.reset1();
}
});
Attach the listener to the container that contains all buttons. Then, I'd use an object indexed by id, and check if the id of the element that was clicked exists in the object - if so, run the function:
const fns = {
'button-1': () => {
shuffler.reset1();
shuffler.displayCards();
},
// ...
}
document.querySelector('< something that contains all buttons >').addEventListener('click', ({ target }) => {
const { id } = target;
if (fns[id]) {
fns[id]();
}
});
Note that in this particular case, you can use just one function by checking the last number in the ID:
document.querySelector('< something that contains all buttons >').addEventListener('click', ({ target }) => {
const { id } = target;
if (id.startsWith('button-')) {
const buttonNum = id.match(/\d+/)[0];
shuffler['reset' + buttonNum]();
shuffler.displayCards();
}
});

onClick function active by default

I find it very complex to create a separate menu for mobile/small screen devices, so I decided to go with the main one, however my goal is to display all the links by default (when loading the page), actually the user needs to click on a small icon to display the dropdowdown links, it's very popular practise and most users are comfortable with that however my client insist on showing all links without clicking on anything.
I managed to hide that icon but I'm stuck because I cannot display those links anymore, is there a way to activate that onClick function by default? Or maybe disable dropdown secondary list on mobile?
The code below is navigation:
NavigationView.prototype.initialize = function() {
this.$el.on('click', (function(_this) {
return function(e) {
if (!$(e.target).closest('.navigation').length) {
return _this.$('.navigation .open').removeClass('open');
}
};
})(this));
this.$el.on('focus', '.header-navigation-link.primary-link', (function(_this) {
return function() {
var $menuWrapper;
$menuWrapper = $(_this.$el.find('.has-dropdown.open'));
if ($menuWrapper.length) {
return $menuWrapper.toggleClass('open', false);
}
};
})(this));
return this.$el.on('focus', '[data-is-dropdown] .secondary-link', (function(_this) {
return function(event) {
var $target;
$target = $(event.currentTarget);
return $target.parents('.has-dropdown').toggleClass('open', true);
};
})(this));
};
NavigationView.prototype.toggleNavigation = function(e) {
var $target;
$target = $(e.target);
if ($target.parents().hasClass('has-dropdown')) {
e.preventDefault();
return $target.parent().toggleClass('open');
}
};
return NavigationView;
What I'm trying to change is that ToggleClass; active by default so don't need to click on icon to show secondary-list
I believe this is the syntax you are looking for:
this.$el.on('click', myFunction); //runs myFunction onclick
function myFunction() {
//display dropdown code here
}
myFunction(); //runs myFunction onload

How to catch ngToast with Protractor?

I need somehow to catch ngToast message after some action. I tried different solutions from this site, but they didn't help me and I don't know why. Does anyone has a working solution ? My last attempt was like:
var HomePage = require('./home.pageObject');
describe('Home Page tests', function () {
var homePage = new HomePage();
var EC = protractor.ExpectedConditions;
beforeEach(function () {
browser.get('/#/');
});
it('should fail login', function () {
var toast = $('.ng-toast');
homePage.signinBtn.click();
homePage.login('admin', 'Password')
.then(function () {
var toast = $('.alert');
browser.wait(EC.visibilityOf(toast), 3000);
});
});
});
Thanks.
Inspect the toast element once it is shown and then try to grab the message using css.
In one of our projects where we use angular-toastr, this does the trick:
element(by.css('#toast-container .toast-message')
You can use this function and call it in the test:
First I'm waiting presence in the dom, then visibility and then I return the text:
this.returnToastText= function(){
browser.wait(EC.presenceOf(element(by.css(".alert"))), 3000).then(function () {
browser.wait(EC.visibilityOf(element(by.css(".alert"))), 3000).then(function () {
return toastText= element(by.css(".alert")).getText();
})
})
};

event for file upload cancel in javascript

We can use file.onchange if we gonna set an event callback for file reading using javascript, but how to set event for when user cancel the upload (close the browse panel)?
There is no API for the file input modal. Besides, if the user closes the browser your code won't be running anymore, will it?
Of course there is the window.onunload method which allows you to detect the example you give.
Per the comments, the best thing I can come up with that would be helpful is that if nothing is selected, file.value.length will be 0.
That’s a great solution:
const createUpload = () => {
let lock = false
return new Promise((resolve, reject) => {
const el = document.createElement('input');
el.id = +new Date();
el.style.display = 'none';
el.setAttribute('type', 'file');
document.body.appendChild(el)
el.addEventListener('change', () => {
lock = true;
const file = el.files[0];
resolve(file)
document.body.removeChild(document.getElementById(el.id));
}, { once: true })
window.addEventListener('focus', () => { // file blur
setTimeout(() => {
if (!lock && document.getElementById(el.id)) {
reject(new Error('onblur'))
document.body.removeChild(document.getElementById(el.id))
}
}, 300)
}, { once: true })
el.click() // open file select box
})
}
Ussage:
// $label.classList.add('loading');
createUpload()
.then(function (file) {
// Sent file
// $label.classList.remove('loading');
})
.catch(function (err) {
// Your Cancel Event
// $label.classList.remove('loading');
});
It is very simple with jQuery :
$("#fileInputId").change(function () {
//implement your code here
});

Categories

Resources