Needless to say, my experience with Meteor is lacking. I come from a Rails background, where you can do a lot more logic (and magic) in your views than Meteor.
The situation: I've got some routes that look like /things/:_id, and I've named that route 'thing' because it shows only one thing of a user's many owned things:
FlowRouter.route('/things/:_id', {
name: 'thing',
action() {
BlazeLayout.render('appPage', {app: 'thing', sidebar: "thingsListOnThing", header: 'thingTitle'});
}
});
As you can see, I'm also loading a template I've built to list all of the user's owned things on the sidebar for easy navigation. That template, thingsListOnThing is the target of this question.
Get the gist? I'm able to mark the route that dislays a template with a complete list of a user's things as active using zimme:active-route like so:
// A list of all a user's things
<div class="{{isActiveRoute name='things' class='is-active'}}">
List of Things
</div>
This package is great, but it won't help me with routes that look like /things/:_id because, then every link to each individual thing would be is-active on any thing page, not just the one where the current path's :_id matches the _id of the active thing.
I'm really kind of stuck here. My first guess would be to use template helpers, but I'm confused as to where to get started with that.
If need be, please as me to upload any piece of my code you require. I figured it's such a generic question that you guys probably don't need to see all of my code to understand what I'm trying to accomplish.
Using: zimme:active-route
Template Helpers
Template.listOfThings.helpers({
activeListClass(page) {
const active = ActiveRoute.name('thing') && FlowRouter.getParam('_id') === this._id;
return active && 'active';
},
});
Template HTML:
<a class="{{isActivePath 'thing'}}" href="/things/{{_id}}">
<div class="thingListItem {{activeListClass page}}">
{{title}}
</div>
</a>
Related
Is there a way with EmberJS to show a loading template somewhere when something in my page is loading something?
For example, I have a page with many many songs artists, and each of them have many many songs. I load the page of the artists, and good, but in background I'm loading info about songs of everyone. I simply need a loading spinner somewhere that says to me the site is loading something (for example, there is in "Network" tab of Chrome Developer Tools something pending...)
how to do that in such an elegant Emberjs way?
You can observe the isPending property of Ember.PROMISEPROXYMIXIN like so:
{{#if artist.songs.isPending}}
loading...
{{/if}}
As far as I know (and based on a quick perusal of the API docs) Ember doesn't provide a built-in way to achieve this. Unless I'm wrong, this means you'll need to implement request state tracking yourself.
You could do this in Ember's adapter layer (e.g. add code in app/adapters/application.js), but the simplest solution might be to just work with jQuery directly. There are a few APIs you can use:
jQuery.active, which indicates the number of outstanding requests
ajaxStart and ajaxStop
I'd recommend creating an Ember.Service to track this state-- then you can inject it in any Controller or Component that needs to render a template based on this info. Your Service could look like:
import Ember from 'ember';
export default Ember.Service.extend({
init() {
this._super(...arguments);
const invalidateRequestInProgress = () => {
this.notifyPropertyChange('dummyProperty');
}
$(document).ajaxStart(invalidateRequestInProgress);
$(document).ajaxStop(invalidateRequestInProgress);
},
requestInProgress: Ember.computed('dummyProperty', function() {
return Ember.$.active !== 0;
}),
});
I haven't tested this code, so I'm not sure if it'll work in practice :)
I have a template I have set up as my layout using iron-router:
Router.configure({
layoutTemplate: 'main'
});
Within this layout, I have a line of html that I would like to be changed on certain routes.
My ideas is doing something like:
{{#if landing }}
<div id="page-wrapper">
{{/if}}
However, how do I implement this for a certain route? I want this variable to be true on every route except for 1.
I think that this kind of "change the template based upon what route I'm on" logic fits best into the use of a template for that route (and any others that will make this same change. Depending on the change required, you may be able to call in your base Template into the modified template.
Example:
Router.route('/yourSpecialRoute', function(){
this.layout('OtherLayout');
});
See the Layout docs - I borrowed the syntax of Setting Region Data Contexts
Having said, if you prefer not to switch layouts per route, consider setting a data on your route (something like {data: item} as shown here in the iron:route readme which can then be read by a global helper (Template.registerHelper syntax) - this will at least make it consistent across your routes / templates.
My supervisor just handed me a pile of JSON files from a freelancer which we are going to use to make multiple (similar) websites. Lucky me, I'll get to be the one updating the content and css for the different versions.
This is my first time working with JSON, so while I can't be sure that this is a poorly designed template, the fact that the css is very messy (in order to change the color of buttons throughout the site from yellow to orange, at least 15 different classes need to be adjusted, which seems to me to defeat the whole purpose of css...) doesn't give me hope.
I've brute-forced my way through the first two different sites, but since it looks like we'll be doing a lot more of them, I'm looking for ways to streamline the process (in particular making sure to change the content in all the places the content needs changing, which is a lot of files, with different content for different versions).
I'm personally old-school enough to like awk (well, that, and it's what I'm most used to programming in), so my backup plan is to just set up an awk/batch script or two which will take in a "these are the bits of info that go in these specific places" file and update all the relevant files. However, I'm sure there's a better way to do this, which is why I'm turning to y'all.
Is there anything that already exists for streamlining processes like these? Or a coding system/language that's well-suited to this project? A GUI which I can connect to bits of text that need changing?
Ideally, I'd like to set up something that even a monkey (or a non-caffeinated me) could use as often as needed. I'm already going to have to dive into the source code to clean it up (because, gasp, we might need to be able to have more than 5 people on the "our team" page, for example - without bad css/html workarounds), so making other tweaks that'll help with the content update process can happen en route.
I have recently used underscore to render templates from JSON. this is a front end tool, but you could automate it with some backend tools (a simple cURL or file_get_content in php will do).
Here is a link to a tutorial
your template will be a JavaScript template in your html file:
<div id="rendered"></div>
<script type="text/template" class="template">
<%- rc.listTitle %>
</script>
and in your JavaScript code you load:
<script type="text/javascript">
// When rending an underscore template, we want top-level
// variables to be referenced as part of an object. For
// technical reasons (scope-chain search), this speeds up
// rendering; however, more importantly, this also allows our
// templates to look / feel more like our server-side
// templates that use the rc (Request Context / Colletion) in
// order to render their markup.
_.templateSettings.variable = "rc";
// Grab the HTML out of our template tag and pre-compile it.
var template = _.template(
$( "script.template" ).html()
);
// Define our render data (to be put into the "rc" variable).
var templateData = {
listTitle: "Olympic Volleyball Players",
};
// Render the underscore template and inject it after the div rendered
// in our current DOM.
$( "#rendered" ).after(
template( templateData )
);
</script>
In my meteor app I have all pages that needs to load one or more documents from elements collection and a couple of pages that loads documents from items collection;
I have the two publish functions for the two collections, but now I'm trying to understand the best way to subscribe them;
currently I'm subscribing elements in a file in the /client directory:
Meteor.subscribe("elements");
This will make the subscription on application start;
in the other case, since the items to be loaded are a little more, I've decided to use the subscription option in iron router:
subscriptions: function() {
return Meteor.subscribe("items");
}
The first difference I can see is that it seems that elements, once started the application, will not take time anymore to load (navigating through pages takes no time for loading);
on the other hand, every time I open the page that loads items, (event without a refresh) takes the loading time again;
Would be probably not the best solution to subscribe also items on application start, since they are only needed if you visit their specific route (elements otherwise are needed everywhere);
but there is a way to avoid reloading them from the server every time I ask the route again?
Also thinking about elements, they are not needed all in all pages: avery page need just one or two of them; so it would be probably more corret to load just the one or two that are really needed...
How subscriptions should be managed in your opinion?
I cover some of the trade-offs in my answer to this question. Here's how we use do this at Edthena using the subscriptions manager package:
// Allow the subman to cache a lot of subscriptions for a very long time
var subman = new SubsManager({cacheLimit: 100, expireIn: 864000});
// This is a global subscription that we may need to wait on later
subman.subscribe('sub1');
// This is a global subscription that we don't need to wait on
Meteor.subscribe('sub2');
Tracker.autorun(function() {
if (Meteor.userId()) {
// Add subscriptions which should be loaded globally but only
// after the user logs in.
Meteor.subscribe('sub3');
}
});
var PostController = RouteController.extend({
waitOn: function() {
// Use subman only if we want to cache the subscription between routes.
// Note that in the case of sub1, the previous handle will be reused
return [
subman.subscribe('sub1'),
subman.subscribe('sub4'),
Meteor.subscribe('sub5')
];
}
});
It's a matter of preference, but I think the simplest answer is that you can still use Iron Router's subscriptions option whilst centrally subscribing:
Subscription file:
Subscriptions = {
elements: Meteor.subscribe('elements'),
...
};
Router:
subscriptions: function() {
return Subscriptions.elements;
}
All the router needs is the handle which is returned by Meteor.subscribe, it doesn't actually have to do the subscribing. If you store these handles in a (sensibly named) global object, you can then pass them to the relevant router function when you need them.
First, install IronRouter enter; at the command prompt, enter "meteor add iron:router"; doing so will add that routing package.
So that your "raw" or base URL doesn't throw a routing exception, add this to the top of your .js file (above/outside the "isClient" and "isService" blocks):
Router.route('/');
Add a template or two (or more) to your project's .html file (you can use Notepad or Notepad++ for this, but I recommend the free (and "hackable") Atom editor, from Github, with the Meteor helper packages added. The project will be in a subfolder of whatever folder you were in when you entered the "meteor create" command. To download the Atom Editor, click this.
BTW, the video of the futuristic/pastistic coding family on the Atom Editor site is a gas, man!
Getting back to adding templates; as an example, here are a couple of templates I added:
<template name="garrapatabeach"><p>Here's a picture of Garrapata Beach; early morning;
long exposure.</p>
<p class="location">Garrapata Beach.Garrapata State Park.Big Sur.Monterey County.California</p>
<img height="600" src="/images/garrapataBeach.jpg" width="900" />
</template>
<template name="garrapataturnout"><p>Here's a picture from the first Garrapata turnout;
long exposure</p>
<p class="location">First Turnout.Garrapata State Park.Big Sur.Monterey County.California</p>
<img height="600" src="/images/GarrapataTurnout1.jpg" width="900" />
</template>
Now, add routes for those templates in the same part of the project's .js file where you added the "home" ("/") route in the .js file, so that it looks like this (the routes match the template names):
Router.route('/');
Router.route('/garrapatabeach');
Router.route('/garrapataturnout');
Note: This supposes you have added a "public" folder to your project, and an "images" folder beneath that, and that you have images with the names indicated in that images folder. If you want to "play along," you can download those images from dplatypus.meteor.com/garrapatabeach and dplatypus.meteor.com/garrapataturnout; otherwise, simply change the names of the jpgs and use your own images.
You will now be able to view your templates by navigating to the links given above (to run it, simply enter "meteor" at the command prompt while in the project's folder, and navigate to localhost:3000). However, to make it "more better" (so that the main page is not naked as a jaybird), we'll put some anchor tags/links in the main template, like so:
<body>
{{> main}}
</body>
<template name="main">
Garrapata Beach
Garrapata Turnout
</template>
Now the links will display when you navigate to localhost:3000, and clicking on them will route you to the appropriate page/template. To continue, just add another template, along with its corresponding routing entry and anchor tag/link.
I've got a very simple template problem going on that appears to be similar to this guy's problem, though I've tried to build a simple example to demonstrate the problem and hopefully have someone explain to me how to fix or work around it.
Although as I'm doing some online research, it may be that the official documentation is out of date with the code. The reason I haven't bought into accepting that just yet is that the problem seems to have existed for a while, the dates on such articles in forums appears to be fairly old, and there's talk of it being fixed. There's also talk the feature is gone. What's the new way, if there is one?
I'm using Meteor 0.9.0.1 with iron:router 0.9.1. Specifically, I set up my project like this:
$ meteor create ironTest
$ cd ironTest
$ meteor add iron:router
$ meteor
Pointing my browser at http://localhost:3000/ as instructed, shows the default project. So far so good.
Now make ironTest.html contain this:
<body>
<h1>Before</h1>
{{renderRouter}}
<h2>Afterward</h2>
</body>
<template name="hello">
Hello Template
</template>
<template name="goodbye">
Goodbye Template
</template>
Make ironTest.js contain this:
Router.configure({
autoRender: true // we will experiment with this Boolean shortly
});
Router.map(function () {
this.route('hello');
this.route('goodbye');
});
If you go to the routes http://localhost:3000/hello and http://localhost:3000/goodbye, you'll see the templates correctly render as expected and documented, appended to the <body> element, so it appears after the <h2> element.
According to the current documentation for iron:router, one should be able to set the autoRender property to false, and the template should no longer be appended to the <body> element, but rather be injected where the Handlebars (okay, Spacebars) element {{renderRouter}} is, that is, between the <h1> and <h2> elements.
When I try this, visually it doesn't do anything. Opening a JavaScript Console to look at errors shows none. Although, by deliberately going to an invalid route it will show a missing template router exception, showing the routing code is indeed working.
Does anyone know how to coerce the code above into working?
For the curious, I've got a working simplistic equivalent that might be of use to others working this problem.
This new ironTest.html uses a template (for a layout) with no <body>:
<template name="main">
<h1>Before</h1>
{{> yield}}
<h2>Afterward</h2>
</template>
<template name="hello">
Hello Template
</template>
<template name="goodbye">
Goodbye Template
</template>
This ironTest.js instead uses a layout:
Router.configure({
layoutTemplate : 'main'
});
Router.map(function () {
this.route('hello');
this.route('goodbye');
});
It's worth an aside that this solution doesn't work for me, as I don't want a global layout, and have concern that riddling layouts in the route themselves is a tighter coupling than desired for my purposes.
I'm currently looking for a way to dump debugging log information from the Router as it performs transitions, but that's another story.