angular4 kendo-numerictextbox ngModel two way data binding - angular5

Below one is just sample. I need to multiple calculations dynamically.
Total value is not updating if I change values in units/price text boxes. How can I calculate using ngModel? Should we use valueChange event? Angular 4 don't update automatically?
JSON:
items:[{units:10;price:20, total:0},
{units:20;price:23, total:0}]
Controller:
pageLoad(){
for(var i=0; i<items.length; i++){
items[i].total = items[i].units*items[i].price
}
}
HTML:
<div *ngFor="let item of items; let i=index">
<kendo-numerictextbox [(ngModel)]="item.units" />
<kendo-numerictextbox [(ngModel)]="item.price" />
<kendo-numerictextbox [readOnly]="true" [(ngModel)]="item.total" />
</div>

Here is working Plunkr.
Code:
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<div *ngFor="let item of items; let i=index">
<div>{{item | json}}</div>
<div>
<kendo-numerictextbox [(ngModel)]="item.units"></kendo-numerictextbox>
<kendo-numerictextbox [(ngModel)]="item.price"></kendo-numerictextbox>
<kendo-numerictextbox [readonly]="true" [value]="getTotal(item)"></kendo-numerictextbox>
</div>
</div>
`
})
export class AppComponent
{
public items = [{units:10,price:20},{units:20,price:23}];
public getTotal(item)
{
return item.units*item.price;
}
}

Related

How to change a prop value in a generated vue components for single instance or for all instances?

Trying to create a simple blog style page. Every post has a like button, that increments when clicked. I generate 10 of these components with a v-for loop, taking data from a vuex store. However, I'd like there to be a button on the home page that resets all of the like counters.
By googling I seem to find and get working solutions that do either one or the other, not together. Yet to get anything working at all except singular counters.
How can I add a button that resets all the PostEntity counter props? Or how should I restructure it? I've thought about somehow doing in with states.
This is my post component, that gets looped in the main view .vue object:
<template>
<div class="post">
<div class="postheader">
<img :src="profilePic" alt="profilepic" class="profilepic" />
<p>{{ postDate }}</p>
</div>
<div class="postbody">
<img :src="postImage" />
<p>{{ postParagraph }}</p>
</div>
<div class="postfooter">
<!--<img :src="require('#/assets/' +nation.drapeau)"/> -->
<img
:src="require('#/assets/like.png')"
class="likepilt"
#click.prevent="increment"
/>
<p>Number of likes: {{ count }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'PostEntity',
props: {
postDate: String,
postImage: String,
profilePic: String,
postParagraph: String
},
data: function () {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
}
}
};
</script>
This is how I retrieve info from my VueX store:
getters: {
postListStuff: state => {
const postListStuff = state.postList.map(post => {
return {
id: post.id,
img: post.img,
profilepic: post.profilepic,
date: post.date,
paragraph: post.paragraph
};
});
return postListStuff;
}
}
This is how I display the components and generate the posts:
<template>
<HeaderBox title-text="Homepage" />
<div v-for="post in postListStuff" :key="post.id" class="posts">
<PostEntity
:post-date="post.date"
:profile-pic="post.profilepic"
:post-image="post.img"
:post-paragraph="post.paragraph"
></PostEntity>
</div>
<FooterBox />
<HelloWorld />
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
import HeaderBox from '#/components/Header';
import FooterBox from '#/components/Footer';
import PostEntity from '#/components/Post';
export default {
name: 'App',
components: {
FooterBox,
HeaderBox,
HelloWorld,
PostEntity
},
computed: {
postListStuff() {
return this.$store.getters.postListStuff;
}
}
};
</script>
There are multiple possible ways to go about doing this, but the simplest way I can think of with least amount of code would be:
Add a reset method to the PostEntity component that sets count to 0.
methods: {
increment() {
this.count++;
},
reset() {
this.count = 0;
}
}
Then in the parent component add a ref to the PostEntity components inside the v-for loop, then add a new button with onclick method resetCounters:
<div v-for="post in postListStuff" :key="post.id" class="posts">
<PostEntity
ref="post"
:post-date="post.date"
:profile-pic="post.profilepic"
:post-image="post.img"
:post-paragraph="post.paragraph"
></PostEntity>
</div>
<button #click="resetCounters">Reset</button>
resetCounters will loop through the array of PostEntity refs and call the reset method on each of them.
methods: {
resetCounters() {
this.$refs.post.forEach(p => p.reset());
}
}

Update properties of component in Vue.js

I've been searching for the answer 2nd day. But still couldn't find solution.
I have the modal window template. And the main page template from where I need to update modal window size by clicking on the button (span). Shortly it's like this for HTML:
<template id="modal">
<div>
<div :class="'modal-' + size">
...
</div>
</div>
</template>
<template id="list">
<div>
<span #click="onDetails">
Show Details
</span>
</div>
<modal size="md" #showdetails="showdetails();" ref="modal">
...
</modal>
</template>
And for JS:
Vue.component("modal", {
template: "#modal",
props: {
size: {
type: String,
default: ""
}
},
methods: {
onDetails() {
this.$emit("showdetails")
}
}
})
var List = Vue.extend({
template: "#list",
methods: {
showDetails() {
if(this.$refs.modal.size == "md") {
this.$refs.modal.size = "lg"
}
<additional code here>
}
}
})
When I'm accessing this.$refs.modal.size for read - it's OK. When I'm just changing it from showDetails - OK if only this action in the function. When I'm put something else instead of - size is not updating.
For example:
this.$refs.modal.size = "lg" - will work
this.$refs.modal.theme = "danger"; this.$refs.modal.size = "lg" - neither of them are updating
What am I doing wrong?
You need to assign the attribute value of Size by the javascript method setAttribute . Example : this.$refs.modal.setAttribute('size', 'lg')
There is a working demo below:
new Vue({
el: '#app',
methods: {
showdetails() {
console.log(this.$refs.modal.getAttribute('size'));
this.$refs.modal.setAttribute('size', 'lg')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<div id='app'>
<button size="md" #click="showdetails" ref="modal">Click</button>
</div>

Angular5 - use key/value pair for select option

I've started working with angular5 and I'm trying to get the value of a select option and it's assigned ID onChange.
Component
import { Component, OnInit, EventEmitter } from '#angular/core';
// Services
import { LookupDataService } from '../../../service/lookup-data.service';
#Component({
selector: 'diversity-dropdown',
templateUrl: './diversity-dropdown.component.html',
providers: [LookupDataService]
})
export class DiversityDropdownComponent implements OnInit {
diversityForm: IDiversityForm[];
title = 'Lookup data';
selectedDiversity = '';
constructor(private readonly lookupDataService: LookupDataService) { }
ngOnInit() {
this.lookupDataService.getDiversityForm().subscribe((diversityForm: IDiversityForm[]) => {
this.diversityForm = diversityForm;
console.log(diversityForm);
}
);
}
onChange($event) {
console.log($event);
}
}
View
<div *ngFor='let section of diversityForm'>
<span class="label label-default">{{section.labelText}}</span>
<select (change)="onChange($event)" ng-model="">
<<option *ngFor='let dropdown of section.lookupDropdowns' id="{{dropdown.labelText}}" value="{{dropdown.value}} {{dropdown.labelText}}">{{dropdown.value}}</option>
</select>
<br />
</div>
Data
I've tried $event.target.id but in the console it's an empty string. $event.target.value works however.
Is there an easy way of doing what I am trying to do?
Stackblitz demo here
You should use select in this way:
HTML:
<select [(ngModel)]="newGovId.first_id_type" (change)="function($event)">
<option [ngValue]="null">Select something</option>
<option *ngFor="let option of options"
[ngValue]="option.value" [innerHtml]="option.text"></option>
</select>
The context of each option should be in the variable you're looping.

Angular2 | How to addClass and removeClass specifically (clicked div element to activate the className)

## Note: ##
Onclick of div I am trying to enable the class name which was clicked.
like eg: $('div[data-index='0.0']').addClass('selected'); in Jquery // addClass only to specified div which has data-index =0.0.
I dont want want it to enable all className on click.
Unique way of enabling specific class name
I want the answer specifically in angular2
## Template: ##
<div class="board">
<div class="matrix selected" data-index="0-0" [ngClass]="{selected:isCellSelected}" (click)="fireClickEvent(0-0)">
</div>
<div class="matrix selected" data-index="0-1" [ngClass]="{selected:isCellSelected}" (click)="fireClickEvent(0-1)">
</div>
<div class="matrix selected" data-index="1-0" [ngClass]="{selected:isCellSelected}" (click)="fireClickEvent(1-0)">
</div>
<div class="matrix selected" data-index="1-1" [ngClass]="{selected:isCellSelected}" (click)="fireClickEvent(1-1)">
</div>
</div>
## component ##
import { Component, OnInit} from '#angular/core';
#Component({
selector: 'app'
})
export class displayComponent implements OnInit {
isCellSelected :boolean ;
constructor() {
}
ngOnInit() {
}
fireClickEvent(clickedCell) {
const selectedCellIndex = clickedCell;
this.isCellSelected = true; // enabling className for all three
// I need only clicked cell to activate the className Not all.
}
}
Thanks in Advance .!!
#Directive({
selector: '[selectable]'
})
export class MatrixDirective {
#HostBinding('class')
classes = '';
#Input('selectableClass')
toggleClass = 'selected';
#HostListener('click')
fireClickEvent() {
if(this.classes.indexOf(this.toggleClass) > -1) {
this.classes = this.classes.replace(this.toggleClass, '');
} else {
this.classes = `${this.classes} ${this.toggleClass}`;
}
};
}
This directive here will accomplish what you're looking for, a bit of overkill, but will help shift you into the "angular way" of doing things.
To use the above directive, just adjust your elements above with this directive.
<!-- OLD -->
<div class="matrix selected" data-index="0-0" [ngClass]="{selected:isCellSelected}" (click)="fireClickEvent(0-0)">
</div>
<!-- Adjusted -->
<div class="matrix" data-index="0-0" selectable>
</div>

Two way binding not working on bootstrap-select with aurelia

I have managed to create a custom element to use the boostrap-select element. However, I can pass/bind values to it from the main view (parent) but I am unable to get the selection out from the element when I use two-way binding.
My custom element is:
import {inject, customElement, bindable} from 'aurelia-framework';
import * as selectpicker from 'bootstrap-select'
#customElement('select-picker')
export class BootStrapSelectPicker {
#bindable selectableValues = null;
#bindable newValue = null;
#bindable selectedValue = 10;
constructor(){
}
attached(){
$('.selectpicker').selectpicker({
style: 'btn-info',
size: 4
});
$('.selectpicker').on('change', function(){
var selected = $(this).find("option:selected").val();
this.selectedValue = selected;
console.log(this.selectedValue);
});
$('.selectpicker').val(this.selectedValue); <-- the selection here is correct
$('.selectpicker').selectpicker('refresh');
}
}
The corresponding view is:
<template>
<select class="selectpicker">
<option repeat.for="p of selectableValues">${p}</option>
</select>
</template>
My containing view that uses the custom element is:
<template>
<require from="./select-picker"></require>
<ul class="list-group">
<li class="list-group-item" repeat.for="p of messageProperties">
<div if.bind="p.propertyType == 'string'">
<div class="form-group">
<label for="ln">Name: ${p.propertyName}</label>
<input type="text" value.bind="p.propertyValue" class="form-control" id="ln" >
</div>
</div>
<div if.bind="p.propertyType == 'integer'">
<div class="form-group">
<label for="ln">Name: ${p.propertyName}</label>
<input type="text" value.bind="p.selectedValue" class="form-control" id="ln" >
<select-picker selectable-values.bind="p.selectableValues"
selected-value.two-way="p.selectedValue"></select-picker>
</div>
</div>
</li>
</ul>
</template>
I expected p.selectedValue to change once a selection is made with the select control as shown here with the two-way command:
selected-value.two-way="p.selectedValue"
However, p.selectedValue is not changing.
Any ideas why this is not working?
Turns out to be a simple scope issue:
attached(){
$('.selectpicker').selectpicker({
style: 'btn-info',
size: 4
});
$('.selectpicker').on('change', function(){
var selected = $(this).find("option:selected").val();
this.selectedValue = selected; // <-- This here doesn't refer to the VM any more
// if you look at the line above you are wrapping $(this) with jq, this works
// because 'this' is now in the scope of the calling element but
// doesn't refer to the aurelia viewmodel
console.log(this.selectedValue);
});
$('.selectpicker').val(this.selectedValue);
$('.selectpicker').selectpicker('refresh');
}
Simple fix is:
attached(){
var self = this; // <--- Create a ref to the VM
$('.selectpicker').selectpicker({
style: 'btn-info',
size: 4
});
$('.selectpicker').on('change', function(){
var selected = $(this).find("option:selected").val();
// Change this to self
self.selectedValue = selected; // <--- Correct object gets the value now - binding works
console.log(this.selectedValue);
});
$('.selectpicker').val(this.selectedValue);
$('.selectpicker').selectpicker('refresh');
}
I'm not sure how this will actually be handled in ES6/7 - I'm sure I read somewhere about how this will change, but since you are transpiling to ES5 it's definitely something to watch out for
The following code works for me, in case anyone has the same issue:
import {inject, customElement, bindable} from 'aurelia-framework';
import 'bootstrap-select'
#customElement('select-picker')
#inject(Element)
export class BootStrapSelectPicker {
#bindable name: string;
#bindable selectableValues;
#bindable selectedValue;
constructor(private element) {
}
attached() {
var self = this;
var $: any = jQuery;
var $elm = $(self.element).find('select');
if ($elm.length > 0) {
$elm.selectpicker();
$elm.on('change', function () {
self.selectedValue = $(this).find("option:selected").val();
});
this.refreshPicker($elm);
}
}
selectedValueChanged(newValue, oldValue) {
var $: any = jQuery;
var $elm = $(this.element).find('select');
this.refreshPicker($elm);
}
private refreshPicker = ($elm) => {
$elm.val(this.selectedValue);
$elm.selectpicker('refresh');
}
}