I am still fairly new to angularjs and am currently porting a existing spa javascript application to the angularjs framework. I am using the ui-bootstrap directive for creating bootstrap modals. I am trying to re-position the modal in the middle of the screen once the modal is displayed on the screen. With jQuery that was a easy task, just grab the modal element after calling:
$('#modal').modal('show')
But I can't seem to figure out the correct way to do this using angular. My first thought was to catch bootstrap events. But it turns out those are not fired according to
Here
and
Here
I tried creating a custom directive, but in the link function the element was defined but was not visible on the screen yet. I need a way to trigger some resize code once the modal is visible.
I also tried accessing the $modalStack.getTop(), but I end up having the same issue. There is no way to know when $modalStack.getTop().value.modalDomEl is actually resolved and showing on the screen.
I also tried the following, but the opened promise is resolved before the modal is actually showing on the screen.
modalInstance.opened.then(function(t){
//$modalStack.getTop()
});
How do you accomplish this using angular?
So I ended up taking what #user2943490 recommended and tweaked it a little bit for my use case.
I created a directive that I added to the Modal Template. Inside I check every 100ms to see if the modal-content class exists. If the class exists we know that the modal has bee added to the DOM. Once I perform my logic, I cancel the $interval check.
return {
link: function(scope, element, attrs){
var checkInterval = $interval(checkDOM, 100);
function checkDOM(){
var $content = element.find('.modal-content');
if($content.length){
//We know if $content.length > 0 Then the modal-content class exists on the screen.
$interval.cancel(checkInterval);
}
}
element.on('$destroy', function(){
$interval.cancel(checkInterval);
});
You should use CSS to position the modal as a first option. This avoids all issues around the timing of when specific elements get rendered and stays in line with Angular's spirit of using data binding rather than manual DOM manipulation to build your UI.
You won't find many things in the Angular world that give you an "element rendered" event or similar. It's just not the recommended way to do things.
If you absolutely cannot use CSS, then use a $timeout within the opened promise, and wait a short time (50ms is probably fine) before executing your repositioning logic.
modalInstance.opened.then(function(t){
$timeout(function () {
// reposition it
}, 50);
});
You should have used modal.rendered instead of modal.opened. No need for a link function or interval checks or anything else.
modalInstance.rendered.then(function () {
$('.modal-content').css({ 'width': $(document).width() - 100 + 'px' });
});
Related
Am playing around with this for a new site, and works great & easy to implement: http://nnattawat.github.io/slideReveal/
But one issue I can't determine, of you have multiple sliders, and one is already "out" from a previous event, how do you first make sure all other sliders are "closed" and THEN open the current slider?
I could do an if/then to check aach individual one, but I figure there must be a more elegant way...
I think the best way of solving this problem is to create a "page-level controller". What this does is acts as a container and controller of sorts for events that happen in the page.
So you essentially have to "proxy" sliding open an closed a side (and adding a dependency of closing the other slide area), since the API doesn't seem to have any hooks or events that it emits like "onSlideOpen". That's what your controller will handle.
var PageController = function(){
var $leftSlide = $('#leftSlider').slideReveal(), // You'll want to sub in
$rightSlide = $('#rightSlider').slideReveal(); // your selector names
return {
openLeft: function() {
$leftSlide.slideReveal('show');
$rightSlide.slideReveal('hide');
},
openRight: function() {
$rightSlide.slideReveal('show');
$leftSlide.slideReveal('hide');
}
};
};
var pageController = new PageController();
pageController.openLeft(); // will slide reveal left side
pageController.openRight(); // will simultaneously open right and close left
Now you can even do things like check on the state of those slides in case you have other cross-dependencies.
A lot of people would probably handle this kind of thing by emitting events and catching them, but I really don't like the pub-sub model. It tends to get very brittle very quickly.
Use .slideReveal("hide") on all of the sliders, then use .slideReveal("show") on the one you want revealed.
One way to do this, would be to give all the sliders a class and use that as the selector
i.e.
$(".slider").slideReveal("hide");
$("#sliderYouWantToShow").slideReveal("show");
I have a problem with nanoScroller. I am using Angularjs on the porject.I've a directive where I want to call the nanoScroller. It looks like this:
<a ng-click='show-me'>Show Me</a>
<div class='nano' ng-show='show-me' style='height:100px'>
<ol class='nano-content'>
<li ng-repeat='post in posts'>
{{post.title}}
</li>
</ol>
</div>
I need that .nano element has the scroller. When I press show-me, div opens which has a height of 100px. I also make a call of nanoScroller in this directive: angular.element(".nano").nanoScroller()
But scroll doesn't appear.
Maybe this is related to the fact that nano elements are not present on the page yet and nanoScroller is already called for it?
I tried to use nanoScroller directive but I get the following bug with it: when changing the height of the div with "This is item" content, scroll of the whole page moves up. This can be replicated by scrolling the page to the bottom and pressing Add item button several times.
Please help.
Thanks.
You're going to have to call .nanoScroller() when you know the element is visible. I recommend wrapping it in a timeout so it will update when the DOM is updated, by adding the following to your Angular Controller methods that handle changes to posts (i.e. getPosts(), addPost(), etc)..
// refresh nanoscroller
setTimeout(function() {
$(".nano").nanoScroller();
}, 250);
In your case, you'll also need to add it to your show-me method, unless show-me is a simple boolean, in which case you'll need to pop the setTimeout() into a $scope.$watch() block that watches for changes to show-me, something like this (again, in your Controller)..
$scope.$watch('show-me', function(newValue, oldValue) {
if (newValue === true) {
// refresh nanoscroller
setTimeout(function() {
$(".nano").nanoScroller();
}, 250);
}
});
Here's a Wrapper for nanoScrollerJS with AngularJS lifecycle integration.
https://github.com/maxaon/angular-nanoscroller
DEMO PLUNKER
I'm sure this sounds a little odd, but here's the background...
We utilize a company that loads their chat program, so we can support our customers, into our page. This is done via javascript and jquery, and creates a structure like this:
<div id="myid" style="...; right: 0px;..."><div><iframe></iframe></div></div>
There's a WHOLE lot more to that, but those are the important parts. Now the tool allows us to put custom scripting, which will be placed in the iframe. My goal is to just remove the "right: 0px", which I have done via the below code, but I don't want to put that code on every page that this tool integrates with. I would like to load it into the tool, and have it run when the iframe and divs are created.
working code on parent:
$(document).ready(function() {
function checkPos() {
$('#myId').each(function() {
var oldstyle = $('#myId').attr('style');
var newstyle = oldstyle.replace(' right: 0px;','');
$('#myId').attr('style', newstyle);
});
setTimeout(checkPos, 100);
};
$(document).ready(function() {
setTimeout(checkPos, 100);
});
});
Once placed in the code include method they provide, I have trouble having it wait until the div tag actually has the "right: 0px;" in its style tag. the only thing I need to run is the three lines in the $('#myId').each(function()
Basically, I need help with having the script in the iframe target the div that the iframe is nested in.
Assuming that whatever tool your using actually lets you pass in a custom script to the content rendered in the iframe (seems fishy to me), a better way of modifying the style in jquery is to use the css function:
$('#myId').css('right', '0px');
Notice I removed the $.each function as well. You are targeting an element with an id, so there isn't any need to iterate.
Edit:
Anyways, back to the problem of delaying execution to when the target, #myId, actually exists. If they are really injecting your javascript into their page (again, seems fishy), then attaching the above code to the $(document).ready() event should do the trick, as long as this listener is attached to their document.
If all else fails, try to use the waitUntilExists plugin, here:
Source:
https://gist.github.com/buu700/4200601
Relevant question:
How to wait until an element exists?
Old Question: How can I call a Polymer Function? (check edits, I don't want to cram code in here)
Rewrite:
I have a <core-scaffold> that I want to call the togglePanel() function. (This sits in project_root/index.html.) I do this using:
<core-icon-button onclick="document.querySelector('core-scaffold').togglePanel();"
icon="drawer></core-icon-button>
In Chrome's Inspector, I can see this causes no errors, but it doesn't do anything on-screen. My code calls this function in project_root/bower_components/core-scaffold/core-scaffold.html:
togglePanel: function() {
this.$.drawerPanel.togglePanel();
}
Which in turn calls this function in project_root/bower_components/core-drawer-panel/core-drawer-panel.html:
togglePanel: function() {
this.selected = this.selected === 'main' ? 'drawer' : 'main';
}
I am either to naive and unexperienced to see the problem, or have a terrible complex bug. Any help would be appreciated!
I ran into the problem as well. The issue is that the closeDrawer(), openDrawer(), and togglePanel() are only usable when size of your screen is less than the value of responsiveWidth.
I think the logic is that if you have the screen real estate you would always want to show the drawer. Of course this could be tweaked by extending core-drawer-panel and making a custom core-scaffold implementation.
You can directly fetch the element using query selector and call its method on onclick just like other html pages
<button onclick="document.querySelector('core-drawer-panel').togglePanel();">toggle drawer</button>
Change the published attribute in core-drawer-panel.html forceNarrow=true See # Call function on polymer navigation drawer panel
Background:
I'm making a portfolio site utilising both Swipe.js and Infinite Ajax Scroll (JQ).
Problem:
When the content from extra pages is loaded into the current page, it is not processed by the already-loaded Swipe.js script. This means that the new content doesn't have it's mark-up changed (needed for the swipe functionality to work).
I think I need to get the Swipe.js script to fire after each page re-load. Would that fix it? Please explain this to me like I'm an 8yr old. JS is not a strong suit...
Demo:
http://hatchcreative.co.nz/tomo
You can see that as the page loads new content, the buttons on either side of the sliders no longer work.
Yes you're right, after the images are loaded you have to create a new Swipe instance on these new elements (as they weren't there at the beginning, when the page was loaded).
Based on the docs of infinite scroll you can use onRenderComplete.
So you had your jQuery.ias constructor like this:
jQuery.ias({
// ... your settings...
onRenderComplete: function(items) {
$(items).each(function(index, element) {
new Swipe(element);
});
}
});
This should work this way somehow, but I am not exactly sure; I haven't worked with these libraries yet.
Edit:
After some more inspection of your code, I saw you had some inline click handler like: onclick='two.prev();return false;'.
You need to remove this and add your onclick handle in the same onRenderComplete function.
onRenderComplete: function(items) {
var swipe;
$(items).each(function(index, element) {
swipe = new Swipe(element);
});
// find tags with the class 'forward' inside the current element and add the handler
$(element).find('.forward').on('click', function() {
swipe.next();
});
// ... also for previous
}
By the way: Usually you should provide a jsFiddle with your important code parts, so it's easier for us to get the problem, and the question is not getting obsolote when the linked page changes.