I have my iron-router code in my top-level lib directory in a file called routes.js. In that file I make calls to the front-end Session variable, specifically inside my onBeforeAction method like so:
Router.map(function () {
this.route('homeTemplate',{
name: 'homeTemplate',
where: 'client',
path: '/',
load: function() {
$('html, body').animate({
scrollTop: 0
}, 400);
return $('.content').hide().fadeIn(1000);
},
waitOn: function() {
return Meteor.subscribe(COLLECTION_NAMES.PlayerCollection.value);
},
onBeforeAction: function(){
Session.set('active_menu_option','home');
this.next();
},
onAfterAction: function(){
},
data: function(){
return {active_menu_option: {'home':'active'}};
},
yieldTemplates: {
'jumbotronTemplate': {
to: 'jumbotron'
}
}
});
});
Is it kosher to call/use the Session variable in this setting? I seem to be getting wonky/non-deterministic behavior.
I'm not sure about the Session usage, but if all you want is setting active menuitems depending on route location, then maybe this will help: meteor add zimme:iron-router-active
Usage:
<nav>
<ul>
<li class="{{isActiveRoute regex='dashboard'}}">...</li>
<li class="{{isActiveRoute regex='dashboard|index'}}">...</li>
<li class="{{isActiveRoute regex='users' className='on'}}">...</li>
<li class="{{isActivePath regex='products'}}">...</li>
{{#if isActiveRoute regex='index'}}
<li>...</li>
{{/if}}
<li class="{{isNotActiveRoute regex='dashboard'}}">...</li>
</ul>
</nav>
This helper uses regex which means strings like this will work too.
'^dashboard$' // Exact match for 'dashboard'
'^product' // Begins with 'product'
'list$' // Ends with 'list'
Related
I'm building a SPA using vue.js, I need to assign a div background-image referencing something in the following path:
I'm trying to reference src/assets/img/firstCard.jpg but for some reason it doesn't shows the image, this is how I'm binding the image:
HTML:
<a class="card">
<div
class="card__background"
v-bind:style="secondCard">
</div>
<div class="card__content">
<p class="card__category">Gratuita</p>
<h3 class="card__heading">Ademas en diferentes plataformas.</h3>
</div>
</a>
JS:
<script>
export default {
data () {
return {
thirdCard: {
'background-image': require('#/assets/img/firstCard.jpg')
},
secondCard: {
'background-image': require('#/assets/img/firstCard.jpg')
},
firstard: {
'background-image': require('#/assets/img/firstCard.jpg')
}
}
}
}
</script>
Thank you all for your time.
You can try to make method or computed property:
getUrl (img) {
return require(`#/assets/img/${img}.jpg`);
}
then call that method in data object (for background-image you need to specify url):
data () {
return {
firstCard: {
'background-image': `url(${this.getUrl('firstCard')})`
}
}
},
I have the following pagination component.
If users adds remove items dynamically i.e via some ajax call, how do i ensure the correct active or disabled classes are applied on the pagination links?
For example if the user is currently on the last page which only has 1 item, if the user deletes that item, the pagination links re-render but then i lose the active disable class becuase that page no longer exists. i.e. the links should update to move the user to previous page.
<div class="comment-pager ">
<div class="panel panel-default panel-highlight no-border ">
<div class="panel-body padded-5">
<nav v-if="totalItems > pageSize">
<ul class="pagination">
<li v-bind:class="[currentPage == 1 ? disabled : '']">
<a v-on:click.prevent="previous()" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li v-bind:class="[currentPage == pages ? active : '']" v-for="page in pages" v-on:click.prevent="changePage(page)">
<a>{{ page }}</a>
</li>
<li v-bind:class="[currentPage == pages.length ? disabled : '']">
<a v-on:click.prevent="next()" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['totalItems', 'pageSize']
data: function () {
return {
currentPage: 1,
pages: [],
}
},
watch: {
totalItems: function () {
var pagesCount = Math.ceil(this.totalItems / this.pageSize);
this.pages = [];
for (var i = 1; i <= pagesCount; i++)
this.pages.push(i);
}
},
methods: {
changePage: function (page){
this.currentPage = page;
this.$emit('pageChanged', page);
}
previous: function (){
if (this.currentPage == 1)
return;
this.currentPage--;
this.$emit('pageChanged', this.currentPage);
}
next: function () {
if (this.currentPage == this.pages.length)
return;
this.currentPage++;
this.$emit('pageChanged', this.currentPage);
}
},
}
</script>
<paginator v-bind:total-items="totalItems" v-bind:page-size="query.pageSize" v-on:pageChanged="onPageChange"></paginator>
There is no complete equivalent to ngOnChanges() in vue.
ngOnChanges() is a lifecycle hook which takes in an object that maps each changed property name to a SimpleChange object holding the current and previous property values.
If you want the lifecycle hook that gets invoked after every change in data and before re-rendering the virtual DOM then you should be using beforeUpdate() hook.
But as in ngOnChanges() you can't get the hold of which property is updated or what is it's oldvalue or newValue is.
As mklimek answered you can set up watcher on the properties you want to watch for changes.
In watcher you get what the oldValue is and what it's changed new value is
new Vue({
el:'#app',
data:{
prop1: '',
prop2: '' // property to watch changes for
},
watch:{
prop#(newValue, oldValue){
console.log(newValue);
console.log(oldValue);
}
}
});
EDIT
For your case you do not need a watcher. You can setup the pages[] property as a computed property:
computed:{
pages(){
var pageArray = [];
var pagesCount = Math.ceil(this.totalItems / this.pageSize);
for (var i = 1; i <= pagesCount; i++)
pages.push(i);
}
return pageArray;
}
computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed in your case the props
totalItems and pageSize
Now you can use the pages computed property as normal data property
You probably want to use watch property of a Vue instance.
var vm = new Vue({
data: {
count: 1
},
watch: {
count: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
})
I always wanted to learn NodeJS to be able to run the same code on server and client side.
I am using NodeJS with Express and EJS.
So. I have a .ejs page with lot's of HTML, JS, CSS and a small bit with template. For the sake of justice let it be like this:
the_list-->some.ejs
<ul>
<% for(i=0;i>the_list.length;i++) { %>
<li>the_list[i]</li>
<% } %>
</ul>
After some rendering on the server we have a perfect list.
So.
Now I want to rerender it on the client. I made some ajax request and now I have new items in the_list. What is the right way?
As per ejs templates documentation
var template = new EJS({
text: `
<ul>
<% for(i = 0; i < the_list.length; i++) { %>
<li>the_list[i]</li>
<% } %>
</ul>
`
});
var html = template.render({ the_list: data });
document.getElementById('list-wrapper').innerHTML = html;
<div id="output"></div>
<script src="/assets/js/ejs.js"></script>
<script>
let blogPosts = [
{
title: 'Perk is for real!',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-19'),
createdAt: new Date('2016-03-19')
},
{
title: 'Development continues...',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-18'),
createdAt: new Date('2016-03-18')
},
{
title: 'Welcome to Perk!',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-17'),
createdAt: new Date('2016-03-17')
}
];
var html = ejs.render(`<% for(let i = 0; i < posts.length; i++) { %>
<article>
<h2><%= posts[i].title %></h1>
<p><%= posts[i].body %></p>
</article>
<% } %>`, {posts: blogPosts});
// Vanilla JS:
document.getElementById('output').innerHTML = html;
</script>
download ejs.js or ejs.min.js from latest version
Sure, EJS works on the client. You can trivially keep the template in a string variable or apply EJS to user-provided input, but more likely, you'll want to store a template in a script (which can be in an external file) or use fetch to grab your template from another file on demand.
Using a template in a <script> is straightforward:
const people = ["geddy", "neil", "alex"];
const template = document
.querySelector("#template")
.innerText;
document.querySelector("#output")
.innerHTML = ejs.render(template, {people});
<!-- could be an external file -->
<script id="template" type="text/template">
<%= people.join(", "); %>
</script>
<div id="output"></div>
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
For fetch, I'll mock the response so it'll be runnable in a snippet:
// mock fetch for illustrative purposes;
// its response content would be another file
fetch = async url => ({text: async () => '<%= people.join(", "); %>'});
fetch("/your-template")
.then(res => res.text())
.then(template => {
const people = ["geddy", "neil", "alex"];
document.querySelector("#output").innerHTML =
ejs.render(template, {people});
});
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
<div id="output"></div>
If this seems like too much heavy lifting, you can bury the fetch in a helper function, or go a step further and pick an attribute for each URL, then plug everything in with a call to a library function you can abstract away from the main code. A simple example:
// mock fetch for illustrative purposes;
// its response content would be in other files
const responses = {
"/template.ejs": "<%= 42 %>",
"/other-template.ejs": "<%= 43 %>",
};
fetch = async url => ({text: async () => responses[url]});
[...document.querySelectorAll("[data-template]")]
.forEach(e => {
fetch(e.getAttribute("data-template"))
.then(res => res.text())
.then(template => {
e.innerHTML = ejs.render(template);
});
});
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
<div data-template="/template.ejs"></div>
<div data-template="/other-template.ejs"></div>
Either way, keep in mind that JS will run after the static HTML is parsed and the DOM loads. This means the data won't appear all in one fully-formed piece as when using EJS on the server. Network errors are possible.
See also using ejs partials from express client side. If you want to mock the include function, the problem is that the fetch call is asynchronous but the include function isn't. EJS offers an include callback that seems like it offers an opportunity to pull in an external file, but it's purely synchronous and won't await any promises you return. How to work around this best depends on your use case.
This should work, looks like your problem was the relational operator '>' because it will never output something.
<ul>
<% for(var i=0; i<the_list.length; i++) { %>
<li>
<a>
<%= the_list[i]%>
</a>
</li>
<% } %>
</ul>
When using routes, like (http://localhost:port/foo/bar), in the view, in my case an ejs, do i need to declare the full path in the view, to load css/js/images ?
Vide: https://github.com/poeticninja/hapi-ninja
hapi-ninja/server/base/index.js
...
{
method: 'GET',
path: '/foo/bar',
config: {
handler: function(request, reply){
reply.view('about', {
title: 'Super Informative About Page'
});
},
id: 'about'
}
},
...
ex:
foot.ejs
<!-- Include the JS -->
<% if (assets.js.length) { %>
<% assets.js.forEach(function(js){ %>
<script src="<%=js%><%=version.cache%>"></script>
<% }) %>
<% } %>
Cause, every time i try to load, it gets the relative path (http://localhost:port/foo/bar/js/script.js).
As described by the creator himself:
First solution:
#poeticninja commented 4 hours ago
In your assets file make the paths /js/script.js and not js/script.js. That will fix your problem.
Other 'solution':
In the assets.js
// assets to be used by the 'hapi-assets' module based on process.env.NODE_ENV
module.exports = {
development: {
js: ['js/jquery-2.1.4.js', 'js/bootstrap.js'],
css: ['css/bootstrap.css', 'css/bootstrap-theme.css', 'css/3-col-portfolio.css'],
host: 'http://development:PORT/'
},
production: {
// TODO: Add the resources minified.
js: ['js/jquery-2.1.4.js', 'js/bootstrap.js'],
css: ['css/bootstrap.css', 'css/bootstrap-theme.css', 'css/3-col-portfolio.css'],
host: 'http://production:PORT/'
}
}
and in the view:
<script src="<%=assets.host%><%=js%><%=version.cache%>"></script>
But, with the initial '/', is better.
I'm using express 3.0.x with hogan-express as template engine to build a single page application.
I would like to add a partial that will contain information that will help me to develop / debug the application. (I am also working with angularjs)
Depending on a variable somewhere in my app, like "devMode = true" I'd like to include this particular partial and it won't be included if "devMode = false".
Here are my files
index.js (routes):
exports.index = function(req, res) {
res.locals = {
template_engine: 'HoganJS'
}
return res.render(
'index',
{
partials:
{
head: 'head', foot: 'foot'
}
}
);
};
exports.partials = function(req, res) {
res.render('partials/' + req.params.entity + '/' + req.params.name)
}
index.html:
[[> head]]
<div class="navbar fixed-top">
<div class="navbar-inner">
<a class="brand" href="/">My app</a>
<ul class="nav">
</ul>
</div>
</div>
<div class="container">
<div ng-view></div>
</div>
[[> foot]]
(I changed {{ }} to [[ ]] to resolve conflict with angularjs syntax)
I'd like to include something like [[>dev]] before [[> foot]] depending on a boolean somewhere in my app.
Thank you for any help:)