VueJS - calling multiple image src as custom props - html

I have a small set of icons i want to call as a custom image prop depending on what type of item the component is. Code looks like this:
Vue.component('otherArticles', {
template: `
<!-- Component -->
<li>
<img :src="icon.text && icon.video" alt="icon">
<a :href="url">{{ Title }}</a>
</li>
`,
props: {
title: String,
url: String,
icon: [
{
text: "/resources/img/icons/text-icon.svg",
video: "/resources/img/icons/video-icon.svg"
}
]
}
});
Ideally in my html I would like to call them like this:
<!--Component with text icon-->
<other-articles
icon='text' <!-- how i'd like to call the text icon as img src -->
url="."
title="Text article title">
</other-articles>
<!--Component with video icon-->
<other-articles
icon='video' <!-- how i'd like to call the video icon as img src -->
url="."
title="Video article title">
</other-articles>
The img src binding is incorrect I know, i'm using it as an example of how i'm thinking it should be done, but I'm looking for any and all recommendations on how to do this correctly so I can call it the html as the example shows.
I only have these two icons and the src location for each may change but i would like to call it the same way even if i have to update the src location for either one in the future, keeping the html calls the same or similar. Any help would be appreciated. Thanks

First start by declaring your icon list as the following in your data function:
data() {
return {
iconList: {
text: '/resources/text.png',
video: '/resource/video.png',
}
};
}
Make sure to remove the list and rename the object, as you cannot have a prop and an entry in data with the same name. Then add your definition for icon to your props section as the following:
props: {
icon: {
type: String,
required: true,
},
},
This tells Vue to typecheck the prop as a string, and warn when it's not present or not a string.
Now you need to update your template function to use this new prop as an key to lookup the related icon:
template: `
<img :src="iconList[icon]"/>
`,
Now you can use your component as <comp icon="video"/>

Related

Anchor Tag in Next.js

I'm tryin got use an anchor tag in Next.js
I don't get any console errors when I set it up and click the link, but the page does not jump to the id tag.
This issue on github suggests that people need to figure out a lot of custom code to use anchors. That can't be right.
I have:
const links = [
{ label: 'Solutions', href: '#solutions', id: 'solutions' },
]
<NavLink.Desktop key={index} href={link.href} id={link.id}>
{link.label}
</NavLink.Desktop>
I get no errors, but the page does not jump to the label that has an id of 'solutions'.
Does anyone know how to solve this, or where to go for ideas on how - it can't be intented that complex custom code is required to use an anchor tag?
Chakra UI has a Link component
<Link href='https://chakra-ui.com' isExternal>
Chakra Design system <ExternalLinkIcon mx='2px' />
</Link>
If you use the regular anchor tags
<Link href="#anchor_one">Menu one</Link>
<Link href="#anchor_two">Menu two</Link>
Then you can add the id for the anchors to the sections you want to navigate into
<div id="anchor_one" />
<div id="anchor_two" />
This can be either pages or components.
I hope this helped a little bit.
As said by #juliomalves in the comments, you have to specify the id attribute on the element in which you wish to navigate to. Not on the anchor tag.
The id for the anchor should be set on the element you want to link to, not on the link itself.
The below code works for me in Next.js -
export default function Home() {
return (
<div>
Click
<section
style={{ marginTop: "1000px", marginBottom: "1000px" }}
id="section"
>
<h1>Test</h1>
</section>
</div>
);
}
Your code should look like this -
const links = [{ label: 'Solutions', href: '#solutions', id: 'solutions' }]
<NavLink.Desktop
key={index}
href={link.href}
// id={link.id} - This is wrong, as you're referring to the same element
>
{link.label}
</NavLink.Desktop>
// Rather set the id attribute in a separate div/section element
<div id={link.id}>
<h2>Solutions</h2>
</div>
maybe try
const links = [
{ label: 'Solutions', href: '#solutions', id: 'solutions' },
]
<NavLink.Desktop key={index} href={links[0].href} id={links[0].id}>
{link.label}
</NavLink.Desktop>
since you only have 1 element in the links array, if you have multiple just map through the array
It is possible to scroll to anchor programatically using Router.push:
import { useRouter } from 'next/router'
const Foo = () => {
const { push } = useRouter()
const handleClick = () => {
push("#blah")
}
return (
<div>
<button onClick={handleClick}>Scroll</button>
<div>Foo</div>
<div>Bar</div>
<div id="blah">Blah</div>
</div>
)
}
Next.js recognises that you are passing something that is not a link to a new page and will concat it (in the example #blah) to the end of the URL.
Have a read about Link from next/link its a built in feature.
https://nextjs.org/docs/api-reference/next/link
https://github.com/vercel/next.js/blob/canary/examples/hello-world/pages/index.js#L7

Angular/Typescript Text with routerLink

Updated Question for more Clarity:
Need to display some texts and links as innerHTML(data from service/DB) in the Angular HTML and when user clicks, it should go to Typescript and programmatically navigates by router.navigate
Also, How to add DomSanitizer from #ViewChild/ElementRef
Added all example in below code
Here is the updated stackblitz code
As shown in screenshot from angular.io some texts and some links
Sorry, I didn't realize you answered my comment. Angular routing is not secondary, if you don't use Angular modules you'll end up with just an HTML/CSS/Typescript application. you need at least the RouterModule for Angular to be able to use routing and hence, do what it's supposed to with the DOM.
First:
You are not importing RouterModule
solution:
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([]) // this one
]
Second:
You can't bind Angular events through innerHTML property
fix:
Make use of #ViewChild directive to change your innerHTML property and manually bind to the click event, so change in your app.component.html from
<div id="box" [innerHTML]="shouldbedivcontent" ></div>
to
<div #box id="box"></div>
Now, in your app.component.ts, add a property to hold a reference to that "box" element so you can later make some changes to the dom with it:
#ViewChild('box') container: ElementRef;
Implement AfterViewInit, that hook is where you will be able to actually handle your container, if you try using it for example in OnInit you'd get undefined because that component's html is not in the dom yet.
export class AppComponent implements AfterViewInit {
and
ngAfterViewInit() {
this.container.nativeElement.innerHTML = this.shouldbedivcontent;
this.container.nativeElement.addEventListener('click',
() => this.goto('bar')
);
}
change shouldbedivcontent property from:
'1) this is a click
<a (click)="goto("bar")">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto("bar")">Click</a><br>
3)This only works with href
bar and test'
to
'1) this is a click
<a id="link_1">Click</a><br>
2)this is with routerlink
<a [routerLink]="" (click)="goto(\'bar\')">Click</a><br>
3)This only works with href
bar and test'
And even so you'd still not get the default anchor style unless you apply some styling yourself.
Third
You are not HTML sanitizing, which could be dangerous. read more here
MY SUGGESTION:
Seems like a lot to do for you and a lot to read for someone else working alongside you for something you could easily do like in the example below!
Move your html to your app.component.html:
<div id="box">
1) this is a click
<a (click)="goto('bar')">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto('bar')">Click</a><br>
3)This only works with href
bar and test
</div>
<p>Below is actual content</p>
You'll notice that everything works now, except the anchor without routerLink or href, because that's not a link.
EDIT:
Looking at the new stackblitz, i suggest a change of approach, binding to innerHTML is ok when working with plain text or even some simple html but not a great choice to bind events or routing logic.
Angular's Renderer2 provides with a bunch of methods to dyncamically add elements to the DOM. With that on the table, you just need a little effort to take that simple html you get from your backend and turn it into something like (paste this property in your code to test it along the rest of the code provided below):
public jsonHTML = [
{
tagName: '',
text: 'some text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'bar',
attributes: {
value: 'bar' // goto parameter
}
},
{
tagName: '',
text: ' some more text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'foo',
attributes: {
value: 'foo' // goto parameter
}
}
]
Once you have it, it's way easier to create all of those elements dynamically:
this is for the code in your Q1:
Inject Renderer2 with private r2: Renderer2
And replace the Q1 related code in AfterViewInit hook to:
const parent = this.r2.createElement('div'); // container div to our stuff
this.jsonHTML.forEach((element) => {
const attributes = Object.keys(element.attributes);
const el = element.tagName && this.r2.createElement(element.tagName);
const text = this.r2.createText(element.text);
if (!el) { // when there's no tag to create we just create text directly into the div.
this.r2.appendChild(
parent,
text
);
} else { // otherwise we create it inside <a></a>
this.r2.appendChild(
el,
text
);
this.r2.appendChild(
parent,
el
);
}
if (attributes.length > 0) {
attributes.forEach((name) => {
if (el) {
this.r2.setAttribute(el, name, element.attributes[name]); // just the value attribute for now
if (name === 'value') {
this.r2.listen(el, 'click', () => {
this.goto(element.attributes[name]); // event binding with property "value" as parameter to navigate to
})
}
} else {
throw new Error('no html tag specified as element...');
}
})
}
})
this.r2.appendChild(this.container.nativeElement, parent); // div added to the DOM
No html sanitizer needed and no need to use routerLink either just inject Router and navigate to the route you want! Make improvements to the code t make it fit your needs, it should be at least a good starting point
Good Luck!
You have a css problem.
looks like a link
<a [routerLink]="something"></a> looks like a link, because if you inspect the HTML it actually gets an href property added because of routerLink
<a (click)="goTo()"></a> does NOT look like a link, because there is no href
Chrome and Safari default user agents css will not style <a> without an href (haven't confirmed Firefox but I'm sure its likely). Same thing for frameworks like bootstrap.
Updated stackblitz with CSS moved to global, not app.css
https://stackblitz.com/edit/angular-ivy-kkgmkc?embed=1&file=src/styles.css
This will style all links as the default blue, or -webkit-link if that browser supports it. It should be in your global.css file if you want it to work through the whole app.
a {
color: rgb(0, 0, 238);
color: -webkit-link;
cursor: pointer;
text-decoration: underline;
}
this works perfectly for me :D
#Directive({
selector: "[linkify]",
})
// * Apply Angular Routing behavior, PreventDefault behavior
export class CustomLinkDirective {
#Input()
appStyle: boolean = true;
constructor(
private router: Router,
private ref: ElementRef,
#Inject(PLATFORM_ID) private platformId: Object
) {}
#HostListener("click", ["$event"])
onClick(e: any) {
e.preventDefault();
const href = e.target.getAttribute("href");
href && this.router.navigate([href]);
}
ngAfterViewInit() {
if (isPlatformBrowser(this.platformId)) {
this.ref.nativeElement.querySelectorAll("a").forEach((a: HTMLElement) => {
const href = a.getAttribute("href");
href &&
this.appStyle &&
a.classList.add("text-indigo-600", "hover:text-indigo-500");
});
}
}
}
HOW I USE IT
<p linkify
class="mt-3 text-lg text-gray-500 include-link"
[innerHtml]="apiSectionText"
></p>
result

How to do an image as a component template, with src as a property in Vue

I am trying to make a component for images in Vue. However, in my latest attempts, the only result is that an empty element is put in it's place.
In previous attempts, by putting a second element inside of the outer element, I was able to get it so that there was no image tag, and all of my props were displayed as a string:
<div> src=`${source}` alt=`${alternate}}` title=`${tit}}` width=`${w}}` height=`${h}}` /> </div>
My current code is:
<body>
<template id="comp-img-template">
<asimage source="uclx3S.jpg" alternate="red" tit="red" w="100" h="100"></asimage>
</template>
<script>.
Vue.component('asimage', {
props: ['source', 'alternate', 'tit', 'w', 'h'],
template: '<img src=`${source}` alt=`${alternate}}` title=`${tit}}` width=`${w}}` height=`${h}}` />'
})
new Vue({ el: '#comp-img-template' })
</script>
</body>
I am expecting there to be an image with the modifications present in the top portion, however as mentioned before I only get the empty tags
Your template string isn't valid HTML.
Something like this may work:
template: '< img :src="source" :alt="alternate" :title="tit" :width="w" :height="h" />'
Check out Vue v-bind syntax here: https://v2.vuejs.org/v2/guide/syntax.html#v-bind-Shorthand

Component Templating

I'm an angular novice, currently building an angular2 app.
What I want to do is generate a series of DOM components from the following object data:
// Class construct with alphabeticalized properties
export class Screens {
screens: Array<Object>;
}
export var screenData: Screens = {
// Lists all of the audio files in the course
screens: [
{
id: 0,
template: 'templateURL-0.html',
css: 'templateURL-0.css'
},
{
id: 1,
template: 'templateURL-1.html',
css: 'templateURL-1.css'
},
{
id: 2,
template: 'templateURL-0.html',
css: 'templateURL-0.css'
}
]
};
I want the end result to be something similar to the following where template 0 will be displayed twice, and template 1 once; in order:
<app-screen></app-screen> <!-- templateURL-0.html content -->
<app-screen></app-screen> <!-- templateURL-1.html content -->
<app-screen></app-screen> <!-- templateURL-0.html content -->
I read the tutorial on Structural Directives and I think I need to implement something along those lines, however I'm honestly feeling a little lost on the best approach.
Ideally I would like to have something like:
<app-screen *ngFor="let screen of screenData.screens"></app-screen>
Which would then somehow set the template URL depending on what screenData.screens.template is.
Or should I do something like this? (unsure if correct syntax)
<div *ngFor="let screen of screenData.screens" [ngSwitch]="screenData.screens.template">
<app-screen-template1 [ngSwitchCase]="'templateURL-0.html'"></app-screen-template1>
<app-screen-template2 [ngSwitchCase]="'templateURL-1.html'">Ready</app-screen-template2>
</div>
Note: I will never change the templateURL reference.
I found that the best method to achieve this is to implement routing with the built in RouterModule.
So in the end I have the following in my class, where the template property is a url path / url segment.
// Class construct with alphabeticalized properties
export class Screens {
screens: Array<Object>;
}
export var screenData: Screens = {
// Lists all of the audio files in the course
screens: [
{
id: 0,
template: 'template/template-0'
}
]
};
Then when I want to load / instantiate this template, all I have to do is navigate to this url using something like:
<!-- Goes to localhost:4200/template/template-0 -->
<button [routerLink]="[screen.template]"></button>
Where screenis a bound variable in my .ts.
More on routing and navigation here.

How can I set class name dynamically?

I have understood from my last question here that string concatenate is not allowed with 0.9 and above (currently I am migrating to version 1.0).
I have to rather wrap every variable inside separate HTML element.
However there are times when I need to use a href or class attribute to be assigned with values dynamically. I cannot make it to work directly like the following:
Link text
since 1.0 won't allow string concatenation!
Please see the snippets below. I am trying to pass an attribute value from my index.html which in turn should replace the value in class attribute inside my custom element. But it is not working and I understand why.
<dom-module id="multi-color-bar-chart">
<template>
<div id="chart">
<p>{{title}}</p>
<div class="{{v1bg}}">
<!-- I want {{v1bg}} to be replaced by value sent from index.html -->
<span>{{value1}}</span>%
</div>
<div class="v2" style="background:#ffcc00;">
<span>{{value2}}</span>%
</div>
<div class="v3" style="background:#369925;">
<span>{{value3}}</span>%
</div>
<div class="clear"></div>
</div>
<div class="clear"></div>
</template>
<script>
(function () {
Polymer({
is: 'multi-color-bar-chart', //registration of element
properties: {
title: { type: String },
value1: { type: String },
value2: { type: String },
value3: { type: String },
v1bg: { type: String }
}
});
})();
</script>
</dom-module>
Here is the snippet in index.html
<multi-color-bar-chart
title="Annual"
value1="45.5"
value2="22.3"
value3="32.2"
v1bg="#ff0000">
...
...
</multi-color-bar-chart>
I am passing a hex code #ff0000 via v1bg attribute which I intend to actually replace the property inside the element.
I don't know yet if there is a work around to it. Might have used document.querySelector() but didn't try that yet. If there is a direct HTML approach that would be wonderful.
Try class$="{{v1bg}}", as this will bind to the class attribute rather than the class property.
https://www.polymer-project.org/1.0/docs/devguide/data-binding.html#attribute-binding