I added fullpage.js to my project and my css transitions stopped working.
I analyzed the code using code inspector to see, if the classes have been removed or if the transitions have been overwritten but I can't find something. It looks like I don't have naming issues with my classes, as well.
My transitions are simple. I have a class like
.element {
opacity: 0;
transition: opacity .5s ease-in;
}
.element-animated {
opacity: 1;
}
Then a little bit of jQuery to active the animation on load:
$(document).ready(function(){
$('.element').addClass('element-animated');
});
When adding $('#fullpage').fullpage(); to my code, my transitions stop working. When I remove it, they work again.
My architecture looks like:
<div id="fullpage">
<div class="section">
<div class="element"></div>
</div>
<div class="section2">
<div class="element2"></div>
</div>
</div>
If you read the fullpage.js faqs you'll it is recommended to fire those actions on the afterRender callback:
Short answer: if you are using options such as verticalCentered:true or overflowScroll:true in fullPage.js initialization, or even horizontal slides, then you will have to treat your selectors as dynamic elements and use delegation. (by using things such as on from jQuery). Another option is to add your code in the afterRender callback of fullpage.js
new fullpage('#fullpage', {
afterRender: function(){
$('.element').addClass('element-animated');
}
});
Related
I've been working on a UI in Ember and I am having difficulty implementing some of the event handling as described on the documentation here
I have a section of a nav bar I need to highlight on hover. The nav bar is made up of ember-bootstrap components.
{{#bs-navbar type="dark" backgroundColor="primary" as |navbar|}}
{{navbar.toggle}}
<div class="container-fluid" style="padding-left:50px;padding-right:50px; ">
<a class="navbar-brand" href="#"><img style="max-width:250px; margin-top: -12px;margin-bottom: -12px" src="/assets/images/logo_white_3x.png"></a>
{{#navbar.content}}
{{#navbar.nav as |nav|}}
{{#nav.item class="highlight-active"}}
{{#nav.link-to "index"}}LIVE{{/nav.link-to}}
{{/nav.item}}
{{/navbar.nav}}
{{/navbar.content}}
<div class="navbar-nav mr-left">
{{#navbar.content}}
{{#navbar.nav as |nav|}}
{{#nav.dropdown class="{{isHover}}" as |dd|}}
{{#dd.toggle }}Link<span class="caret"></span>{{/dd.toggle}}
{{#dd.menu as |ddm|}}
{{#ddm.item}}{{#ddm.link-to "index"}}Link{{/ddm.link-to}}{{/ddm.item}}
{{#ddm.item}}{{#ddm.link-to "cau.all"}}Link{{/ddm.link-to}}{{/ddm.item}}
{{/dd.menu}}
{{/nav.dropdown}}
{{#nav.item}}
{{#nav.link-to "index"}}Current User: <b>MICKEY MOUSE</b>{{/nav.link-to}}
{{/nav.item}}
{{/navbar.nav}}
{{/navbar.content}}
</div>
</div>
{{/bs-navbar}}
To accomplish this I tried to use one of the block events described n the documentation:
//template
{{#hover}}
<h1>link</h1>
{{/hover}}
//component
export default Component.extend({
hover() {
alert('hovered')
},
actions: {
//actions here
}
});
This produces the following error: hover not found, and the catch-all block handler didn't handle it
I thought it might be because the name of the even must be hyphenated so changed it accordingly. This produced a no component or helper by that name error.
Copying and pasting the same text from the guide produces the same errors which suggests there is something more fundamental I am not understanding.
Can anyone shed any light?
First off, if you need to highlight a navbar on hover, you should be doing this with css.
.someClass:hover: {
//apply highlight style
}
As for what's wrong with what you're doing in general, go back and look at those linked docs again. There's no event that ember handles called hover. What you're looking for is mouseEnter and mouseLeave. Check this twiddle to see an example:
export default Component.extend({
mouseEnter(){
this.set('hovering', true);
},
mouseLeave(){
this.set('hovering', false);
}
});
Where we only show the passed block on hover
Hover here ->
{{#if hovering}}
{{yield}}
{{/if}}
Try use an action for the mouseEnter event, e.g. <div mouseEnter={{action "showCaution"}}>
Another way to preserve native event behaviors and use an action, is
to assign a (closure) action to an inline event handler.
The action is simply a function defined on the actions hash of a component. Since the action is assigned to an inline handler, the function definition can define the event object as its first parameter.
actions: {
showCaution(event){
// Only when assigning the action to an inline handler, the event object
// is passed to the action as the first parameter.
}
}
On this page there is a slider updating a input box with example HTML code. You can also see that same code in the source.
I would like to use this in my application so I transplanted it into my code and converted it to Jade (aka Pug). The source now looks like:
div.row
div.small-10.columns
div.range-slider(data-slider data-options="display_selector: #days-off-count; initial: 28;")
span.range-slider-handle(role="slider" tabindex="0")
span.range-slider-active-segment
div.small-2.columns
input(type="number" id="days-off-count" value="28")
And the resulting html looks like this (after prettifying it):
<div class="row">
<div class="small-10 columns">
<div data-slider data-options="display_selector: #days-off-count; initial: 28;" class="range-slider">
<span role="slider" tabindex="0" class="range-slider-handle"></span>
<span class="range-slider-active-segment"></span>
</div>
</div>
<div class="small-2 columns">
<input type="number" id="days-off-count" value="28">
</div>
</div>
Which is very close that shown on in the docs. However on the resulting page the input box is not updated. If I change the input box to a span like in the
'With Label' example it updates.
span(id="days-off-count" value="28")
becomes
<span id="days-off-count" value="28"></span>
I have the foundation.js and the .slider.js included at the bottom of the page.
In addition, if I manually change the value of the input box via the keyboard the slider will jump to that position, so there is some sort of link there.
The software being used:
Ubuntu 14_04
Chrome
Node v0.10.25
Express 4.14.0
Jade 1.11.0
Foundation v5.5.0
Other things to note:
The page has more than one slider so any javascript solutions need to take this into account.
I think this is a bug (hasOwnProperty instead of hasAttribute #6221) in the version of Foundation (5.5.0) you're using. It seems that while it initially applied only to Firefox, it now applies to Chrome too.
Example with (broken) sliders from 5.5.0: jsfiddle.net/tymothytym/jth99pkw/3
Example with (working) sliders from 5.5.3: jsfiddle.net/tymothytym/tw1we8fk/3
The bug was fixed here: https://github.com/zurb/foundation-sites/commit/896e81f1275eefbbdb84ce4da9004ab059b26d45
Basically, go to foundation.slider.js and change this (line 157):
if (settings.display_selector != '') {
$(settings.display_selector).each(function(){
if (this.hasOwnProperty('value')) { // this is the mistake / bug
$(this).val(value);
} else {
$(this).text(value);
}
});
}
to this:
if (settings.display_selector != '') {
$(settings.display_selector).each(function(){
if (this.hasAttribute('value')) { // this should fix it
$(this).val(value);
} else {
$(this).text(value);
}
});
}
This is not my fix, it's the same as the patch, but it should mean that when you do upgrade you don't need to modify your application code to account for a workaround.
1) Maybe I be wrong... but you didn't specify the version, you give an example from Foundation v5... are you not have installed Foundation v6?
Try this example : https://foundation.zurb.com/sites/docs/slider.html
2) After you include your js files, you need to have this:
<script>
$(document).foundation();
</script>
Edit: Sorry, first time I didn't read all, maybe the problem is that the Foundation use the "value" attribute, which is an attribute designed for <input> tags:
value <button>, <input>, <li>, <option>, <meter>, <progress>, <param> Specifies the value of the element
Source: https://www.w3schools.com/tags/ref_attributes.asp
I have to construct a tree structure like the below image.
For this I use a Ember View and recursively call to construct the whole tree like structure based on the supplied model.
My Templates are:
<script type="text/x-handlebars" data-template-name="index">
<div class="zd-fldr fleft" style="width:230px;">
<ul class="fldr-sub">
{{#each item in model}}
{{view App.FoldertreeView model=item contentBinding="item"}}
{{/each}}
</ul>
</div>
</script>
<script type="text/x-handlebars" data-template-name="foldertree">
{{#if item.subfolder }}
<span {{action 'getSubFolder' item}} {{bind-attr class="item.IS_OPENED:fdtree-icon:ftree-icon"}}> </span>
{{else}}
<span class=""> </span>
{{/if}}
<span style="padding-top:20px;" class="fdetail fleft" >{{item.FOLDER_NAME}}</span>
<ul style="margin-top:30px;" {{bind-attr class="item.IS_OPENED:showdiv:hidediv"}}>
{{#each item in item.children}}
{{view "foldertree" model=item contentBinding="item"}}
{{/each}}
</ul>
</script>
JavaScript:
App.IndexRoute = Ember.Route.extend({
model: function() {
var treeArray = [];
for(var i=0; i<4000; i++){
var temp_obj = { 'FETCHED_DATA': false, 'FOLDER_ID': i, 'FOLDER_NAME': 'Folder_'+i, 'IS_OPENED': false, 'opened': true, 'subfolder': true, 'children': [] };
treeArray.push(temp_obj);
}
return treeArray;
}
});
App.FoldertreeView = Ember.View.extend({
tagName: 'li',
templateName: 'foldertree',
classNames: ['treediv', 's-fldr']
});
Initially I load only the first level folders from the server by calling an API.
Then when the open node is clicked, the children array is filled by calling an request to the server.
Now when the model length is greater than 3000 "Stop Script" error is thrown in Firefox browser.
In my tree there is no limit for the number of nodes. How can I solve this problem.
Demo JS Bin (Try it in Firefox)
Ember is a web framework. Given that information, you need to realize that you can't efficiently render 6000 items in a browser without reusing some view elements. Even native applications don't do this: in iOS, for instance, the cells in a TableView are reusable, so a table displaying a collection of 6000 items only has enough cells to cover the height of he view and some scrolling overlap. The view is aware of its scroll location, and renders the 10-20 items that need to be rendered from the collection, and when you scroll down it removes the top element, places an element at the bottom, and renders the next item in the data array. This way, everyone wins. I would suggest you do the same, as JS/HTML just can't handle that many elements efficiently.
I know it's not a fun implementation, but once you come up with a component that does this the first time, you'll be glad you did.
Honorable mentions: https://github.com/emberjs/list-view. You're doing a file tree and not a list, which is more difficult than just a long list, but you may still be able to use it if you change up your UI a little bit. If you have the folder structure navigable with a tree and show files in a list-view, this may mitigate your issue depending on whether the problem is with a number of files or a number of folders.
This is not really an Ember issue but a general javascript issue. When a script is taking to long time to execute this kind of errors message are displayed / fired by the browser and it's different on each browser.
You can read this good blog post about long time runing scripts
If you have browser environment undercontroll (i mean your computer our your companies computers) you can still setup firefox to run longer scripts
However a good practice would be to "split" your script in sub task taking less time to execute.
EDIT
Ass discussed in the comments this is due to the Huge number of view you generate. You can have 6000 models returned from your backend however generating 6000 view at once is heavy.
Here is a proposition on how to handle this : http://jsbin.com/zakisoyesi/6/edit?html,js,output free to you to adapt it to your use case and event to make it transparent to the user by using onScroll or any other event.
I'm building a popover/dropdown with Ember which essentially boils down to:
<div class="popover">
<span {{action showPopover}}>Click</span>
{{#if popoverShowing}}
<div class="popover-body">The popover body</div>
{{/if}}
</div>
All works fine but I have other elements on the page which are absolutely positioned and due to them forming a new stacking context there's no way I can make the popover be displayed above them.
If this were plain old Javascript, I'd append the popover to the body much like how Bootstrap does with the container option but we don't have that level of control in Ember AFAICT.
The only solution I can think of is to use an {{outlet}} in the application template and render to that, but that means for every popover/dropdown I have to split the contents out to a different view/template/controller and have an action in the router which seems rather over-complicated!
Can anyone think of a better option?
One approach that seems to work is to detach the body element on didInsertElement and then manually destroy on willDestroyElement:
didInsertElement: function() {
Ember.$("body").append(this.$())
},
willDestroyElement: function() {
this.$().remove()
}
This appears to work fine, but there are probably bugs lurking!
There are numerous questions that ask in one way or another: "How do I do something after some part of a view is rendered?" (here, here, and here just to give a few). The answer is usually:
use didInsertElement to run code when a view is initially rendered.
use Ember.run.next(...) to run your code after the view changes are flushed, if you need to access the DOM elements that are created.
use an observer on isLoaded or a similar property to do something after the data you need is loaded.
What's irritating about this is, it leads to some very clumsy looking things like this:
didInsertElement: function(){
content.on('didLoad', function(){
Ember.run.next(function(){
// now finally do my stuff
});
});
}
And that doesn't really even necessarily work when you're using ember-data because isLoaded may already be true (if the record has already been loaded before and is not requested again from the server). So getting the sequencing right is hard.
On top of that, you're probably already watching isLoaded in your view template like so:
{{#if content.isLoaded}}
<input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
<div>Loading data...</div>
{{/if}}
and doing it again in your controller seems like duplication.
I came up with a slightly novel solution, but it either needs work or is actually a bad idea...either case could be true:
I wrote a small Handlebars helper called {{fire}} that will fire an event with a custom name when the containing handlebars template is executed (i.e. that should be every time the subview is re-rendered, right?).
Here is my very early attempt:
Ember.Handlebars.registerHelper('fire', function (evtName, options) {
if (typeof this[evtName] == 'function') {
var context = this;
Ember.run.next(function () {
context[evtName].apply(context, options);
});
}
});
which is used like so:
{{#if content.isLoaded}}
{{fire typeaheadHostDidRender}}
<input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
<div>Loading data...</div>
{{/if}}
This essentially works as is, but it has a couple of flaws I know of already:
It calls the method on the controller...it would probably be better to at least be able to send the "event" to the ancestor view object instead, perhaps even to make that the default behavior. I tried {{fire typeaheadHostDidRender target="view"}} and that didn't work. I can't see yet how to get the "current" view from what gets passed into the helper, but obviously the {{view}} helper can do it.
I'm guessing there is a more formal way to trigger a custom event than what I'm doing here, but I haven't learned that yet. jQuery's .trigger() doesn't seem to work on controller objects, though it may work on views. Is there an "Ember" way to do this?
There could be things I don't understand, like a case where this event would be triggered but the view wasn't in fact going to be added to the DOM...?
As you might be able to guess, I'm using Bootstrap's Typeahead control, and I need to wire it after the <input> is rendered, which actually only happens after several nested {{#if}} blocks evaluate to true in my template. I also use jqPlot, so I run into the need for this pattern a lot. This seems like a viable and useful tool, but it could be I'm missing something big picture that makes this approach dumb. Or maybe there's another way to do this that hasn't shown up in my searches?
Can someone either improve this approach for me or tell me why it's a bad idea?
UPDATE
I've figured a few of the bits out:
I can get the first "real" containing view with options.data.view.get('parentView')...obvious perhaps, but I didn't think it would be that simple.
You actually can do a jQuery-style obj.trigger(evtName) on any arbitrary object...but the object must extend the Ember.Evented mixin! So that I suppose is the correct way to do this kind of event sending in Ember. Just make sure the intended target extends Ember.Evented (views already do).
Here's the improved version so far:
Ember.Handlebars.registerHelper('fire', function (evtName, options) {
var view = options.data.view;
if (view.get('parentView')) view = view.get('parentView');
var context = this;
var target = null;
if (typeof view[evtName] == 'function') {
target = view;
} else if (typeof context[evtName] == 'function') {
target = context;
} else if (view.get('controller') && typeof view.get('controller')[evtName] == 'function') {
target = view.get('controller');
}
if (target) {
Ember.run.next(function () {
target.trigger(evtName);
});
}
});
Now just about all I'm missing is figuring out how to pass in the intended target (e.g. the controller or view--the above code tries to guess). Or, figuring out if there's some unexpected behavior that breaks the whole concept.
Any other input?
UPDATED
Updated for Ember 1.0 final, I'm currently using this code on Ember 1.3.1.
Okay, I think I got it all figured out. Here's the "complete" handlebars helper:
Ember.Handlebars.registerHelper('trigger', function (evtName, options) {
// See http://stackoverflow.com/questions/13760733/ember-js-using-a-handlebars-helper-to-detect-that-a-subview-has-rendered
// for known flaws with this approach
var options = arguments[arguments.length - 1],
hash = options.hash,
hbview = options.data.view,
concreteView, target, controller, link;
concreteView = hbview.get('concreteView');
if (hash.target) {
target = Ember.Handlebars.get(this, hash.target, options);
} else {
target = concreteView;
}
Ember.run.next(function () {
var newElements;
if(hbview.morph){
newElements = $('#' + hbview.morph.start).nextUntil('#' + hbview.morph.end)
} else {
newElements = $('#' + hbview.get('elementId')).children();
}
target.trigger(evtName, concreteView, newElements);
});
});
I changed the name from {{fire}} to {{trigger}} to more closely match Ember.Evented/jQuery convention. This updated code is based on the built-in Ember {{action}} helper, and should be able to accept any target="..." argument in your template, just as {{action}} does. Where it differs from {{action}} is (besides firing automatically when the template section is rendered):
Sends the event to the view by default. Sending to the route or controller by default wouldn't make as much sense, as this should probably primarily be used for view-centric actions (though I often use it to send events to a controller).
Uses Ember.Evented style events, so for sending an event to an arbitrary non-view object (including a controller) the object must extend Ember.Evented, and must have a listener registered. (To be clear, it does not call something in the actions: {…} hash!)
Note that if you send an event to an instance of Ember.View, all you have to do is implement a method by the same name (see docs, code). But if your target is not a view (e.g. a controller) you must register a listener on the object with obj.on('evtName', function(evt){...}) or the Function.prototype.on extension.
So here's a real-world example. I have a view with the following template, using Ember and Bootstrap:
<script data-template-name="reportPicker" type="text/x-handlebars">
<div id="reportPickerModal" class="modal show fade">
<div class="modal-header">
<button type="button" class="close" data-dissmis="modal" aria-hidden="true">×</button>
<h3>Add Metric</h3>
</div>
<div class="modal-body">
<div class="modal-body">
<form>
<label>Report Type</label>
{{view Ember.Select
viewName="selectReport"
contentBinding="reportTypes"
selectionBinding="reportType"
prompt="Select"
}}
{{#if reportType}}
<label>Subject Type</label>
{{#unless subjectType}}
{{view Ember.Select
viewName="selectSubjectType"
contentBinding="subjectTypes"
selectionBinding="subjectType"
prompt="Select"
}}
{{else}}
<button class="btn btn-small" {{action clearSubjectType target="controller"}}>{{subjectType}} <i class="icon-remove"></i></button>
<label>{{subjectType}}</label>
{{#if subjects.isUpdating}}
<div class="progress progress-striped active">
<div class="bar" style="width: 100%;">Loading subjects...</div>
</div>
{{else}}
{{#if subject}}
<button class="btn btn-small" {{action clearSubject target="controller"}}>{{subject.label}} <i class="icon-remove"></i></button>
{{else}}
{{trigger didRenderSubjectPicker}}
<input id="subjectPicker" type="text" data-provide="typeahead">
{{/if}}
{{/if}}
{{/unless}}
{{/if}}
</form>
</div>
</div>
<div class="modal-footer">
Cancel
Add
</div>
</div>
</script>
I needed to know when this element was available in the DOM, so I could attach a typeahead to it:
<input id="subjectPicker" type="text" data-provide="typeahead">
So, I put a {{trigger}} helper in the same block:
{{#if subject}}
<button class="btn btn-small" {{action clearSubject target="controller"}}>{{subject.label}} <i class="icon-remove"></i></button>
{{else}}
{{trigger didRenderSubjectPicker}}
<input id="subjectPicker" type="text" data-provide="typeahead">
{{/if}}
And then implemented didRenderSubjectPicker in my view class:
App.ReportPickerView = Ember.View.extend({
templateName: 'reportPicker',
didInsertElement: function () {
this.get('controller').viewDidLoad(this);
}
,
didRenderSubjectPicker: function () {
this.get('controller').wireTypeahead();
$('#subjectPicker').focus();
}
});
Done! Now the typeahead gets wired when (and only when) the sub-section of the template is finally rendered. Note the difference in utility, didInsertElement is used when the main (or perhaps "concrete" is the proper term) view is rendered, while didRenderSubjectPicker is run when the sub-section of the view is rendered.
If I wanted to send the event directly to the controller instead, I'd just change the template to read:
{{trigger didRenderSubjectPicker target=controller}}
and do this in my controller:
App.ReportPickerController = Ember.ArrayController.extend({
wireTypeahead: function(){
// I can access the rendered DOM elements here
}.on("didRenderSubjectPicker")
});
Done!
The one caveat is that this may happen again when the view sub-section is already on screen (for example if a parent view is re-rendered). But in my case, running the typeahead initialization again is fine anyway, and it would be pretty easy to detect and code around if need be. And this behavior may be desired in some cases.
I'm releasing this code as public domain, no warranty given or liability accepted whatsoever. If you want to use this, or the Ember folks want to include it in the baseline, go right ahead! (Personally I think that would be a great idea, but that's not surprising.)