Changing color of link dynamically in emberJS - javascript

This is my app.hbs file, when i click the link it should be highlighted,Please help me how to do this.I am new to Ember and cannot find a clear solution to it. I am having ember version 2.18.
{{#link-to 'adduser' id="addlink" }}MANUAL ADD {{/link-to}}</div>
<br>
{{#link-to 'csvadd' class="button"}}
CSV ADD
{{/link-to}}

You should create a component that can handle the state of clicked/active or not.
Your template can look something like this:
<span {{action "transitionToRoute"}}>
<a>{{linkText}}</a>
</span>
Your js file looks like this:
import Component from '#ember/component';
import { inject as service } from '#ember/service';
export default Component.extend({
router: service(),
classNameBindings: ['highlighted'],
highlighted: false.
actions: {
transitionToRoute() {
this.set('highlighted', true);
this.get('router').transitionTo(this.get('route'));
}
}
});
And, if you call your component hughlightedLink you would use it like this:
{{highlighted-link route="addUser" linkText="Add User"}}
Of course you would have to define the css highlighted class to style the span like you want to

Related

How to use an image as a button in Vue.js?

I'm trying to make a login button as a single-file-component in Vue.js (it's a Rails app with a Vue.js front-end). If you click this button, it's supposed to take you to the an external provider's login page.
How can I use an image as a button? I'm guessing you use v-on:click for the actual redirect, but I'm stuck there.
Right now, this code below shows a hardcoded button that looks like img(src="../assets/img/login_button.png"). You can click on it, but that's obviously not what I want. I want to show the actual png image, not the path.
// LoginButton.vue
<template lang="pug">
#login-button
<button v-on:click="redirect_to_login">img(src="../assets/img/login_button.png")</button>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
#Component
export default class LoginButton extends Vue{
redirect_to_login():void{ // I haven't written this method yet
}
}
</script>
Is there any reason you can't just use normal HTML image inside your button? I haven't used pug before.
<button v-on:click="redirect_to_login"><img src="../assets/img/login_button.png" /></button
Though since you're using Vue and not an actual HTML form you might not even need a button you could just add the click binding to the image instead
<img src="../assets/img/login_button.png" v-on:click="redirect_to_login" />
I am not familiar with pug, so I don't know what the correct syntax you'll need is. But you can use the <router-link> tag to set the route. For example (using Vuetify)
<router-link to="/">
<v-img src="/path/to/img.gif"/>
</router-link>
Either you can use:
<a #click="Redirect">
<img src='IMAGE_SRC' />
</a>
or
<img #click="Redirect" src='IMAGE_SRC'/>
new Vue({
el: '#app',
methods:
{
Redirect()
{
window.location.href = "https://jsfiddle.net/";
//or
//this.$router.push('LINK_HERE'); // if ur using router
}
}
})
Demo LINK:
https://jsfiddle.net/snxohqa3/5/

Vuejs template inheritance

How can I use template inheritance (Like what jade has, extends file.jade and then the blocks with the same name would be overwritten)?
I know that I can do everything with composition, but for components like footer and header which appear on every single page except one or two (e.g.login page) I must write them on every single component. In my app I have a two level navigation and it seems painful to repeat them on every one of those child components :(
I know that I can use jade and then inherit a jade file within my components, but it seems wrong because I would have some jade and some Vue files, is there any other way to do this?
// Component.vue
<template lang="jade">
extends ./StandardLayout
block content
router-view
</template>
// StandardLayout.Vue
<template lang="jade">
div
navbar
div.container
div.spacer
div.row
block content
<template>
What I've settled for, is a layouts folder filled with jade layouts and I use them to extend my components. I used vue-cli with webpack template.
In the most general case if you have to repeat the same HTML over and over, one option you could use is <partial>s.
<partial name="header"></partial>
<div>My content content</div>
<partial name="footer"></partial>
Where you declare partials as
Vue.partial('header', '<h3>This is the title: {{title}}</h3>')
Vue.partial('footer', '<footer>Mini footer</footer>')
However if you are building a Single Page Application the strategy you could follow is to simply have a header and a footer around your <router-view>, here is a jsfiddle that demonstrates how to do.
https://jsfiddle.net/gurghet/vdqutw2y/
<header><h1>
My title: {{title}}
</h1></header>
<p>
<a v-link="{ path: '/foo' }">Go to Foo</a>
<a v-link="{ path: '/bar' }">Go to Bar</a>
</p>
<router-view></router-view>
<footer>Such footer, many links, wow!</footer>
If you know Chinses, please look it
// Base Component
<template>
<div class="base-thing special-class">
<Button />
</div>
</template>
<script>
import Button from './ButtonClick'
export default {
components: { Button }
}
</script>
// Inheriting Component
<script>
import BaseComponent from './BaseComponent'
import Button from './OtherButton'
export default {
extends: BaseComponent
components: {
Button
}
}
</script>
The Button of Child Component will be replaced OtherButton. We can do something in the OtherButton

using Ember-data with JSONApi and opening page in a new tab

I have a page that has pictures (index.js) and when you click a picture, a detail page with bigger version of the picture and its content (pic.js) opens. When I was using hard-coded data, I created a service and put the data in it. By this way, the model hook wasn't skipped when I click a picture. I did it because my links are dynamic {{#link-to}} helper and I saw that model hook gets skipped when you have it. But now, I need to use JSON api to get the data from an URL, when I do it in the index.js there is no problem with displaying it but when I try to open any link in new tab or paste a link in URL bar, model hook doesn't work in pic.js.
//routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.$.getJSON('My jsonApi Url');
}
});
I read that I need to use ember-data in order to fix it. I created a model "news-list" and put attributes in it. Also I created an adapter and take the code which I call API from index.js and put there.
//adapters/application.js
import JSONAPIAdapter from 'ember-data/adapters/json-api';
import Ember from 'ember';
export default JSONAPIAdapter.extend({
model(params){
return Ember.$.getJSON('My jsonApi Url',params.NewsUrl);
}
});
//templates/index.hbs
{{image-list model=model.Data currentPos=currentPos }}
{{outlet}}
//templates/components/image-list.hbs
{{#each model as |pic|}}
<div>{{#link-to "pic" pic}}
<p class="info">{{pic.Title}}</p><br/>
<img src={{pic.Image}} width="300">
{{/link-to}}</div> {{/each}}
{{yield}}
//routes/pic.js
import Ember from 'ember';
export default Ember.Route.extend({
activate: function() {
this._super(...arguments);
window.scrollTo(0,0);
},
model() {
//return this.store.findAll('news-list');
}
});
//templates/pic.hbs
<p class= "back">{{#link-to 'index'}}Home Page{{/link-to}}</p>
<p class="detail"><img src="{{model.Image}}" width="600" ></p>
<p class="content"><br/><br/>{{model.Content}}</p><br/><br/>
<p class= "back">{{#link-to 'index'}}Home Page{{/link-to}}</p>
{{outlet}}
I tried to use return this.store.findAll('news-list'); in the pic.js but then all I see was a blank page when I click a picture.
I guess there is something I'm missing. I can't use ember-data properly. How can I fix it?

How to open a route in a modal dialog in ember.js?

We have a requirement of opening a modal dialog containing a route or a component. We are looking for some modal components, and saw ember-bootstrap's modal is useful.
So,
How can we open any route as a modal dialog ? (If parent route decides a route to be open in a modal, the child route should be open in modal.)
Can we create a service, to pop up a modal dialog? Such as: ModalDialogService.popup(title, bodyComponent, commitHandler, cancelHandler); or ModalDialogService.popup(title, routeName, commitHandler, cancelHandler); And how can we do this without violating the Data Down Action Up principle?
Is there any guide, document, tutorial or npm package for implementing modals in ember.js?
UPDATED:
What I need is to open any of the current routes in a modal. For example, in a given route hierarchy:
-module1
|-module1.query
|-module1.add
|-module1.update
|-module1.delete
Currently module1.query has transitions to others. But I want to give an option to the module developers to open any of the add, update, delete routes in a modal. So that query route doesn't lose its state, when an add operation finished.
Also we have some services used by components. At some conditions, services need to display a modal that has a component.
You should be able to use a service and component similar to one below to achieve what you want.
Have a look at the twiddle for a demo of how this works exactly, and the code below for quick reference
Your route template could look something like this.
// templates/hasmodal.hbs
{{#bs-modal}}
Modal Content
{{/bs-modal}}
Your route hooks, with service injected
// routes/hasmodal.js
export default Ember.Route.extend({
modalNavigation: Ember.inject.service(),
activate(){
console.log('openingModal')
this.get('modalNavigation').openModal()
},
deactivate(){
console.log('closingModal')
this.get('modalNavigation').openModal()
},
actions: {
onClose(){
console.log('we want to close route')
}
}
})
Your bs-modal or relevant component
//components/bs-modal.js
export default Ember.Component.extend({
modalNavigation: Ember.inject.service(),
isOpen: Ember.computed.alias('modalNavigation.modalOpen'),
classNameBindings: ['isOpen:modalDialog:notOpen'],
actions: {
close(){
this.get('modalNavigation').closeModal()
}
}
})
The bs-modal component template
// templates/components/bs-modal
<div>
{{yield}}
</div>
<button class='close' {{action 'close'}}>Close Me</button>
Your Modal Service to manage state
// services/modal-navigation.js
export default Ember.Service.extend({
modalOpen: false,
openModal(){
this.set('modalOpen',true)
},
closeModal(){
this.set('modalOpen',false)
}
})
UPDATE:
updated twiddle
It basically nests routes that contain a modal underneath a route you want to preserve the state of and show behind the modal.
// router.js [truncated]
Router.map(function() {
this.route('module1',function(){
this.route('query',function(){
this.route('add')
this.route('update', { path: '/update/:item_id' })
this.route('delete', { path: '/delete/:item_id' })
})
})
// templates/modules1/query.hbs
Queried List {{link-to 'add item' 'module1.query.add'}}<br/>
<ul>
{{#each model as |item|}}
<li>
{{item.id}}-{{item.title}}
{{link-to 'u' 'module1.query.update' item}}
{{link-to 'd' 'module1.query.delete' item}}
</li>
{{/each}}
</ul>
{{outlet}}
// templates/module1/query/add.hbs
{{#modal-component isOpen=true onClose=(action "routeClosed")}}
<div>
Title:{{input value=model.title}}
</div>
<button {{action 'save'}}>Save</button>
{{/modal-component}}
Where all the other sub components follow the same modal wrapper principle

How to enhance a server side generated page with Aurelia.io?

I'm writing an app with some parts as SPA and some pages generated on server side for SEO. I've chosen Aurelia.io framework and I use enhance method to enable custom elements on my pages. But I can't find the best way to use aurelia specific template directives and interpolation on my server side page. Let's start with an exemple.
All of my pages contains a dynamic header. This header will be a custom element named my-cool-header. This header will load authentified user and display its name, or, if no user is currently authentified, a link to the signin will be displayed. The body of the page will be generated on server side and cached. So, we'll have something like that :
<html>
<body>
<my-cool-header>
<img src="logo.png">
<div
show.bind="user">${user.name}</div>
<div
show.bind="!user">Sign-in</div>
</my-cool-header>
<div>Cachabled content</div>
</body>
</html>
Then, my header will by defined by :
import {UserService} from './user';
import {inject} from 'aurelia-framework';
#inject(UserService)
export class MyCoolHeader {
constructor(userService) {
this.userService = userService;
}
async attached() {
this.user = await this.userService.get();
}
}
With the following template :
<template>
<content></content>
</template>
And this bootstrap script :
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.globalResources('my-cool-header');
aurelia.start().then(a => a.enhance(document.body));
}
In this configuration, the custom element is well loaded and instanciated. But, I can't access the viewModel of the node inside the <content> node. So, all the interpolation (${user.name}) and attributes (show.bind) are ignored. If I include a custom-element in my content template, it will be loaded only if it is declared as global in the bootstrap : the` tag is ignored.
I've found a workaround to be able to change the viewModel after reading the doc by setting a custom viewModel to enhance method and then, injecting it to my custom element class. Something like :
import {MainData} from './main-data';
export function configure(aurelia) {
const mainData = aurelia.container.get(MainData);
aurelia.use
.standardConfiguration()
.developmentLogging()
.globalResources('my-cool-header');
aurelia.start().then(a => a.enhance(mainData, document.body));
}
Custom element:
import {UserService} from './user';
import {inject} from 'aurelia-framework';
import {MainData} from './main-data';
#inject(UserService, MainData)
export class MyCustomElement {
constructor(userService, mainData) {
this.userService = userService;
this.mainData = mainData;
}
async attached() {
this.mainData.user = await this.userService.get();
}
}
And finally, if I change my template like that, it will work :
<html>
<body>
<my-cool-header
user.bind="user">
<img src="logo.png">
<div
show.bind="user">${user.name}</div>
<div
show.bind="!user">Sign-in</div>
</my-cool-header>
<div>Cachabled content</div>
</body>
</html>
I can't believe it is the right way to do because it's ugly and it does not resolve the problem of <require> tag. So my question is : What is the best way to do ?
Thanks to your clues, I found the solution!
Custom element need to construct its own template:
import {processContent, noView} from 'aurelia-framework';
#processContent(function(viewCompiler, viewResources, element, instruction) {
instruction.viewFactory = viewCompiler.compile(`<template>${element.innerHTML}</template>`, viewResources, instruction);
element.innerHTML = '';
return false;
})
#noView
export class MyCustomElement {
attached() {
this.world = 'World!';
this.display = true;
}
}
Then, in my view from server, we can interpolate and require custom elements!
<body>
<my-custom-element>
<require="./other-custom-element"></require>
<p
if.bind="display">Hello ${world}</p>
<other-custom-element></other-custom-element>
</my-custom-element>
</body>
I've wrote a decorator to help creating this kind of enhanced custom elements : https://github.com/hadrienl/aurelia-enhanced-template
Plus de détails en français sur mon blog : https://blog.hadrien.eu/2016/02/04/amelioration-progressive-avec-aurelia-io/
EDIT: <require> is not really working with this solution. I have to dig again :(
Change your MyCoolHeader's template from:
<template>
<content></content>
</template>
to:
<template>
<img src="logo.png">
<div show.bind="user">${user.name}</div>
<div show.bind="!user">Sign-in</div>
</template>
then change your server-generated page to something like this:
<html>
<body>
<my-cool-header></my-cool-header>
<div>Cachabled content</div>
</body>
</html>
Hope that helps. If this doesn't solve the problem or is not an acceptable solution, let me know.
Edit
After reading your reply and thinking about this a bit more I'm leaning towards removing the <my-cool-header> element. It's not providing any behavior, it only acts as a data loader, it's template is provided by the server-side rendering process and it's expected to be rendered outside of the aurelia templating system, there's no real need to re-render it. Here's what this approach would look like, let me know if it seems like a better fit:
<html>
<body>
<div class="my-cool-header">
<img src="logo.png">
<div show.bind="user">${user.name}</div>
<div show.bind="!user">Sign-in</div>
</div>
<div>Cachabled content</div>
</body>
</html>
import {MainData} from './main-data';
import {UserService} from './user';
export function configure(aurelia) {
const mainData = aurelia.container.get(MainData);
const userService = aurelia.container.get(UserService);
aurelia.use
.standardConfiguration()
.developmentLogging();
Promise.all([
this.userService.get(),
aurelia.start()
]).then(([user, a]) => {
mainData.user = user;
a.enhance(mainData, document.body);
});
}
To supplement Jeremy's answer, if you did change the template to:
<template>
<img src="logo.png">
<div show.bind="user">${user.name}</div>
<div show.bind="!user">Sign-in</div>
</template>
This content would be present when Aurelia processed the element and in the absence of a content selector, anything inside the custom element tags will be replaced by the template
If you then put your non-javascript content inside the custom element tags:
<my-cool-header>
<div>This stuff will be visible when JS is turned off</div>
</my-cool-header>
In the example above, in the absence of JS the div should still be there as Aurelia won't remove it from the DOM.
(This is of course assuming your server side tech doesn't mangle/fix the unknown HTML tags in the DOM for some reason when serving pages - which it probably won't since it would break Aurelia anyway)
EDIT:
The alternative you may be looking for is the #processContent decorator.
This allows you to pass a callback function that runs before Aurelia inspects the element.
At this point you could just lift the content between the custom element tags and add it as a child of the template element. The content should then be in scope of your viewmodel.
This way you can have the same markup in between the custom element tags with no javascript, and inside your template in the correct scope when Aurelia is running
import {processContent, TargetInstruction, inject} from 'aurelia-framework';
#inject(Element, TargetInstruction)
#processContent(function(viewCompiler, viewResources, element, instruction) {
// Do stuff
instruction.templateContent = element;
return true;
})
class MyViewModel {
constructor(element, targetInstruction) {
var behavior = targetInstruction.behaviorInstructions[0];
var userTemplate = behavior.templateContent;
element.addChild(userTemplate);
}
}
Disclaimer: the above code hasn't been tested and I pulled it from my grid which is several releases old - you may need to tweak

Categories

Resources