Angular 2 complex property binding - json

I've a model json like this:
[{
"PropName":"disabled",
"Value":"false"
},
{
"PropName":"color",
"Value":"primary"
},
{
"PropName":"visible",
"Value":"false"
}]
now I need to apply few property to a button, in this case only second element of data model color primary.
<button md-raised-button color="primary"></Button>
PropName value should be replace to color property, while Value of data with primary value.
Mainly I want to apply one or more property dynamically, form an array of element json with property name and value.
Someone can show me an example ?
thanks

On the logic you will create your object and call it for example elements.
Then in the template:
<button *ngFor="let element of elements" [color]="'primary': element.PropName == 'disabled'" [value]="element.Value" md-raised-button color="primary"></Button>

I solved so, but I don't know if best pratics:
<button md-raised-button [color]="getColorButton()">
{{dati.Label}}
</button>
from ts component I search color property from model and return value
getColorButton():string {
if (this.colore!=null) return this.colore;
this.dati.Proprieta.forEach(element => {
if (element.Nome=='color'){
this.colore = element.Valore;
}
});
return this.colore;
}

Related

How to dynamically append HTML element to component in Vue.js

I'm new to vue.js, before this i'm using jquery or js for my project, i'm working on a project that require me to append HTML element dynamically on button click, and at the same time bind the input value to model, similar to:
$(".button").click(function() {
$("#target").append("<input type='hidden' name='data' v-model='inputModel' value='1'/>");
});
But i need this in Vue.js ways.
Here is my code:
data() {
return {
programmeBanner: [],
dropzoneOptions: {
...
...
init: function () {
this.on("success", function(file, response) {
file.previewElement.id = response;
// this is the part that i want to append the html input into
// the .dz-preview is the target that i want to append
$(".dz-preview[id='"+response+"']").append("<input type='hidden' name='"+fileInputName+"[]' v-model='programmeBanner' class='"+fileInputName+"' value='"+response+"'/>");
});
},
...
Here is a sample that i want to achieve, this is in Jquery, i need it in Vue.js
https://jsfiddle.net/041xnfzu/
Hmm I think you're mixing all kinds of code here :)
First off, you shouldn't use jquery inside VueJS. I think that your setup is a little off. You shouldn't define a whole object with functions and event listeners in your vue data object.
That's what Vue components are for, define methods in your methods property and data in you data property.
Thanks to your jsfiddle example, I have this pure vuejs example for you on codepen: https://codepen.io/bergur/pen/vwRJVx
VueJS code:
new Vue({
el: '#demo',
name: 'Adding html',
data() {
return {
inputs: []
}
},
methods: {
addInput() {
this.inputs.push(this.inputs.length+1)
}
},
computed: {
buttonText() {
return this.showInput ? 'Hide input' : 'Show input'
}
}
})
HTML template
<div id="demo">
<button #click="addInput">Add input</button>
<div v-for="(input, index) in inputs">
<input name="data" v-model="inputs[index]" />
</div>
<p>
First value: {{ inputs[0] }}<br />
Second value: {{ inputs[1] }}
</p>
</div>
Here's a walkthrough of the code.
We create a data property called inputs, that is an array.
We create a method called addInput and all that does is to push a new item into the inputs array
In the template we loop with v-for through our inputs array and render a input for each item in our inputs data property.
We then use v-model to bind each input to its corresponding place in the inputs array.
You can try to change the input value and see that the template updates the value.
So input[0] holds the value for the first input, input[1] holds the value for the second input and so on.
If you want only one element to be appended to component, then you should use v-if
https://v2.vuejs.org/v2/guide/conditional.html#v-if
If you want to append multiple elements, like todo list, you should use v-for
https://v2.vuejs.org/v2/guide/#Conditionals-and-Loops

#ContentChildren not picking up items inside custom component

I'm trying to use #ContentChildren to pick up all items with the #buttonItem tag.
#ContentChildren('buttonItem', { descendants: true })
This works when we have the ref item directly in the parent component.
<!-- #ContentChildren returns child item -->
<parent-component>
<button #buttonItem></button>
<parent-component>
But, if the element with the #buttonItem ref is wrapped in a custom component, that does not get picked by the #ContentChildren even when I set the {descendants: true} option.
<!-- #ContentChildren returns empty -->
<parent-component>
<child-component-with-button-ref></child-component-with-button-ref>
<parent-component>
I have created a simple StackBlitz example demonstrating this.
Doesn't appear to be a timeline for a resolution of this item via github... I also found a comment stating you cannot query across an ng-content boundary.
https://github.com/angular/angular/issues/14320#issuecomment-278228336
Below is possible workaround to get the elements to bubble up from the OptionPickerComponent.
in OptionPickerComponent count #listItem there and emit the array AfterContentInit
#Output() grandchildElements = new EventEmitter();
#ViewChildren('listItem') _items
ngAfterContentInit() {
setTimeout(() => {
this.grandchildElements.emit(this._items)
})
}
Set template reference #picker, register to (grandchildElements) event and set the $event to picker.grandchildElements
<app-option-picker #picker [optionList]="[1, 2, 3]" (grandchildElements)="picker.grandchildElements = $event" popup-content>
Create Input on PopupComponent to accept values from picker.grandchildElements
#Input('grandchildElements') grandchildElements: any
In app.component.html accept picker.grandchildElements to the input
<app-popup [grandchildElements]="picker.grandchildElements">
popup.component set console.log for open and close
open() {
if (this.grandchildElements) {
console.log(this.grandchildElements);
}
else {
console.log(this.childItems);
}
close() {
if (this.grandchildElements) {
console.log(this.grandchildElements);
}
else {
console.log(this.childItems);
}
popup.component change your ContentChildren back to listItem
#ContentChildren('listItem', { descendants: true }) childItems: Element;
popup.component.html set header expression
<h3>Child Items: {{grandchildElements ? grandchildElements.length : childItems.length}}</h3>
Stackblitz
https://stackblitz.com/edit/angular-popup-child-selection-issue-bjhjds?embed=1&file=src/app/option-picker/option-picker.component.ts
I had the same issue. We are using Kendo Components for angular. It is required to define Columns as ContentChilds of the Grid component. When I wanted to wrap it into a custom component and tried to provide additional columns via ng-content it simply didn't work.
I managed to get it working by resetting the QueryList of the grid component AfterViewInit of the custom wrapping component.
#ViewChild(GridComponent, { static: true })
public grid: GridComponent;
#ContentChildren(ColumnBase)
columns: QueryList<ColumnBase>;
ngAfterViewInit(): void {
this.grid.columns.reset([...this.grid.columns.toArray(), ...this.columns.toArray()]);
this.grid.columnList = new ColumnList(this.grid.columns);
}
One option is re-binding to the content child.
In the template where you are adding the content child you want picked up:
<outer-component>
<content-child [someBinding]="true" (onEvent)="someMethod($event)">
e.g. inner text content
</content-child>
</outer-component>
And inside of the example fictional <outer-component>:
#Component()
class OuterComponent {
#ContentChildren(ContentChild) contentChildren: QueryList<ContentChild>;
}
and the template for <outer-component> adding the <content-child> component, re-binding to it:
<inner-template>
<content-child
*ngFor="let child of contentChildren?.toArray()"
[someBinding]="child.someBinding"
(onEvent)="child.onEvent.emit($event)"
>
<!--Here you'll have to get the inner text somehow-->
</content-child>
</inner-template>
Getting that inner text could be impossible depending on your case. If you have full control over the fictional <content-child> component you could expose access to the element ref:
#Component()
class ContentChildComponent {
constructor(public element: ElementRef<HTMLElement>)
}
And then when you're rebinding to it, you can add the [innerHTML] binding:
<content-child
*ngFor="let child of contentChildren?.toArray()"
[someBinding]="child.someBinding"
(onEvent)="child.onEvent.emit($event)"
[innerHTML]="child.element.nativeElement.innerHTML"
></content-child>
You may have to sanitize the input to [innerHTML] however.

Depth First Search using nested ng-Repeat

I have an object with variable key names, which also contain objects. I would like to do a depth-first search (DFS) over the object.
An example object:
object = { "var1" : {
"var2a" : {
"someKey" : someValue
},
"var2b" : {
"someKey" : someOtherValue
}}}
My attempt at a DFS is as follows (which will not work):
<div ng-repeat = "child in object">
<div ng-repeat = "grandChild in child ">
<td> {{grandChild.aKey}}</td>
</div>
</div>
I have seen the approach of others in this previous question, however this will not work for me due to variable key names.
Additionally, the depth of the object is known.
If we use this code snippet to check what the result of child is:
<div ng-repeat = "child in object">
<td> {{child}}</td>
<div ng-repeat = "grandChild in child ">
<td> {{grandChild.aKey}}</td>
</div>
</div>
We get the first instance of child to be:
{"var2a" : {
"someKey" : someValue
}}
EDIT: Could it be possible to utilise this JavaScript DFS snippet that will output to console?
angular.forEach(object,function(child,value){
angular.forEach(child,function(grandChild,value){
console.log(grandChild.someKey)
});
});
So, it turns out that this is not actually possible using nested ng-Repeats. It must be done in JavaScript. This actually works in line with the 'Angular JS' mentality of keeping logic out of the HTML file.
Here is the DFS in JavaScript:
angular.forEach(object,function(key,value){
if (value!='$id'){
angular.forEach(key,function(keyChild,valueChild){
console.log(valueChild)
}
});
});
This will return "someKey" : someOtherValue and "someKey" : someOtherValue

How to dynamic add new element in Angularjs

I have a ng-repeat to loop my object value to view.
Then I want to have a button to add new blank element to the last of ng-repeat value.
How can I do this in angular?
My data is json object. I tried
In controller
$scope.objs = {'a': 'a', 'b':'b'};
In view
{{Object.keys(objs).length}};
But nothing show in view.
Update
<div ng-repeat="docstep in docs.docsteps" class="docstep">
{{docstep.text}}
</div>
Then I want to get the length of objects so I can .length + 1 in the button click
But I have no idea how to get objects length. Or is there any better idea?
Bind a click handler to the button using ng-click:
<div ng-repeat="docstep in docs.docsteps" class="docstep">
<input type="text" value="{{docstep.text}}">
</div>
<button ng-click="addNew()">Add another input</button>
When this button is clicked. It will add another blank input
<br>Which the new input will be docstep3
This is how your JS should look:
var myApp = angular.module('myApp',[]);
myApp.run(function($rootScope){
$rootScope.docs = {
"docsteps" : {
"docstep1" : {
"text" : "a"
},
"docstep2" : {
"text" : "b"
}
}
}
var c = 2;
$rootScope.addNew = function(){
count++;
$rootScope.docs.docsteps["docstep"+count] = {"text":count}
}
});
NOTE: You should use ng-app to define work area for angular and use controllers to reside the models(docs) and define the behaviour of your view (addNew).
I took your ng-repeat and made it work. Notice I put your object in the $rootScope but you can apply the same object to any scope that your ng-repeat is in.
JS
var myApp = angular.module('myApp',[]);
myApp.run(function($rootScope){
$rootScope.docs={docsteps:[{text:'A'},{text:'B'},{text:'C'}]};
});
JSFIDDLE: http://jsfiddle.net/mac1175/Snn9p/

Dropdownlist binding to an object not returning selected object

I am binding an object to a dropdownlist using Knockout 2.2.1. The binding is working for putting the correct items in the list but when I try to get the OBJECT selected it is not working. I have a JSFiddle showing this problem; http://jsfiddle.net/CTBSTerry/g4Gex/
Html
<div style="margin-bottom: 15px;">
Your Choices:
<select data-bind="options: choicelists[0].options, optionsText: 'otext', optionsValue: 'oprice', value: selOpt1, optionsCaption: 'Choose...'"></select>
</div>
<div data-bind="visible: selOpt1" style="margin-bottom: 15px;"> <!-- Appears when you select something -->
You have chosen<br>
From Object:
<span data-bind="text: selOpt1() ? selOpt1().otext : 'unknown'"></span>
<br>From Value:
<span data-bind="text: selOpt1() ? selOpt1() : 'unknown'"></span>
</div>
JavaScript:
var mychoice = function (txt, price) {
this.otext = txt;
this.oprice = price;
}
var viewModel = {
prodphoto: "",
prodname: "",
prodDesc: "",
baseprice: "",
choicelists: [
{ 'listlabel': 'Size List',
'options': ko.observableArray([
new mychoice('Small', 'Small|$|0.00'),
new mychoice('Medium', 'Medium|$|0.00'),
new mychoice('Large', 'Large|$|0.00'),
new mychoice('X Large + 2.00', 'X Large|$|2.00'),
])
}],
textlists: [],
selOpt1: ko.observable()
}
ko.applyBindings(viewModel);
When you click the dropdown to make a choice I have 2 spans that attempt to show the selected value which I want as the object selected not just the specific value field. The object notation returns nothing but does not error. The second span shows the selected value but since it is not the selected object I would have to iterate through the object to get the related object. The Knockout documentation shows a very similar sample but I need a bit more complex view model. Can someone help me and point out why this is not working?
Thanks,
Terry
If you remove optionsValue from your binding, then Knockout will use the actual object rather than a property on it.
So, you would want to remove optionsValue: 'oprice' from the binding, then selOpt1 will be populated with the actual object.
Sample: http://jsfiddle.net/rniemeyer/g4Gex/1/