Angular 8 adding dynamic model - angular8

I have a card component
#Component({selector: 'card',})
export class CardComponent {
#Input() title: string;
#Input() isHistory: boolean;
#Input() showMenu = boolean;
}
And I want pass two properties dynamically. 'isHistory' and 'showMenu', which are defined as cardModels
general.component.html
<card title="{{ configCard.header }}"
[(ngModel)]="configCard.cardModels">
</card>
and
general.component.ts
export class GeneralComponent {
private cardModels: {
isHistory: true,
showMenu : false
}
}
How to do this in angular 8, I tried with 'ngModel' but doesn't work.

[(ngModel)] is an atribute for binding form elements.
For parent-child interaction you'll need to use the #Input binding.
In your case
<card
[isHistory]="cardModels.isHistory"
[showMenu]="cardModels.showMenu">
</card>
For further reading https://angular.io/guide/component-interaction

Related

Vue Mixin - Handle Data Properties

I am trying to use mixins for some reusable methods being used in several of my Vue SFCs.
Now, what I am worried about is how does mixins handle data properties? I find that it is too easy to make errors when using mixins (e.g. when renaming the data properties in the main component and it is being accessed in the mixin). What is the best way to handle such scenario?
// main.vue
<template>
<div>
Input Name:
<input v-model="mainComponentValue" placeholder="edit me">
<br/><br/>
{{ welcomeMessage }} <br/>
Member ID: {{ getMemberIdNum() }}
<br/><br/>
{{ checkMemberOftheMonth() }}
<br/><br/><br/>
{{ someMainComponentMethod() }}
</div>
</template>
<script>
import mixin from ‘./mixin.js’
export default {
mixins: [mixin],
data () {
return {
title: 'Component',
mainComponentValue: 'John'
}
},
methods: {
someMainComponentMethod () {
return 'This is a computed property from the mixin: ' + this.memberName
}
}
}
</script>
Then on my mixin:
// mixin.js
export default {
data () {
return {
mixinSampleValue: 'Hello there'
}
},
computed: {
welcomeMessage () {
return this.mixinSampleValue + ', ' + this.mainComponentValue.toUpperCase() + '!'
},
memberName () {
return this.mainComponentValue.toLowerCase()
}
},
methods: {
getMemberIdNum () {
switch (this.memberName) {
case 'john':
return '0001'
case 'roger':
return '0002'
default:
return '0003'
}
},
checkMemberOftheMonth () {
// use main component's data
if (this.memberName === 'john') {
return 'Congratulations, you are our member of the month!'
}
}
}
}
As you can see, main.vue data properties are used in the mixin and vice versa (and computed properties and methods).. So I worry that if I change the data property name (let's say mainComponentValue to name) at main.vue or any other vue component that uses the mixin, then it will mess my mixin up. And vice versa when I change a computed property from the mixin, then main.vue will be messed up.
Any way I can add certain checkings for cases like this?
For reference, here is a sample CodePen:
https://codepen.io/keechan/pen/eYBRQGM
Thanks!
We do not use mixins anymore (almost). We had the same issues in our dev team, that it is not obvious inside a component what the mixin provides.
Instead we use full or renderless components with scoped slots or we simply import some functions from a lib directory that we want to share across several vue components.
Another alternative is the Composition API, see also: https://css-tricks.com/how-the-vue-composition-api-replaces-vue-mixins/

Vue – pass variable from child template to App.vue

I read up on passing variables from child to parent using $emit but I can't fully figure it out yet.
In App.vue I have a <header/> component for the page header containing a button which controls the mobile navigation's visibility. On click it changes its class:
<button #click="toggleMobileNavigation" :class="isOpen ? 'is-open' : 'is-closed'">
The <header/>'s js:
export default {
data() {
return {
isOpen: false,
};
},
methods: {
toggleMobileNavigation() {
if(!this.isOpen) {
this.isOpen = true;
} else {
this.isOpen = false;
}
this.$emit(this.isOpen)
}
}
}
The App.vue:
<Header />
<main id="main" tabindex="-1" class="main" :class="isOpen">
This obviously this doesn't work and I can't figure out what the right way is to catch the $emit.
Thanks for any tips!
I would say you are on the right track, this child needs to emit some event to alert its parent of an important change.
But instead of doing this in your Header component:
this.$emit(this.isOpen)
Supply an event name:
this.$emit('opened', this.isOpen)
// or:
if (this.isOpen) {
this.$emit('opened');
} else {
this.$emit('closed');
}
The way you catch this event in the parent component (App.vue) should be:
<Header #opened="handleOpenedEvent"> // will call method handleOpenedEvent
// alternatively:
<Header #opened="menuStatus = $event"> // $event contains data you supply as second argument to your this.$emit(name, ...) call
// #[eventname] is one way of doing it, v-on is the same:
<Header v-on:opened="handleEvent">

Vue.js Trying to use props in style

I am trying to use some props in a component of mine that show different v-icons and sizes depending on the values passed to it.
However when i try use one of the props to set the icon size with styles the icon doesnt change at all
Heres the code:
<template>
<div v-if="this.name === 'someName'">
<v-icon :style="style" >some-icon-name</v-icon>
</div>
<div v-else>
<v-icon :style="style" >some-other-icon-name</v-icon>
</div>
</template>
export default {
computed: {
style () {
return 'size: ' + this.iconSize + ';'
}
},
props: {
iconSize: {
type: Number,
required: false
},
name : {
type: String,
required: false
},
},
data () {
return {
//
}
}
}
</script>
When using the component i simply use v-bind to pass the props:
<appIcons v-bind:iconSize="90" v-name="someName" />
You seem to be confusing two things. v-icon has a prop called size whereas style is a Vue mechanism for setting custom CSS styles. Either could be used in this case. You cannot set a style of size, that's meaningless as size is not a CSS property.
I think what you want is this:
<v-icon :size="iconSize" >some-icon-name</v-icon>
That's using the size prop of v-icon rather than a custom style.
You could in theory do it using a style if you set the font-size. e.g.
style () {
return 'font-size: ' + this.iconSize + 'px'
}
Or perhaps using an object instead:
style () {
return {
fontSize: this.iconSize + 'px'
}
}

stencil is not rerendeing component when #state() decorated property is changed

I started with stencil today. Following code is all I have in my project. Docs say, if Component member decorated with #state() is changed, component will rerender.
Any changes to a #State() property will cause the components render function to be called again.
But even this simple thing is not working. Pls advice.
import {Component, State} from '#stencil/core';
#Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true
})
export class MyComponent {
#State() name:string = 'john';
changeName(){
this.name = "Peter";
console.log('inside changeName');
}
render() {
return <div>
<p>{this.name}</p>
<button onClick={this.changeName}>click</button>
</div>;
}
}
When I click on the button inside changeName is logged but no changes to name is seen in view.
Try changing your onClick to an arrow function:
<button onClick={() => this.changeName()}>click</button>
This preserves the meaning of this to reference your class. See https://stenciljs.com/docs/templating-jsx/#handling-user-input for more details and examples.
#matthewsteele answer is right but you can also define your function like below to make it work.
private changeName = () => {
this.name = "Peter";
console.log('inside changeName');
}
Doing above the this reference will still preserves to the class.

VUE2js: How to have component re-render after its props change?

a Vue newbie here. The thing is is simple:
<template>
<btn :color="color" #click="toggleColor">{{btnmsg}}</btn>
</template>
<script>
import { Btn } from 'chico'
export default = {
name: 'AButton',
componenents: {
Btn
},
data () {
return {
btnmsg: 'Legia pany'
colors: ['blue', 'black', 'green', 'organge', 'teal', 'cyan', 'yellow', 'white'],
color: 'red'
}
},
methods: {
toggleColor () {
this.color = this.colors[Math.floor(Math.random() * Math.floor(this.colors.length))]
}
}
</script>
The 'Btn' from the ChicoFamily goes something like this
<template>
<button :is="tag" :class="[className, {'active': active}]" :type="type" :role="role" ">
<slot></slot>
</button>
</template>
<script>
import classNames from 'classnames';
export default {
props: {
tag: {
type: String,
default: "button"
},
color: {
type: String,
default: "default"
...it takes hellotta props...
},
data () {
return {
className: classNames(
this.floating ? 'btn-floating' : 'btn',
this.outline ? 'btn-outline-' + this.outline : this.flat ? 'btn-flat' : this.transparent ? '' : 'btn-' + this.color,
...classes derived from these props...
)
};
}
};
</script>
Yes, it is a button that, when clicked, should change its color. Clicking it indeed changes a prop passed, but does not, in fact, have the button re-rendered. I am asking this question, because I feel like there is something bigger about Vue2 mechanics that is eluding me.
Why passing a different prop does not re-render this sweet-baby-to-be button? How does one do it properly?
Best, Paco
[edit:] The Btn takes its color from Bootstrap classes deriving from the prop. Can it be that it gets the proper props in, but the className mechanic does not catch up?
Setting a :key on the component is the best way to force Vue to re-render the component. If you require the component to be re-rendered, simply modify the value of the key, and Vue will re-render it.
Your color isn't reactive because you set it as a data and not as a computed.
The way you did it, the className will be set once when the instance will be created.
In ordre to make the className re-evaluate each time you change one of the props in state, you will have to make a computed property out of this :
Btn component :
export default {
props: {
[...]
},
computed: {
className() {
return classNames(
this.floating ? 'btn-floating' : 'btn',
this.outline ? 'btn-outline-' + this.outline : this.flat ? 'btn-flat' : this.transparent ? '' : 'btn-' + this.color);
);
},
},
}