Vue - Unexpected token '<' - html

I have a really simple code. I'm trying to learn Vue by following this tutorial on youtube
https://www.youtube.com/watch?v=4deVCNJq3qc
I am stuck at 31:30 with this code:
Vue.component('car-list', {
template:
<ul>
<li v-for="car in cars">{{ car }}</li>
</ul>
})
for some reason it breaks the whole code and when i inspect the page on google chrome i get the error
Uncaught SyntaxError: Unexpected token '<'
screenshot of the error: https://ibb.co/CnzRFtZ
I am using Atom as my editor.
my whole code
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.18/vue.min.js"></script>
</head>
<body>
<div id="root">
{{ carzName }}
<br>
<input v-model="newCar" #keyup.enter="addCar">
<button #click="addCar">+ Add</button>
<li v-for="car in cars">
{{ car }}
</li>
<car-list :cars="cars" />
</div>
</body>
</html>
<script>
Vue.component('car-list', {
template:
<ul>
<li v-for="car in cars">{{ car }}</li>
</ul>
})
const app = new Vue({
el: '#root',
component: [
'car-list'
],
data: {
cars: [
'audi',
'bmw',
'mercu'
],
newCar: ''
},
methods: {
addCar: function() {
this.cars.push(this.newCar)
this.newCar = ''
}
},
computed: {
carzName: function() {
if (this.newCar.length > 1) {
return this.newCar + 'y'
}
}
}
})
</script>
Thanks to anyone who is willing to explain me where the problem is at.

Template needs to be string. Add backtick quotes around it.
Vue.component('car-list', {
template: `
<ul>
<li v-for="car in cars">{{ car }}</li>
</ul>`
})
Edit: To iterate more on #Lawrence Cherone comment there is no need to register the component globally with Vue.component if you are gonna register it within your vue app.
Version 1: Register globally
Vue.component('car-list', {
template: `
<ul>
<li v-for="car in cars">{{ car }}</li>
</ul>`
})
and then remove component property from vue app
const app = new Vue({
el: '#root',
data: {}
})
Verison 2: Register within app
const carList = {
template: `
<ul>
<li v-for="car in cars">{{ car }}</li>
</ul>`
}
And then
const app = new Vue({
el: '#root',
component: {
carList
},
data: {}
})

Related

How to define variables in Vue router to be used inside components?

I have a navbar on top and a <router-view> right below it (as seen in App.vue). I want the title inside the navbar to change depending on the route/view I am on. Since my views in the <router-view> do not contain the title itself, I need to define them somewhere. An example for the scenario could be when reaching the route /login, the title in the navbar changes to "Login".
How do I achieve this?
When searching for a solution, I came across a lot of page title questions. I am not talking about the document.title assignment, however, that could be a solution, but not a perfect one. What if I wanted the title to be something else than the document title..
App.vue:
<template>
<Menu :isActive="isMenuActive" />
<Navbar #toggle:hamburger="onHamburgerToggle($event)" />
<router-view />
</template>
Vue router allows you to attach any information you want to any route using Route Meta Fields
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
meta: { title: "FOO" }
}
]
})
You can access the information for currently active route in any component using $route variable
const child = Vue.component('child', {
template: `
<div>
Child component ("{{ $route.meta.title }}")
</div>
`
})
const router = new VueRouter({
mode: 'history',
routes: [
{
name: 'route1',
path: '/route1',
component: child,
meta: { title: 'Hello from route 1!'}
},
{
name: 'route2',
path: '/route2',
component: child,
meta: { title: 'Hello from route 2!'}
},
]
})
new Vue({
el: '#app',
router,
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/route1">Route 1</router-link>
<router-link to="/route2">Route 2</router-link>
<h4> Current route: {{ $route.meta.title }} </h4>
<router-view></router-view>
</div>
Every component can access the $route object, so you could use that directly in your template. Or you can access that object in the script and do something with it.
Imagine your routes have the name property:
router/index.js
routes: [
{ path: '/', name: 'home', component: Home },
{ path: '/foo', name: 'foo', component: Foo },
{ path: '/bar', name: 'bar', component: Bar }
]
You could show that name in the template with no script necessary:
Navbar.vue
<template>
<div>Title: {{ $route.name }}</div>
</template>
(meta is also a good idea here as explained in #MichalLevý's answer.)
Or you could access the route object in the script and create a title however you want.
Navbar.vue (Composition API)
<template>
<div>Title: {{ title }}</div>
</template>
<script>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
const title = ref('My title ' /* Do something with `route` */);
return { title }
}
}
</script>

How to dynamically change content of component with JSON?

I am creating my design portfolio using Vue CLI 3. The architecture of my website is very simple. I have a home page, about page, work page, and several individual project pages:
Home
About
Work
Project
Project
Project
The work page consists of several links that would click through to the individual project pages. The work component is set up like so:
<template>
<div>
<projectLink v-for="data in projectLinkJson" />
</div>
</template>
<script>
import projectLink from '#/components/projectLink.vue'
import json from '#/json/projectLink.json'
export default {
name: 'work',
data(){
return{
projectLinkJson: json
}
},
components: {
projectLink
}
}
</script>
As you can see, I'm importing JSON to dynamically render the content. Next, the projectLink component can be seen in the code block below. Within this component I am passing a param into <router-link> called projectName
<template>
<router-link :to="{ name: 'projectDetails', params: { name: projectName }}">
<h1>{{ title }}</h1>
</router-link>
</template>
<script>
export default {
name: 'projectLink',
props: {
title: String,
projectName: String
}
}
</script>
My routes.js file is setup like so:
const routes = [
{ path: '/', component: home },
{ path: '/about', component: about },
{ path: '/work', component: work },
{
path: "/work/:name",
name: "projectDetails",
props: true,
component: projectDetails
},
];
and my JSON is like so:
{
"0": {
"title": "test",
"projectName": "test"
}
}
Lastly, my projectDetails component is the component that is where I am having this issue:
<template>
<div>
<div
v-for="(data,index) in projectDetailsJson" v-if="index <= 1">
<h1>{{ data.title }}</h1>
<p>{{ data.description }}</p>
</div>
</div>
</template>
<script>
import json from '#/json/projectDetails.json'
export default {
name: 'projectDetails',
data(){
return{
projectDetailsJson: json
}
},
props: {
description: String,
title: String
}
}
</script>
I am successfully routing to the URL I want, which is /project/'name'. I want to use the projectDetails component as the framework for each of my individual project pages. But how do I do this dynamically? I want to retrieve data from a JSON file and display the correct object from the array based on the name that was passed to the URL. I do not want to iterate and have all of the array display on the page. I just want one project to display.
Quick solution:
projectDetails.vue
<template>
<div>
<div>
<h1>{{ projectDetails.title }}</h1>
<p>{{ projectDetails.description }}</p>
</div>
</div>
</template>
<script>
import json from '#/json/projectDetails.json';
export default {
name: 'projectDetails',
props: {
name: String,
},
data() {
return {
projectDetails: Object.values(json).find(project => project.title === this.name),
};
},
};
</script>
In my opinion, a better solution:
I don't get the idea that you keep project data in 2 separate JSON files. During compilation, both files are saved to the resulting JavaScript file. Isn't it better to keep this data in 1 file? You don't have to use all of your data in one place. The second thing, if you have a project listing then you can do routing with an optional segment, and depending on whether the segment has a value or not, display the listing or data of a particular project. Then you load project data only in one place, and when one project is selected, pass its data to the data rendering component of this project. Nowhere else do you need to load this JSON file.
routes.js
import home from '#/components/home.vue';
import about from '#/components/about.vue';
import work from '#/components/work.vue';
const routes = [
{path: '/', name: 'home', component: home},
{path: '/about', name: 'about', component: about},
{path: '/work/:name?', name: 'work', component: work, props: true},
];
export default routes;
work.vue
<template>
<div>
<project-details v-if="currentProject" :project="currentProject"/>
<projectLink v-else
v-for="project in projects"
v-bind="project"
v-bind:key="project.projectName"
/>
</div>
</template>
<script>
import projectLink from './projectLink';
import projectDetails from './projectDetails';
import json from '#/json/projectLink.json';
export default {
name: 'work',
props: {
name: String,
},
data() {
return {
projects: Object.values(json),
};
},
computed: {
currentProject() {
if (this.name) {
return this.projects.find(
project => project.projectName === this.name,
);
}
},
},
components: {
projectLink,
projectDetails,
},
};
</script>
projectDetails.vue
<template>
<div>
<div>
<h1>{{ project.title }}</h1>
<p>{{ project.description }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'projectDetails',
props: {
project: Object,
},
};
</script>
projectLink.vue (changed only one line)
<router-link v-if="projectName" :to="{ name: 'work', params: { name: projectName }}">
A full working example:
Vue.component("navigation", {
template: "#navigation"
});
const Projects = {
template: "#projects",
props: ["projects"]
};
const Project = {
template: "#project",
props: ["project"]
};
const HomePage = {
template: "#home"
};
const AboutPage = {
template: "#about"
};
const WorkPage = {
data() {
return {
projects: [{
slug: "foo",
name: "Foo",
desc: "Fus Ro Dah"
},
{
slug: "bar",
name: "Bar",
desc: "Lorem Ipsum"
}
]
};
},
props: {
slug: String
},
template: "#work",
components: {
Projects,
Project
},
computed: {
currentProject() {
if (this.slug) {
return this.projects.find(project => project.slug === this.slug);
}
}
}
};
const router = new VueRouter({
routes: [{
path: "/",
name: "home",
component: HomePage
},
{
path: "/about",
name: "about",
component: AboutPage
},
{
path: "/work/:slug?",
name: "work",
component: WorkPage,
props: true
}
]
});
new Vue({
router,
template: "#base"
}).$mount("#app");
ul.nav {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
ul.nav>li {
float: left;
}
ul.nav>li>a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
ul.nav>li>a:hover {
background-color: #111;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script>
<div id="app"></div>
<script type="text/x-template" id="base">
<div id="app">
<div>
<navigation></navigation>
<router-view></router-view>
</div>
</div>
</script>
<script type="text/x-template" id="navigation">
<ul class="nav" id="navigation">
<li>
<router-link :to="{name: 'home'}">Home</router-link>
</li>
<li>
<router-link :to="{name: 'about'}">About</router-link>
</li>
<li>
<router-link :to="{name: 'work'}">Work</router-link>
</li>
</ul>
</script>
<script type="text/x-template" id="home">
<div id="home">This is Home Page</div>
</script>
<script type="text/x-template" id="about">
<div id="about">This is About Page</div>
</script>
<script type="text/x-template" id="work">
<div id="work">
<project v-if="currentProject" :project="currentProject"></project>
<projects v-else :projects="projects"></projects>
</div>
</script>
<script type="text/x-template" id="projects">
<div id="projects">
<ul>
<li v-for="project in projects" :key="project.slug">
<router-link :to="{name: 'work', params:{ slug: project.slug}}">{{project.name}}</router-link>
</li>
</ul>
</div>
</script>
<script type="text/x-template" id="project">
<div id="project">
<h2>{{project.name}}</h2>
<p>{{project.desc}}</p>
</div>
</script>
Great work thus far, Austin! You're very close having this working. There are a few different ways you could parse out the correct data from your JSON file into the projectDetails component, but I'll just demo my preferred way.
First, you're going to need a bit of vanilla JS to search through your JSON file and return only the row that you want. I would do this as a method since the data isn't going to be changing or requiring the component to re-render. So, after your props, I would add something like this:
methods: {
findProject(projectName) {
return Object.values(json).find(project => project.title === projectName)
}
}
Note that this is going to return the first project that matches the project name. If you have projects with the exact same project name, this won't work.
Next, you'll just need to update the default value of projectDetailsJson to call this method and pass the route's project name. Update data with something like this:
data() {
return {
projectDetailsJson: this.findProject(this.$route.params.name)
}
}
If that doesn't work, we may need to set the projectDetailsJson in the created lifecycle hook, but try the above code first.
If I understood correctly, you want to keep a parent component as a layout for all of your page?
If I always understood correctly, you must use the children property of vuerouter
https://router.vuejs.org/guide/essentials/nested-routes.html
import layout from 'layout';
const projectRoute = {
path: '/project',
component: Layout, // Load your layout
redirect: '/project/list',
name: 'Project',
children: [
{
path: "list", // here the path become /project/list
component: () => import('#/views/project/List'), // load your components
name: "List of project",
},
{
path: "detail/:id",
component: () => import('#/views/project/Detail'),
name: "Detail of project",
}
],
};
So you can create your layout and add everything you want, this will be available on all child components, and you can use $emit, $refs $props ect...
+
You can create an file routes/index.js and create folder routes/modules . Inside this, you can add your routes/modules/project.js and load the modules in routes/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import projectRoutes from "./modules/project";
const routes = [
projectRoutes,
{
// other routes....
},
]
export default new VueRouter({
routes,
mode: 'history',
history: true,
});
#see the same doc : https://router.vuejs.org/guide/essentials/nested-routes.html
Finally, you just have to do the processing on the layout, and use the props to distribute the values ​​both in detail and in the project list; and use the filter methods described just above
I hope I have understood your request, if this is not the case, let me know,
see you
Edit: Here is a very nice architecture with vue, vuex and vuerouter. maybe inspire you
https://github.com/tuandm/laravue/tree/master/resources/js
For everyone, to take this one step further. How would you show only the projectLinks that match the current URL? So if I have three different JSON projectTypes: design, code, motion. If the URL contains motion in it, how do I filter my projectLink components to show only those that have a matching JSON value of either design, code or motion. Essentially I'm just trying to filter.

Get Json values with Axios

I'm trying to get all "Title", "Year" and "imdbID" from this link.
I'm using Vuejs and Axios to do so. But I'm not sure how it's done ?
Here's my code :
<!DOCTYPE html>
<html>
<head>
<meta charset=""utf-8>
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
<title>Web Project 2018</title>
<link rel="stylesheet" href="">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<h2>Liste of films</h2>
<ul>
<li v-for="film in films">{{ film.Title }}, {{ film.Year }}, {{ film.imdbID }}</li>
</ul>
</div>
<script>
new Vue({
el: '#app',
data : {
films: [],
errors: []
},
created() {
axios.get('http://www.omdbapi.com/?apikey=xxxxx&s=iron%20man')
.then(function(response) {
this.films = response.data;
})
.catch(function(error) {
this.errors.push(error);
});
}
})
</script>
</body>
</html>
With this, I only get a page with {{ film.Title }}, {{ film.Year }}, {{ film.imdbID }}
I'm sure it's simple but I can't figure it out... Any help please ?
Worked with arrows :
axios.get('http://www.omdbapi.com/?apikey=xxxx&s=iron%20man')
.then(response => {
this.films = response.data.Search;
})
.catch(error => {
this.errors.push(error);
});

VueJS and a static JSON endpoint without IDs

So I'm trying to create a VueJS application, and I was given a set of JSON objects that are retrievable through a .json endpoint.
I'll call them People. So I get an array of people in this.people after using VueResource.
I'm able to iterate and get all the name displayed on the side, however, since its not an API nor has unique IDs minus their array indexes, I am having trouble trying to narrow down each object and create a single Person view page.
Hence if it was a normal api, I could do '/people/:id', but I can't. I'm also wondering if I may have stored the Prop/Component correctly.
I put together a quick example of how this might work with the Star Wars API.
const Loading = {
template: `<h1>Loading...</h1>`
};
const People = {
props: ["people"],
template: `
<div>
<h1>People</h1>
<ul>
<li v-for="person in people">
<router-link :to='{name: "person", params:{person: person}}'>{{person.name}}</router-link>
</li>
</ul>
</div>
`
};
const PersonDetails = {
props: ["person"],
template: `
<div>
<h1>{{person.name}}</h1>
<div>Height: {{person.height}}</div>
<div>Mass: {{person.mass}}</div>
<div>Hair Color: {{person.hair_color}}</div>
<br/>
<router-link to="people">Back to people</router-link>
</div>
`
};
const routes = [
{ path:"/", component: Loading},
{ path: "/people", name: "people", component: People},
{ path: "/person", name: "person", component: PersonDetails, props: true},
]
const router = new VueRouter({
routes
})
new Vue({
el: "#app",
router,
data:{
people:[]
},
mounted(){
this.$axios.get("https://swapi.co/api/people/")
.then((response) => {
this.people = response.data.results
this.$router.push("people")
})
}
});
Here is the working example.

when using routeparams in angular,templateurl not working?

https://plnkr.co/edit/oo05d6H6AxuJGXBAUQvr?p=preview
I have created an array of items and when I click on each item details page will be displayed ,for all the items in the array I have used same details page,can anyone look at my plunker and explain why the templateURL is not working when I click on an item?
var app = angular.module("myApp", ["ngRoute"]);
app.controller('mobileController', function($scope) {
$scope.items = [{
name: 'Iphone',
}, {
name: 'Oneplus'
}, {
name: 'Moto'
}];
});
app.config(function($routeProvider) {
$routeProvider
.when('/item/:itemName', {
templateUrl: 'details.html',
controller: 'ItemCtrl'
});
app.controller('ItemCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.itemName = $routeParams.itemName;
}
]);
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script>
<script src="script.js"></script>
<body ng-app="myApp" ng-controller="mobileController">
<h2> Welcome to Mobile Store</h2>
<p>Search:<input type="text" ng-model="test"></p>
<ul>
<li ng-repeat="item in items|filter:test">{{ item.name }}
</li>
</ul>
<div ng-view></div>
</body>
</html>
here is my details page
<!DOCTYPE html>
{{itemName}}
is it because of a mismatch?
.when('/item/:itemName', {
a href="/items/{{item}}"
there's an extra s there
Summary of problems:
Your ItemCtrl is currently defined inside your module's config function. Move it out of there
app.config(function($routeProvider) {
$routeProvider
.when('/item/:itemName', {
templateUrl: 'details.html',
controller: 'ItemCtrl'
});
}); // you were missing this
app.controller('ItemCtrl', ['$scope', '$routeParams',
Your route is /item/:itemName and since you're not using HTML5 mode, you need to create your href attributes with a # prefix. For example
ng-href="#/item/{{item.name}}"
Fixed demo here ~ https://plnkr.co/edit/rKHsBMFcXqJUGp8Blx7Q?p=preview