How to use js data in a template? - vue.js

I'm trying to learn some useful skills while creating my Resume Website, so I decided to try Vue.js. I had no trouble understanding the basic structure of the framework, but I have trouble actually manipulating data in .Vue files.
What I'm trying to do currently is creating two classes in a tag:
Skills
Categories (list of skills with a name)
<script>
class Category {
name = "";
skills = [];
constructor(name) {
this.name = name;
this.skills = [];
}
}
class Skill {
constructor(name, level) {
this.name = name;
this.level = level;
}
}
let programmation = new Category("Programmation");
let cpp = new Skill("C++", "Confirmed");
programmation.skills.push(cpp);
let categories = [].push(programmation, ...);
Then, I create a list of categories with skills in them (I showed only one in the example code for clarity), and try to display those in my template as such:
<template>
<div class="skills">
<h1 v-for="cat in categories" v-bind:key="cat.name">{{cat.name}}</h1>
<container xl="1200"></container>
</div>
</template>
For now, I'm only trying to display the names of my categories.
However, the compiler show me this error:
error: 'categories' is assigned a value but never used (no-unused-vars) at src\views\Skills.vue:72:5:
71 |
> 72 | let categories = [].push(programmation, ...);
| ^
73 |
74 | export default {
75 | name: "skills",
while I'm using this value in the template. How am I supposed to define and use Js variables in my template?

You are using categories as reactive property inside vue component, but you need to register to make it reactivity
Here is the fix in export:
export default {
name: "skills",
data() {
return {
categories: categories,
};
},
components: {
//Skill
}
};
Here is the sample codepen: https://codepen.io/chansv/pen/YzzGOMz?editors=1010

Related

How to differentiate component in angular 6

I am trying to create a global component but that is working same like in all places..i want to use with different label values and in many palaces like aglobal component.I am learning angular 6 so facing trouble now. How do it to resolve?
service:
addComp(Names,c){
let item = {name: Names, componentid: c};
if (this.item.find((test) => test.name === Names) === undefined) {
this.item.push(item);
}
}
You can use the #Input decorator to declare values in your BreadcrumbDemoComponent that you want to be different, and pass those values as properties in the selector tag.
Example:
app-component.html
<breadcrumb-demo [label]="'Value 1'"></breadcrumb-demo>
<breadcrumb-demo [label]="'Value 2'"></breadcrumb-demo>
breadcrumb-demo.component.ts
...
export class BreadcrumbDemoComponent {
#Input() label;
...
}
breadcrumb-demo.component.html
...
<h1>{{ label }}</h1>
...

What is the mechanism of calling a function from a template in Vue js

I am trying to learn Vue.js. I am following a tutorial on this site https://scrimba.com/p/pZ45Hz/c7anmTk. From here I am not getting something clear.
Here is the code below and my confusion as well :
<div id="app">
<wizard :name="harry" :cast="oculus_reparo" ></wizard>
<wizard :name="ron" :cast="wingardium_leviosa"></wizard>
<wizard :name="hermione" :cast="alohomora" ></wizard>
</div>
// emojify returns the corresponding emoji image
function emojify(name) {
var out = `<img src="emojis/` + name + `.png">`
return out
}
// cast returns a spell (function) that decorates the wizard
function cast(emoji) {
var magic = emojify("magic")
return function (wizard) {
return wizard + " " + magic + " " + emoji + " " + magic
}
}
Vue.component("wizard", {
props: ["name", "cast"],
template: `<p v-html="cast(name)"></p>`
})
var app = new Vue({
el: "#app",
data: {
harry : emojify("harry" ),
ron : emojify("ron" ),
hermione : emojify("hermione")
},
methods: {
// oculus_reparo returns a spell (function) that repairs glasses
oculus_reparo: cast(emojify("oculus-reparo")),
// wingardium_leviosa returns a spell (function) that levitates an object
wingardium_leviosa: cast(emojify("wingardium-leviosa")),
// alohomora returns a spell (function) that unlocks a door
alohomora: cast(emojify("alohomora"))
}
})
So far what I have got is that, I have created a component named wizard which takes two properties - name and cast. name is getting the value from data, and so far I understand that cast is calling the method with a parameter.
So both of them should return their specific image. My first confusion: Where does wizard come from and how is it showing the data.name image? If it is because of the method call in the template then why does emoji return another image?
I think the example is unnecessarily complex for the ideas you're looking to learn.
wizard is being globally registered with Vue by Vue.component("wizard", ...). When Vue interprets each wizard call in the template it will replace it with <p v-html="cast(name)"></p> which is set in the wizard component definition. Here name gets mapped to the property that is set via :name=. v-html is just saying to render as html the return value of cast(name), here cast is the function property that is passed to the component and not the cast function locally defined. Everything after that happens as you would expect where emojify returns a template literal that is passed to cast, that then returns a function, which combines the emoji and other properties.

Set a variable inside a v-for loop on Vue JS

I have a v-for loop with vue.js on a SPA and I wonder if it's posible to set a variable at the beginning and then just print it everytime you need it, because right now i'm calling a method everytime i need to print the variable.
This is the JSON data.
{
"likes": ["famiglia", "ridere", "caffè", "cioccolato", "tres leches", "ballare", "cinema"],
"dislikes":["tristezze", "abuso su animali", "ingiustizie", "bugie"]
}
Then I use it in a loop:
<template>
<div class="c-interests__item" v-for="(value, key) in interests" :key="key" :data-key="key" :data-is="getEmotion(key)" >
// NOTE: I need to use the variable like this in different places, and I find myself calling getEmotion(key) everythime, is this the way to go on Vue? or there is another way to set a var and just call it where we need it?
<div :class="['c-card__frontTopBox', 'c-card__frontTopBox--' + getEmotion(key)]" ...
<svgicon :icon="getEmotion(key) ...
</div>
</template>
<script>
import interests from '../assets/json/interests.json'
... More imports
let emotion = ''
export default {
name: 'CInfographicsInterests',
components: {
JSubtitle, svgicon
},
data () {
return {
interests,
emotion
}
},
methods: {
getEmotion (key) {
let emotion = (key === 0) ? 'happy' : 'sad'
return emotion
}
}
}
</script>
// Not relevanty to the question
<style lang='scss'>
.c-interests{...}
</style>
I tried adding a prop like :testy="getEmotion(key)" and then { testy } with no luck...
I tried printing { emotion } directly and it doesn't work
So, there is anyway to acomplish this or should i stick calling the method every time?
Thanks in advance for any help.
It's not a good idea to use methods inside a template for non-user-directed actions (like onClicks). It's especially bad, when it comes to performance, inside loops.
Instead of using a method, you can use a computed variable to store the state like so
computed: {
emotions() {
return this.interests.map((index, key) => key === 0 ? 'happy' : 'sad');
}
}
This will create an array that will return the data you need, so you can use
<div class="c-interests__item"
v-for="(value, key) in interests"
:key="key" />`
which will reduce the amount of times the item gets re-drawn.

Is there a better way to share data between dynamically loaded components?

I recently built a small application with Vue.js and Express.js. There are few components needed to be prepared by the servers, e.g., the combobox for Article.Category and Article.User. The options for these 2 components needed to be rendered from server. I use <component /> as the placeholder for these 2 components in the article edit form:
<component v-bind:is="user_selection_component"></component>
<component v-bind:is="category_selection_component"></component>
I use the template string for initialising the components, the template string result.data.template is passed by server:
let org_data = original_store;
let new_data = () => {
org_data['remote_options'] = result.data.remote_options;
//if there is any default value, then assign the value to field referred by "model_name"
if(model_name && result.data.preset_value){
let previous_value = $shared.index(org_data, model_name);
if(!previous_value){
$shared.index(org_data, model_name, result.data.preset_value);
}
}
return org_data;
}
var default_cb = ()=>{console.info('['+model_name+'].default_cb()')};
let TempComponent = {
template: result.data.template,
methods: component_ref.methods,
data: new_data,
mounted: cb !== null ? cb.bind(this, org_data) : default_cb.bind(this)
};
app[mount_component] = TempComponent;
Here is the problem, the data method returns a new Observable store for the dynamically loaded components, they don't share the same store object with the parent component, which is the article edit from. Hence, if I want to modify the category field value or user field value, I have to let the callback function cb to accept the store objects of these 2 dynamically loaded components. Otherwise, from the parent component, I could not modify the values in these 2 components.
So I came up with a temporary workaround, I passed the setter method as the callback function to these dynamically loaded functions:
let set_user_id = null;
let set_cate_id = null;
(org_store) => { set_user_id = (new_id) => { org_store.form.user_id = new_id; }}
(org_store) => { set_cate_id = (new_id) => {org_store.form.category_id = new_id; }}
After I load other components or anytime I want to set the category/user value, I can just call set_user_id($new_user_id) or set_cate_id($new_category_id);
I don't like this work around at all. I tried to use the event handler to emit the new values into these 2 components. But I couldn't access these 2 dynamically loaded component via $ref. Is there a better way to let data be shared between dynamically loaded components? Thanks.
If your components will accept props, you can localize your event bus, which is a little nicer than having a global. The parent component creates the bus as a data item:
data() {
...
bus: new Vue()
}
The components accept it as a prop:
<component v-bind:is="user_selection_component" :bus="bus"></component>
<component v-bind:is="category_selection_component" :bus="bus"></component>
and you use it as in your answer, except referring to this.bus instead of just bus.
I don't think what I have now is the best solution. After consulting with other people, I took event bus as the better solution. So I modified my code as:
In my init component:
let org_data = original_store;
let new_data = () => {
org_data['remote_options'] = result.data.remote_options;
//if there is any default value, then assign the value to field referred by "model_name"
if(model_name && result.data.preset_value){
let previous_value = $shared.index(org_data, model_name);
if(!previous_value){
$shared.index(org_data, model_name, result.data.preset_value);
}
}
return org_data;
}
var default_cb = () => { console.info('['+model_name+'].default_cb()') };
let TempComponent = {
template: result.data.template,
methods: component_ref.methods,
data: new_data,
mounted: cb !== null ? cb.bind(this, org_data) : default_cb.bind(this)
};
bus.$on('set.' + model_name, function(value){
$shared.index(org_data, model_name, value);
});
The difference is here:
bus.$on('set.' + model_name, function(value){
$shared.index(org_data, model_name, value);
});
The bus is the common event bus created by Vue():
let bus = new Vue()
From the parent component, I can just use this event bus to emit the event:
bus.$emit('set.form.user_id', this.form.user_id);
I do feel better after changing to this solution. But I still appreciate if there is an even better way. Thanks.

access object with dynamic variable vue.js

This is my object
var users ={
twitter : {
name : //,
lastname : //
},
facebook : {
name : //,
lastname : //
}
}
}
I have a dynamic variable activeuser that updates from Facebook to twitter.
What i'm trying to do is refer to the inner object in users depending on the value of activeuser. I need to give my div something like this class :
<div class=' {{users.activeuser}}'></div>
I know this is not how it should be done with vue.js. Do you have any suggestions?
Thank You!
Using VueJS you should be able to assign your dynamic variable to a Vue Model when you load the new object using a Vue setter $set('property name', 'value')
Example AJAX retreival:
$.getJSON('myURL.html?query=xxx', function(data, textStatus, jqXHR){
try{
MyVue.$set('dynamicObject', data);
}
catch(e){}
});
A generic Vue may look like this:
var MyVue = new Vue({
el:'#exampleDiv',
data: {
dynamicObject : ''
}
});
Bound to an example HTML element:
<div id="exampleDiv">
<label class="{{dynamicObject.activeuser}}">{{dynamicObject.username}}</label>
</div>
In the case that you have an object with an array of objects which also contain properties Vue makes it very simple to create many HTML elements (for each child object) by simply adding a v-repeat (example) to the desired HTML and assigning the datasource:
<div id="exampleDiv">
<label v-repeat="dynamicObject" class="{{dynamicObject.activeuser}}"></label>
</div>