How to make a one page menu in Ember? - ember.js

I'm wondering how can I make a menu like in this website with Ember ?
The page is split in different sections and we can scroll to go to each section, a click on the menu make the page scroll to the wanted section.
I'm not sure if I should have different routes in the router for this behavior, I would guess it's not the case as when we change route the view is remove from the DOM.
Then, how should I build the anchor link to each section ?
The best solution will automatically updates route when we scroll the page but any solution to handle the link and URL recognition will be fine.

Some may argue otherwise, but Ember may be a little overkill for a website landing page like you've shown. Ember is meant more for robust web apps that have multiple views and data they need to be connected with.
First off, if you look at their script, they're using jQuery to animate the body's scrollTop position to the respective div and setting window.location.hash to the hash of the menu element's href which also happens to be the ID of the <section/> the body scrolls to:
$(document).on('click', '#nav a, .clients-capabilities a', function(){
var target = $(this);
var hash = this.hash;
var destination = $(hash).offset().top;
stopAnimatedScroll();
$('#nav li').removeClass('on');
target.parent().addClass('on');
$('html, body').stop().animate({
scrollTop: destination
}, 400, function() { window.location.hash = hash; });
return false;
});
Secondly, they are not doing anything special to load to a specific position on page load. If you load any page on the web with a hash, the browser will look for an element with that ID and load at that position. For example, http://emberjs.com/#download.
Even if you still want to use Ember for this, you'd probably end up doing something similar with a single view loaded from the / route so I wouldn't even worry about Ember until your site becomes a full fledged web app.

Try using a *: catch all pattern in the router on the page you want to handle this scenario.
so lets say that index will work as a single page that you have to be able to automatically scroll to certain elements from the url.
Also you have posts and about sub pages that you can go to from the index page via links.
then...
App.Router.map(function() {
this.route('about');
this.route('posts');
this.route('index',{path:'*:'});
});
so if you have an element with id="elementToScrollTo" then the url /#elementToScrollTo would load the index page and scroll to that element.
See How to handle 'no route matched' in Ember.js and show 404 page? also for some other ways to solve this.
I hope this helps you.

Related

Ember router breaks on redirect to '/'

I'm having a hard time making my router understand that we are on a certain page and it should be displayed in the navigation as active. The situation is as follows:
this.route('mainRoute', function() {
this.route('list');
});
The path we are interested in is /mainRoute. Unfortunately, there are a lot of legacy links that point to /mainRoute/list. The workaround for this was to redirect from the /mainRoute/list component back to the /mainRoute component.
beforeModel() {
this.replaceWith('/mainRoute');
}
Now, my issue is that the /mainRoute navigation link will never be seen as active. I've tried adding a path for the /mainRoute ('/', '/mainRoute', 'mainRoute'), I've tried transforming it to a resource and a bunch of other things that passed my mind. But it either won't work, or will go in an infinite redirecting loop.
Any thoughts on it? Thanks so much, I really need a solution for this!
If the navigation links are {{link-to}} components. There is a current-when property you could use here. It accepts either a boolean or a string. The string is a space separated values with the route names you want this link to be active when.
From the docs
If you need a link to be 'active' even when it doesn't match the current route, you can use the
current-when argument.
<LinkTo #route='photoGallery' #current-when='photos'>
Photo Gallery
</LinkTo>
{{#link-to 'photoGallery' current-when='photos'}}
Photo Gallery
{{/link-to}}

Ember: get target url from afterModel

I have two paths pointing to the same route:
this.route('items', { path: ':fruitName/rotten' });
this.route('items', { path: ':fruitName' });
I am trying to get the current URL from within the afterModel hook of the /items/fruitName route. If I use this.get('router.url'), it will give me the previous URL before the transition. I need to detect if the target has 'rotten' at the end of the path.
I found that transition.intent.url has the path as a string, but only when its manually entered into the browser. If I click a link to transtion, transition.intent.url is undefined.
How can I determine if the transition URL is /items/fruitName/ or /items/FruitName/rotten?
Before determine which url you have, my question is : how do you make a transition with a different url to the same route (by using {{link-to}}) ?
In your case, you have the same route for 2 different purpose, I understand what you want to accomplish but you will break ember rules (which force you to make a bad design).
You can accomplish what you want to do by using nested routes. let me explain:
/:fruitName and /:fruitName/rotten have the same base (same data): a page for a fruit but in the rotten route, you want to display in another tab.
It's a design problem not a route problem, you can make the most of nested route to:
use index to display main content
subroute to display other thing
you can check an example here with a basic css tab.
Could you use queryParams for this? So your URL would look something like this:
items/tomato?rotten=false
or perhaps you want to go with something more generic:
items/tomato?state=rotten
The route and controller will be set up something like what can be found here: https://guides.emberjs.com/v2.14.0/routing/query-params/

ember.js beginner advice required

I am attempting to learn ember.js to produce a simple-ish web application. I have read around the subject and completed the basic tutorial on emberjs.com . So far, I have managed to create the beginnings of an API using PHP and I have been able to retrieve that data into my fledgling application. So far so good.
As a starter, I wish to retrieve a list of users from the server and populate a select with these values. The select is to sit on the menu bar and the selection from this drives the rest of the data for the application. I have placed the model in models/users.js. The application menu I have defined in templates/application.hbs (not sure if this is considered correct or not).
I am unable to get the users data for the select retrieved from the server unless I visit the users route that I have also setup. How can I ensure that the users data is populated at application initialisation? How would I apply/filter the rest of the application data upon selection from this component - would I need to set a global variable or something?
Additionally, I am using materializecss, which requires $('select').material_select(); to be run in order to render the select input. I have this as:
$( document ).ready(function(){
$('select').material_select();
});
in templates/route.js and works fine on initial page load, but upon navigation to another area and back again, the select is not rendered, and from what I have read, I need to call material_select() again, but I can't for the life of me work out where this call should go.
If anyone is willing to take the time to help me better understand things, I will be very grateful.
Cheers
Tim
Preloading Clarification
I wish to have a select box on the main menu bar (menu-bar component). The user will use this to select a user. All content in the app will be filtered to the currently selected user. I currently have an API endpoint (/users.php) which returns the required data, but I am only able to get my application to call this when I visit the users route, which obviously I won't be doing when the app loads initially. So I am thinking I need to preload the data somehow, unless there is a better way that I am not aware of?
Create a component for menu and put the component in application.hbs
And in component put the materialize stuff like this : (notice to the place)
//components/menu-bar.js
didInsertElement(){
this._super(...arguments);
Ember.$( document ).ready(function(){
Ember.$('select').material_select();
});
}
And
//templates/application.hbs
{{menu-bar}}
{{outlet}}
UPDATE
Create a service holding current user selection and use it in other places that you want be changed by changing the user selection.
These codes is just for showing that the solution for what you want (changing the other parts of app based on changing the selected user in menu) is services.
//services/current-selection.js
...
selectedUser: null,
...
and
//components/menu-bar.js
currentSelection: Ember.inject.service(),
actions: {
selectUser(user){
this.set('currentSelection.selectedUser', user);
}
}
and in another place you want to be notified when selection changed :
//components/user-info.js
currentSelection: Ember.inject.service(),
and corresponding template
//templates/components/user-info.hbs
{{#if currentSelection.selectedUser}}
<span>Current Username: <strong>{{currentSelection.selectedUser}}</strong></span>
{{/if}}
Update For pre-loading users
Actually it's not pre-loading
In menu-bar component you load a select box and populate it by calling a service to backend. If you put this component in application.hbs you could ensure what you want is satisfied.
//components/menu-bar.js
store: Ember.inject.store(),
users: Ember.computed(function(){
return this.get('store').findAll('user');
}),
OR
In application route model hook you fetch those users and pass to component like {{menu-bar users=model}}
And for these two options you can use users in menu-bar template.
But pay attention to that if the users count is a lot, this is very bad for performance or UX. If your users are a lot it's better to use from a autocomplete component

Facebook Developer API in reactjs code

I have two files in my project
index.html : contains 'app-container' where reactjs components mount
app.js : the file which has the react components
I want to use the facebook like and share button in my reactjs app. But I want them to appear only at certain points of time.
That is, I want to display them from reactjs's render() function, not permanently on my html page.
I proceeded the following way -
1.) I added the link to facebook javascript SDK on my index.html page.
2.) I added the code-plugin for like/share button in my render method.
<div class="fb-share-button" data-href="http://localhost:3000/game" id = "fbshare" data-layout="button_count"></div>
But the problem is that even though the components mount properly, like/share button are not visible.
Can anyone point out how I should proceed ?
Since class is a reserved word, React uses className. The below should solve your problem
<div className="fb-share-button" data-href="http://localhost:3000/game" id="fbshare" data-layout="button_count"></div>
The problem is that it won't complain if you type class="", or anything else for that matter, like xlink:href="" or other unsupported attribute names. It will simply leave it out, and your share button won't get it's style, thus probably leaving it invisible on the page.
If you want to use unsupported attribute names, dangerouslySetInnerHTML is the way to go.
render: function() {
var html = '<div class="fb-share-button" data-href="http://localhost:3000/game" id="fbshare" data-layout="button_count"></div>';
return (
< div dangerouslySetInnerHTML = {{__html: html}} />
);
}
If it still isn't working, it's probably because you are rendering the button after Facebook has finished parsing the page. In that case, you can use FB.XFBML.parse() in componentDidMount().

ArrayController model records are hanging around between calls

I have a search screen with search filters and a results screen with the table of search results.
this.resource('search', function() {
this.route('results');
});
When the user selects the submit button on the "search" route they are transitioned to "search.results" route.
When the user changes any of the filters while in the "search.results" route they are redirected to the "search" resource because the results are no longer valid.
The issue I had was that the previous ArrayController records were hanging around between searches. I had to manually clear the items in the array controller as below so that the old records would disappear.
Why do I need to do this. Is there a better way around it?
model : function() {
this.controllerFor('search/results').set('content', []); //why do I need this?
return this.fetchItems(1);
},
Controllers' content hangs around as you navigate within the app unless something changes them. To clear them, you could use
this.controllerFor('search/results').get('content').clear(). If you would rather not clear them and want to indicate that the results are being fetched in some way, like a loading spinner, look at ember's loading routes.