dojo: Show or hide divs based on selection in a list - list

Is there a simple way to accomplish this using dojo (jQuery would be easier for me but I have to use dojo): I have a simple unordered list. I don't want dojo to style the list (as it might if I used some widget). When I click a link on the list I want to show a div associated with the link. Then if I click another link in the list the first div hides and that one shows.
<div id="content">
<h2>Header</h2>
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<div id="sub_content1" style="display:none;">
<h3>Sub Content Header 1</h3>
<p>Lorem ipsum veritas britas conflictum civa</p>
</div>
<div id="sub_content2" style="display:none;">
<h3>Sub Content Header 2</h3>
<p>Lorem ipsum mobius ceti</p>
</div>
<div id="sub_content3" style="display:none;">
<h3>Sub Content Header 3</h3>
<p>Lorem ipsum citrus pecto</p>
<ul>
<li>Lemons</li>
<li>Limes</li>
</ul>
</div>
</div><!-- end of #content -->

So in fact you're creating your own tabcontainer? If you really want to do it yourself you should probably need something like this:
require(["dojo/ready", "dojo/on", "dojo/dom-attr", "dojo/dom-style", "dojo/query", "dojo/NodeList-dom"], function(ready, on, domAttr, domStyle, query) {
ready(function() {
query("ul li a").forEach(function(node) {
query(domAttr.get(node, "href")).forEach(function(node) {
domStyle.set(node, "display", "none");
});
on(node, "click", function(e) {
query("ul li a").forEach(function(node) {
if (node == e.target) {
query(domAttr.get(node, "href")).forEach(function(node) {
domStyle.set(node, "display", "block");
});
} else {
query(domAttr.get(node, "href")).forEach(function(node) {
domStyle.set(node, "display", "none");
});
}
});
});
});
});
});
I'm not sure how familiar you are with Dojo, but it uses a query that will loop all links in lists (with the dojo/query and dojo/NodeList-dom modules) (you should provide a classname or something like that to make it easier). Then it will, for each link, retrieve the div corresponding to it and hide it, it will also connect a click event handler to it (with the dojo/on module).
When someone clicks the link, it will (again) loop all the links, but this time it's doing that to determine which node is the target one and which isn't (so it can hide/show the corresponding div).
I made a JSFiddle to show you this. If something is still not clear you should first try to look at the reference guide of Dojo since it really demonstrates the most common uses of most modules.
But since this behavior is quite similar to a TabContainer, I would recommend you to look at the TabContainer reference guide.

Related

Foundation Slider does not update input

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

Remove accordion slide animation on page load

When my page loads the active accordion item slides open which causes the other elements on the page to also move for a second.
I'd like the active accordion item to render "already opened", but still retain the animation for when a user clicks on another accordion item, or even the originally opened item in cases where the user selects another item and then the original.
I cant find any css being used to do this, so maybe its something in the javascript? If so, how would I go about overriding this.
Any suggestions on how to accomplish this would be greatly appreciated!
Foundation Accordion: http://foundation.zurb.com/sites/docs/accordion.html
There's no simple solution from what I know, but you can override some attributes on load so that the active item is displayed by default. You'd have to begin with an accordion none of the items are active i.e. none of the accordion items have a class of .is-active:
Html:
<ul class="accordion" data-accordion data-allow-all-closed='true'>
<li class="accordion-item" data-accordion-item>
Accordion 1
<div class="accordion-content" data-tab-content>
Panel 1. Lorem ipsum dolor
</div>
</li>
<li class="accordion-item" data-accordion-item>
Accordion 2
<div class="accordion-content" data-tab-content>
Panel 2. Lorem ipsum dolor
</div>
</li>
<li class="accordion-item" data-accordion-item>
Accordion 3
<div class="accordion-content" data-tab-content>
Panel 3. Lorem ipsum dolor
</div>
</li>
</ul>
jQuery:
//Initialise the Foundation plugins
$(document).foundation();
function activateWithoutAnimation(itemIndex) {
//Get the accordion item according to its index (0 based)
var $accordionItem = $('.accordion-title:eq(' + itemIndex + ')');
//Set the aria attributes of the accordion item you want to appear
$accordionItem.attr('aria-expanded', 'true');
$accordionItem.attr('aria-selected', 'true');
//Set the attributes of the content.
$accordionItem.next().attr('aria-hidden', 'false');
$accordionItem.next().css('display', 'block');
//This is essential as it lets foundation know that the item is active (to re-allow toggling)
$accordionItem.trigger('click');
}
//Call the function.
activateWithoutAnimation(0);
There might be a better way to do the above, but from what I've read, there's no option that allows the animation to be disabled. I've tried overriding the defaults that Foundation provides to no avail.
Fiddle Demo
CSS Solution
If using the default helper class .is-active on initial load then some plain css can eliminate the slide open animation.
.is-active .accordion-content {
display: block;
}
https://jsfiddle.net/r6jvbohu/
CSS only (without losing the animation)
To always open the first item immediately while still keeping the animation, you can use the following code.
.accordion-item:first-of-type .accordion-content {
display: block;
}
The first item should still have the .is-active class to be consistent, but this removes the initial animation because we overwrite the display value from none to block. The JS still overwrites this state when another accordion item is clicked, resulting in the animation we want to keep.

Zurb foundation more than one joyride

Is it possible to have two joyrides on one page? For example: run one joyride, then, once the user selects something, start a second joyride?
I believe what your looking for has its roots in the answer to this question. You can use the Callback to then initialize a 2nd Joyride by doing something such as triggering a button click on a hidden button.
I haven't been able to test this code yet, but it should work something along these lines:
// set up the config for a Callback
var config = {
nubPosition : 'auto',
postRideCallback : function() {
// Joyride has ended, simulate a click on a hidden button
$(#hidden_button).click();
}
// When the Hidden button is clicked, call the 2nd Joyride
$( "#hidden_button" ).click(function() {
$(document).foundation('joyride2', 'start');
});
You just have to wrap your joyride in a div and then start that joyride when you want to.
For example:
<div id="Joyryde1">
<ol class="joyride-list" data-joyride >
<li data-id="message1" data-text="Next" data-options="tip_location: top">
<p>Joyride 01</p>
</li>
</ol>
</div>
<div id="Joyryde2">
<ol class="joyride-list" data-joyride >
<li data-id="message1" data-text="Next" data-options="tip_location: top">
<p>Joyride 02</p>
</li>
</ol>
</div>
<script>
$("#joyride1").foundation('joyride', 'start'); //Start when the page loads
// Add this to your trigger(button or another page load)
$("#joyride2").foundation('joyride', 'start');
</script>

Ember.Component (block form): more than one outlet {{yield}}

I see that ember has a very nice mechanism for wrapping content in a component using the {{yield}} mechanism documented here.
So, to use the example in the documentation, I can have a blog-post component template defined like so:
<script type="text/x-handlebars" id="components/blog-post">
<h1>{{title}}</h1>
<div class="body">{{yield}}</div>
</script>
I can then embed blog-post into any other template using the form:
{{#blog-post title=title}}
<p class="author">by {{author}}</p>
{{body}}
{{/blog-post}}
My question is, can I specify two different {{yield}} outlets in the components template?
Something like this is possible via Named Outlets in Ember.Route#renderTemplate like so:
Handlebars:
<div class="toolbar">{{outlet toolbar}}</div>
<div class="sidebar">{{outlet sidebar}}</div>
JavaScript:
App.PostsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ outlet: 'sidebar' });
}
});
I'm not sure I can take this path for a component which will not know what route's template would be rendering it.
EDIT 1:
For the sake of clarity, I'm trying to implement the Android Swipe for Action Pattern as an Ember component.
So, I'd like users of this component to be able to specify two different templates:
A template for the normal list item, and
A template for the actions that are revealed when a swipe on (1) is detected.
I want to make this into a component, because quite a lot of javascript goes into handling the touch(start/move/end) events, while still managing smooth touch based scrolling of the list. Users would supply the two templates and this component would manage handling of touch events and necessary animations.
I've managed to get the component working in the block form, where the block's contents are treated like (1). The second template (2) is specified through a parameter (actionPartial below) which is the name of a partial template for the actions:
Component Handlebars Template: sfa-item.handlebars
<div {{bind-attr class=":sfa-item-actions shouldRevealActions:show" }}>
{{partial actionPartial}}
</div>
<div {{bind-attr class=":sfa-item-details isDragging:dragging shouldRevealActions:moveout"}}>
{{yield}}
</div>
Calling Handlebars Template:
{{#each response in controller}}
<div class="list-group-item sf-mr-item">
{{#sfa-item actionPartial="mr-item-action"}}
<h5>{{response.name}}</h5>
{{/sfa-item}}
</div>
{{/each}}
Where the mr-item-action handlebars is defined like so:
mr-item-action.handlebars:
<div class="sf-mr-item-action">
<button class="btn btn-lg btn-primary" {{action 'sfaClickedAction'}}>Edit</button>
<button class="btn btn-lg btn-primary">Delete</button>
</div>
Problem is, actions from the user supplied partial, sfaClickedAction above, are not bubbled up from the component. A fact which is mentioned in the docs in para 4.
So, now I do not know how a user could capture actions that he defined in the supplied actions template. A component cannot catch those actions because it doesn't know about them either.
EDIT 2
I sprung a follow up question here
This blog post describes the most elegant solution for Ember 1.10+: https://coderwall.com/p/qkk2zq/components-with-structured-markup-in-ember-js-v1-10
In your component you pass yield names into {{yield}}s:
<header>
{{yield "header"}}
</header>
<div class="body">
{{yield "body"}}
</div>
<footer>
{{yield "footer"}}
</footer>
When you invoke your component, you accept the yield name as a block param... and use an esleif chain!
{{#my-comp as |section|}}
{{#if (eq section "header")}}
My header
{{else if (eq section "body")}}
My body
{{else if (eq section "footer")}}
My footer
{{/if}}
{{/my-comp}}
PS eq is a subexpression helper from the must-have ember-truth-helpers addon.
PPS Relevant RFC: proposal, discussion.
Since it is not possible to have two {{yield}} helpers within one component (how would the component know where one {{yield}}'s markup stops and the next one begins?) you may be able to approach this problem from a different direction.
Consider the pattern of nested components. Browsers do this already with great success. Take, for example, the <ul> and <li> components. A <ul> wants to take many bits of markup and render each one like a member of a list. In order to accomplish this, it forces you to separate your itemized markup into <li> tags. There are many other examples of this. <table>, <tbody>, <tr>, <td> is another good case.
I think you may have stumbled upon a case where you can implement this pattern. For example:
{{#sfa-item}}
{{#first-thing}}
... some markup
{{/first-thing}}
{{#second-thing}}
... some other markup
{{/second-thing}}
{{/sfa-item}}
Obviously first-thing and second-thing are terrible names for your specialized components that represent the things you'd want to wrap with your first and second templates. You get the idea.
Do be careful since the nested components won't have access to properties within the outer component. You'll have to bind values with both outer and inner components if they are needed in both.

Ember.js: Complex view layout, what's the proper approach?

We're building app that allows users to post messages to various social media outlets. Our designer has created a series of interactions which allow users to change various settings in their application by use of sliding panels. I've done a quick screen cap to illustrate:
http://screencast.com/t/tDlyMud7Yb7e
The question I have is one of architecture. I'm not sure whether I should be using a View or a Controller (or both) to store some of the methods these panels will contain. Here's the HTML for the panels. They're not currently in a script tag or view:
<div id="panel-account-settings" class="panel closed">
<div class="panel-inner">
<i class="icon-cancel"></i>close
<h3>Account Settings</h3>
Google Analytics
Link Shortening
Disconnect Account
</div>
<div id="panel-google-analytics" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Google Analytics</h3>
<div class="toggle">
<label>Off</label>
</div>
<p>We <strong>won't</strong> append stuff to your links, so they <strong>won't</strong> be trackable in your Google Analytics account.</p>
<img src="{{ STATIC_URL }}images/ga-addressbar.png" />
</div>
<div id="panel-disconnect" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Disconnect This Account</h3>
<p>If you disconnect this account you will lose all the metrics we tracked for each message. Are you absolute sure you want to get rid of them?</p>
<div class="button-group">
Disconnect
</div>
</div>
</div>
The gear icon shown in the video is contained with the accounts template
<script type="text/x-handlebars" data-template-name="accounts">
{{#each account in controller}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>#{{account.username}}</p>
<i class="icon-cog" {{action "openPanel" Social.SettingsView account }}></i>
</div>
{{/each}}
</script>
which has a bare bones controller
Social.AccountsController = Ember.ArrayController.extend({
openPanel: function(view,account){
console.log(view,account);
$(this).parents(".item-account").addClass("active");
$("#panel-account-settings").prepareTransition().removeClass("closed");
}
});
as well as a Route and a Model. Given the interaction I'm looking to accomplish, my question is where should I be putting the pieces and parts? At a minimum I need to pass in the current Account model so that I know which account I'll be applying changes to. I thought about creating a mainPanel view which would contain the other view...something like this:
<script type="text/x-handlebars" data-template-name="panelView">
<div id="panel-account-settings" class="panel closed">
{{ partial "panelSettingsView" }}
{{ partial "panelAnalyticsView" }}
{{ partial "panelDisconnectView" }}
</div>
</script>
and then the action helper on the gear icon could pass in the account AND the required view. But I'm not sure if that's the right approach. I'd appreciate some input or suggestions. Thanks.
UPDATE 1
Ideally I'd like to eventually load in the content of each panel via AJAX but that's a want to, not a need to.
UPDATE 2
I tried creating a PanelView which would contain the logic on which panels to load:
Social.PanelView = Ember.View.extend({
tagName: 'div',
classNames: ['panel-inner'],
openPanel: function(view,account){
console.log(view,account);
}
});
But when I tried to call it from the gear icon I got an error. This:
<i class="icon-cog" {{action openPanel target="Social.PanelView" }}></i>
Threw this error:
Uncaught Error: assertion failed: The action 'openPanel' did not exist on Social.PanelView
Isn't that the correct syntax?
UPDATE 3
Adding version information:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.1
The best practice is to always put any DOM- or UI-related logic into your view, and leave data representation to the controller (i.e., a reference to a 'selected' item in the controller is a common example).
Your Social.AccountsController.openPanel method has logic that touches the DOM, which is entirely a view concern. A good start would be to move that logic into the view (Social. SettingsView ?).
It'd be a bit easier to understand your goals and offer more suggestions if you had a jsfiddle of what you have so far.
EDIT: Another good practice is to decompose things into very small objects. So you could explore having a selectedAccount ObjectController whose content is the currently chosen Account (and a corresponding View for it).