Link a div to a route meteor (Iron Router) - javascript

I would like to "associate a route to a div" with Iron Router without refreshing/changing page. I'll try to be as clear as possible.
I got a div that's not displayed when you arrive in the page (ex: /home), but already exists.
When you click on something (box, text, ...), the url changes ( /home/box1) and the div appears with some data I'd have given to the route before.
But I would like to be able to do that without having to refresh the page, just to see the div appears in the same page.
So, is it possible with Iron Router and how ?
Thanks for your help.

If you are trying to display the same template on both routes, but only show the div on one of them, you can pass data into the template. The render method takes a second argument which is an object. You set the data property on that object to be a function that returns an object with all of your data values. You can then access them from the template as if they had been defined when inserting the template.
Router.route('/home', function() {
this.render('Home');
});
Router.route('/home/box1', function() {
this.render('Home', {
data: function() {
return {
showDiv: true
}
}
}
});
<head>
</head>
<body>
</body>
<template name="Main">
<p>Always showing</p>
{{#if showDiv}}
<p>Only showing when on /home/box1 route</p>
{{/if}}
</template>
Note: This will not preserve the page you are on regardless of where you are. I.E. If you wanted to do this on the /other and /other/box1 routes. If this is the behavior you're looking for I can help you with that as well.

Related

How to set menu item to active in sveltekit app

I have been working on a sveltekit application and recently ran into an issue with my main navigation menu. when the application is loaded or refreshed the current menu item that corresponds with the current URL is not set to active. (i.e. menu item 'Home' would have URL of '/') I had a hard time finding anything useful online demonstrating how to set this up, but I have a working solution and thought I'd document it for others. It is very simple at the moment.
I have a nav.json file where I define any menus within my application, making it easy to organize menus. I import that at the top of the file and store it in an array for use later.
import menuObj from '../nav/nav.json';
let sidebar = menuObj.menus[0].items;
onMount() is just getting the current path when the nav component is loaded, and only runs once for me.
then - using <svelte:window on:click={handlePageClick}/> we can listen to any clicks on the page.
In the handlePageClick(e) method we pass the on:click event. Using that event we can capture the current pathname of the URL. this allows us to check the pathname against our item.path which is defined in the nav.json file I've imported and extracted using the #each method in our html. On the <a> we can set the class to active if the items route matches the current path.
here is the full implementation
<script>
import menuObj from '../nav/nav.json';
import {onMount} from "svelte";
let path;
let sidebar = menuObj.menus[0].items;
onMount(() => {
path = window.location.pathname;
})
function handlePageClick(e) {
path = e.view.location.pathname;
}
</script>
<svelte:window on:click={handlePageClick}/>
<nav class="sidebar">
{#each sidebar as item, i}
<a href={item.route} id="sidebar-menu-item-{item.id}"
class={path === item.route ? "item active" : "item"}
>
<i id="sidebar-menu-item-{item.id}-i" class="{item.icon}"></i>
</a>
{/each}
</nav>
I haven't found any drawbacks to this yet. I will update this question if I find anything.
It's not really a question, but I will add an answer anyway because your method is not necessary, SvelteKit already has a built in system for this:
<script>
import { page } from '$app/stores';
</script>
<a href={item.route} class="item" class:active={$page.url.pathname == item.route}>
...
</a>
page is a store like object that among other things hold the current path, it will automatically be updated with the new path
as a bonus:
class:active is an easy shorthand, removing that ternary expression there.

How to add dynamic parameters/variables to templates rendered with ng-bind-html or custom directive (inside ng-repeat)

EDIT: Here is the solution sample based on the first answer. You're still welcome to offer further suggestions and fill some gaps in my knowledge (see comments).
plnkr.co/edit/mqGCaBHptlbafR0Ue17j?p=preview
ORIGINAL POST
Hello all Angular heads,
I’m trying to build a tool for building simple web pages from a set of predefined elements. The end user designs the layout by choosing which elements appear in which order (there would be a selection of maybe 20-30 different elements). An example layout:
Heading
Paragraph
Paragraph
Subheading
Barchart
Paragraph
…and so forth
Under the hood, there would be an array of Javascript objects:
var page_structure = [
{type: ”heading”, content: ”The final exam - details” },
{type: ”paragraph”, content: ”Bla bla bla bla bla…” },
{type: ”paragraph”, content: ”Bla bla bla bla bla…” }
…
{type: ”bar chart”, y: ”Grades”, colour: ”blue” }
…
];
The end user inputs parameters like content, variable, colour, etc, for each individual element.
THE PROBLEM
I can’t figure how to draw these elements on the page so that the dynamic parameters for each element are properly included.
Each element type (heading, paragraph, bar chart, other possible elements) has it’s own HTML template, which has to be able to dynamically display user-defined parameters. I’m thinking I need something like this:
var templates = {
heading: ”<h1>USER_CHOSEN_PARAMETER_HERE</h1>”,
paragraph: ”<p>USER_CHOSEN_PARAMETER_HERE</p>”
…
};
I’m using Angular’s ng-repeat directive to draw each element. I have tried using either ng-html-bind…
<div ng-repeat="x in page_structure track by $index">
<div ng-bind-html="templates[x.type]"></div>
</div>
…or a custom directive, which I call mycomponent:
<div ng-repeat="x in page_structure track by $index">
<div mycomponent component='x'></div>
</div>
Both methods work just dandy when there are no user defined parameters in templates. I can’t, however, wire up the parameters. With ng-bind-html, I have tried using an expression markup like this:
var templates = {
heading: ”<h1>{{x.content}}</h1>”,
paragraph: ”<p>{{x.content}}</p>”,
…
};
(I actually define templates in app controller constructor, so it’s $scope.templates = { bla bla } to be precise.)
This is just showing curlies and ”x.content” in the actual web page. How do I refer to a dynamically variable parameter inside ng-html-bind template, or is it even possible?
I also tried the custom directive route, defining the directive as
.directive('mycomponent', function() {
return {
scope: { component: "=" },
template: templates[component.type]
}; )
This was even more messed up, since I actually couldn’t figure out how I should even try to refer to a dynamic parameter here inside the template. So I apologise my inability to offer a meaningful example of what I tried to do.
Any help or working examples of the methods I should use here are greatly appreciated. I’m happy to provide more details if needed (this is my first post in SO, so I’m still trying to get the jist of how to flesh out the questions).
I suggest using ngInclude, in which you can have a funciton that check for the proper template to load based on the passed page structure.
<div ng-repeat="structure in page_structure">
<div ng-include="getMyTemplate(structure)"></div>
</div>
$scope.getMyTemplate = function(structure) {
switch (structure.type) {
case 'heading':
return '/Views/Heading.html' // or id of a pre-loaded template
case 'paragraph':
return '/Views/Paragraph.html' // or id of a pre-loaded template
}
}

How to re-assign template in emberjs component?

What I want is when I click on a link of a component, the template will change dynamically with its subsequent state(some other html codes).
What I have done is in init process, every component's template could be displayed dynamically. When I click link Add, Invite or Accept, the property status of component was changed accordingly and function statusChanged is triggered but component's template was still not changed.
(I've spent hours on this and I'm gonna crazy.)
Here's the code on emberjs.jsbin.com:
http://emberjs.jsbin.com/laxeqigepu/2/
As the outputs, html has changed and could be logged out. But why layout is not changed?
console.log( html );
this.set('layout', Ember.Handlebars.compile(html));
Try putting these cases in your template:
//templates/components/contact-invitation.hbs
{{#if zeroStatus}}
{{#if uidPositive}}
</i> Add
{{#else}}
</i> Invite
{{/if}}
{{#else}}
{{#if wait}}
Wait
{{#else}}
...
{{/if}}
{{/if}}
//components/contact-invitation.js
import Ember from "ember";
export default Ember.Component.extend({
zeroStatus: function() {
return this.get('status') === 0;
}.property('status'),
uidPositive: function() {
return this.get('uid') > 0
}.property('uid')
//etc.
});
This is a little awkward because Handlebars lacks an {{# else if }} construct, and also because I don't know much about your domain -- my guess is there's probably a better solution than zeroStatus, oneStatus, and so on. This does seem to be the idiomatic solution in Ember/Handlebars, though: define properties in the class, and use conditionals based on those properties in the templates.

I want my action to alter the DOM element it is attached to

By alter I mean changing color/font size/ etc.
So this is the action in my controller
actions {
changeColor: function() {
this.$().css('background-color', '#f1f1f2'); //error!
}
}
<span {{action 'changeColor' this}} > </span>
this.$() is causing an error. What should I use instead?
The action must be in the controller.
When you say {{action 'someAction' this}} this from within the template in this sense will refer to the model/content of the template not the DOM element of the action.
Here is another suggestion. I am not sure if this is best practice.
Lets say we are working within the index template and have a span with a action:
//index template
<span>Change my background</span>
Now the index template has a view that is backing it called the IndexView. Within that View we can use one of the DOM events. in the code below we use the click Dom event which will send along an event with which we can use to the grab the target and then send to controller.
//App.IndexView
click: function(evt) {
//get the evt target
var target = evt.target
//send a action the controller
this.get('controller').send('handleBackgroundChange', target);
}
Now in the indexController:
//indexController
actions: {
handleBackgroundChange: function(target) {
$(target).css({'background': 'green'});
}
}
Again this works but I am not sure it is a good idea.
If you want to do that change with jQuery, you can do something like the following:
// You'd have to know the selector of that view or the css class wrapping it
// in order to do this in the controller, since the "this"
// is not a pointer to your view at this point (I think)
Ember.$('selector goes here selector').css({'color': '#00F'});
It would be much easier form a view instead of a controller, since you could do things like (note the this keyword):
this.$().css('color': '#0F0');
This code would work in a view with the this keyword and without the need for a selector, since it is a pointer to the view at that moment. So this.$() in a view would essentially mean $("#ember416") where "ember416" is the id Ember assigned to that view.
Another way you can do this would be through a View or Component implementation so you could use attributeBindings for style, and have a lot more control over what is going on. You can apply rules to specific css properties based on the business reqs and take advantage of the binding to always recompute the style. With this approach you don't need to use $.css() to change the styles, as it gets built and applied for you.
So... say you create a style property watches other properties (e.g. color, border, size, etc) within your class and ultimately compose the style string. This way you can program each property and then bind the result of a given combination directly to your view via attributeBindings. Example:
// JS
App = Em.Application.create();
App.LeToggleButtonComponent = Ember.Component.extend({
// I want an input
tagName: 'input',
// of the type button
type: 'button',
// that has a `clicked` flag
clicked: false,
// When I change the `clicked` flag, I want my to force any
// properties depending on `clicked` to re-evaluate their values
changeColor: function() {
this.toggleProperty('clicked');
}.on('click'),
// Then I want the color attribute to re-evaluate itself
// based on the `clicked` property, switching it from red to blue
color: function() {
return "color: %#;".fmt((this.get('clicked')) ? "#00F" : "#F00");
}.property('clicked'),
// And I also want the border attribute to re-evaluate itself
// based on the `clicked` property, switching it from red to blue
border: function() {
return "border: %#;".fmt((this.get('clicked')) ? "1px solid #00F" : "1px solid #F00");
}.property('clicked'),
// Then I want to combine all css related properties
// into a single string, composing the style of this component
style: function() {
return '%#1%#2'.fmt(this.get('border'), this.get('color'));
}.property('border', 'color')
// finally, I want to bind some attributes to the view
// so the styles get updated automatically when
// any of them change. The real important one, is the `style` prop
attributeBindings: ['type', 'value', 'style'],
});
// Handlebars
<script type="text/x-handlebars" data-template-name='index'>
{{le-toggle-button value='Click Here'}}
</script>
<script type="text/x-handlebars" data-template-name='application'>
<h2>button test</h2>
{{outlet}}
</script>
(see jsbin)
An argument can be made (and I'd agree) that for the majority of the scenarios, one should use classes instead of inline styles. This proposed approach would be good when you have very specific rules and/or complex style combinations that could potentially create duplicated/hard-to-maintain css classes.
Update
I wanted to satisfy my own curiosity on how to send and handle css changes through a controller or route and went on reading. I came up with a second solution that allows for changes made directly in the controller or route:
// JS
App = Ember.Application.create();
App.Router.map(function() {
this.route('other');
});
App.IndexRoute = Em.Route.extend({
actions: {
changeColor: function(selector) {
console.log("Index > Ember.$('%#')".fmt(selector), Ember.$(selector));
// now I can simply call jQuery to
// change the style of the view in
// this particular route
Ember.$(selector).css({
'color': '#F00',
'font-weight': 'bold'
});
}
}
});
App.OtherRoute = Em.Route.extend({
actions: {
changeColor: function(selector) {
console.log("Other > Ember.$('%#')".fmt(selector), Ember.$(selector));
// or on this one
Ember.$(selector).css({
'height': '80px'
});
}
}
});
App.LeOtherButtonComponent = Ember.Component.extend({
tagName: 'input',
type: 'button',
target: Ember.computed.alias('route'),
// within the action handler, you can
// call this.triggerAction, passing the
// action name, context and target.
// in this case, i didn't pass the target
// with the hash, but left as the default
// target of the view, and also changed
// it's default value to the route just
// as an example
changeColor: function(e) {
var selector = '#%#'.fmt(this.get('elementId'));
this.triggerAction({
action: 'changeColor',
actionContext: selector
});
}.on('click'),
attributeBindings: ['type', 'value' ]
});
// Markup
<script type="text/x-handlebars" data-template-name='index'>
{{le-other-button value='Click Here - Index'}}
</script>
<script type="text/x-handlebars" data-template-name='other'>
{{le-other-button value='Click Here - Other'}}
</script>
<script type="text/x-handlebars" data-template-name='application'>
<div class="more">
{{#link-to 'index'}}Index{{/link-to}} |
{{#link-to 'other'}}Other{{/link-to}}
</div>
{{outlet}}
</script>
(see jsbin)
I don't really know if I like this solution because it's really easy to go out of sync with inline styles versus whatever state your view/component is supposed to be in and look like at that point. The approach on the first hypothetical component is good because you can tell exactly what is going to change, without the need of using jQuery. On the other hand, you have to create a computed property and add it as a dependency of the style each time you want to add a new style attribute in that string, making the 2nd hypothetical component look more flexible, allowing each controller/route to implement its own changeColor, and allowing you to inject an animation or another type of operation in that component or view. But I think it sorta leaves it open for imperative style changes (not tied to any logic/rule, which can become difficult to maintain).
I'm sure there's a better way to do this :P
The best approach is to bind the DOM node's class attribute to a controller property, like this:
<div {{bind-attr 'class' myClass}}>blah blah</div>
and in your action:
actions: {
changeColor: function() {
this.set('myClass', 'someCssClass');
}
}
If you'd rather deal with styles directly (not recommended), then you could do the same thing with the style attribute binding.

Best way to find (i.e. set CSS) on DIV in table created by {{#each}} in Meteor?

I have this code working but wondering if I'm doing it the long way. Is there a more efficient way? I was hoping for a 1 line of code solution.
I want to change the color of a single DIV in a table created from a {{#each}} in Handlebars template. I came up with this callback, so every time isSharedByMe (which is a field in a Collection) becomes true, the templates reactivity sets the CSS color to green:
Template.showRepost.rendered = function () {
if (this.data.isSharedByMe) {
$( this.find('.repost') ).css( {'color': 'green'} );
}
return; // I like to explicitly show a return value so people know
// I'm not returning any specific value on purpose.
// Not sure if this kills efficiency (separate topic).
};
The Handlebars template is simple, I call this as a partial from my main template that has the {{#each posts}} call which produces the table:
<template name="showRepost">
{{show_repost_txt}}
</template>
{{show_repost_txt}} just shows returns text like, "Share", or "Already Shared".
This code above works, but what I was hoping for was to have 1 jQuery type line in my show_repost_txt helper to set the CSS color at the same time as changing text to "Already Shared".
But I could figure out how to set ONLY the current class .repost, since this.find is not available in custom template helpers, but is available in the .rendered callback (along with event handlers). I tried this jQuery with no luck:
Template.showRepost.show_repost_txt = function () {
if (this.isSharedByMe) {
// Type $(this) in the Browser console
// (it's the jQuery call to the DOM window object,
// I just can't figure out how to get the specific DIV I need.
$(this).find('.repost').css( {'color': 'green'} );
return "Already shared.";
}
};
Can you just make the css tag reactive like this?
<template name="showRepost">
{{show_repost_txt}}
</template>
and then add this to your css:
// Returns any extra classes to be applied to the link
Template.showRepost.extraClasses = function () {
if (this.data.isSharedByMe) {
return "theColorGreen"; // you will also need to add a 'theColorGreen' class to your .css file that matches this
}
return "";
};

Categories

Resources