I have a small expression to check whether 2 objects are different or not, in order to display this element (via adding class name):
<div ngClass='{{JSON.stringify(obj1) != JSON.stringify(obj2) ? "div-show" : ""}}'></div>
The problem is I get this error:
Cannot read property 'stringify' of undefined.
What I need a way to work around, or a proper solution if available. Thanks.
PS: I use JSON.stringify() to compare 2 simple objects, nothing fancy here.
Template code doesn't have access to all javascript, only component properties and methods. I think it would be best to create a 'stringify' method on your component, but you could just set a JSON property:
public constructor() {
this.JSON = JSON;
}
Also I think your expression is backwards. NgClass takes the css class as the property name and a true/false value to tell whether that class is on the element, and it needs to be in brackets:
<div [ngClass]="{'div-show': JSON.stringify(obj1) != JSON.stringify(obj2)}"></div>
2 Years later but, you can do it the Angular Way using the built in pipe 'JsonPipe' from #angular/common
#Component({
selector: 'json-pipe',
template: `<div>
<p>Without JSON pipe:</p>
<pre>{{object}}</pre>
<p>With JSON pipe:</p>
<pre>{{object | json}}</pre>
</div>`
})
export class JsonPipeComponent {
object: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2, 3, 4, 5]}};
}
you can achieve it like this in your component do this.
myjson:any=JSON;
and in you view do it like this
<div ngClass='{{myjson.stringify(obj1) != myjson.stringify(obj2) ? "div-show" : ""}}'></div>
Related
I am in process on creating a small poc to try whether is it possible to load components according to a given json data structure. json will provide and array of component selectors. I tried a small example according to the reference materials i found via online. I used the "componentFactoryResolver" which is recommended way by Angular
I basically create couple of components and registered it with the entrycomponent decorator as follow in my module
entryComponents: [PersonalDetailsComponent, ContactDetailsComponent],
and in my app component i use the following code
#ViewChild('dynamicInsert', { read: ViewContainerRef }) dynamicInsert: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
ngAfterViewInit() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(PersonalDetailsComponent );
const componentFactory2 = this.componentFactoryResolver.resolveComponentFactory(ContactDetailsComponent);
this.dynamicInsert.clear();
this.dynamicInsert.createComponent(componentFactory);
this.dynamicInsert.createComponent(componentFactory2);
}
and as you see i have to create component for each and every component i use. but having this an inside a loop might not be the best way to do it. i would much appreciate if any one could give me some heads up to do it in a proper way.
my actual json would look like something like this
{
"step":"1",
"viewed":false,
"stepDependant":{
"parentComponent":null,
"childComponent":null,
"varMap":null
},
"widgets":[
{
"Component":"shipper",
"inputs":[
{
"ServiceLine":"Export"
}
],
"outputs":[
],
"name":"Shipper Details"
},
{
"Component":"shipper",
"inputs":[
{
"ServiceLine":"Export"
}
],
"outputs":[
],
"name":"Consignee Details"
},
{
"Component":"status-of-shipment",
"inputs":[
],
"outputs":[
],
"name":"Status of Shipment"
}
]
}
much appreciate your inputs
As you have already found the componentFactoryResolver is the correct way to create components dynamically from code.
With this approach what I would do in your case is create a map or service that maps the component selectors to component types. This way you can then quickly lookup the type when you are creating the dynamic components from the JSON data. From the types you then resolve the factory and then add the components like in your sample.
If you have a predefined set of components that are known another alternative would be to define them all as <ng-template> in the parent component like this:
<ng-template #shipper><shipper ></shipper></ng-template>
<ng-template #statusOfShippment><status-of-shipment ></status-of-shipment></ng-template>
Then you can get the templates in the component by using the #ViewChild decorator.
#ViewChild('shipper')
shipperTemplate: TemplateRef<any>;
#ViewChild('statusOfShippment')
statusOfShippmentTemplate: TemplateRef<any>;
And then you can create the components in a simmilar fashion than with the factory.
this.dynamicInsert.createEmbeddedView(shipper);
this.dynamicInsert.createEmbeddedView(statusOfShippment);
What is good about this approach is that you can still have classic template binding and send a different context object to every template.
<ng-template #shipper><shipper [ServiceLine]="ServiceLine"></shipper></ng-template>
this.dynamicInsert.createEmbeddedView(shipper, {ServiceLine:"Export"});
This way you could directly send an object created from your JSON and configure the component bindings. If you use the component factory you need to set everything from code manually.
So I am still kind of a novice at Angular 2. My API call returns an object with several arrays and objects within it.
This is the JSON object I receive when i make the get request
{"report":[{"customer_name":"kaminto" ,
"customer_address":"Second","tel_no":"Second","id":"15","order_no":"RC13",
"total_amount":"28000","amount_paid":"30000","balance":"-2000",
"sale_date":"2017-08-15" ,"customer_id":"21"},"}], "message":"success" ,
"totalSales":[{"amount_paid":"1174300"}]}
I want to output the customer_name and address but they are within the Report array.
Using observables, How can I save this information into objects that i can bind to the html.
Please help.
[SOLVED]
I know this is embarrassing, but i solved it. I just had to add the string type and array brackets to the interface report variable.
export interface ISalesReport{
report:string[];
message:string;
totalSales:string[];
}
I don't know exactly why, but after i did this, it worked.
You get that object from Observable? Yes, it is not necessary if you only need value of two properties. Something like this should help:
customer_name;
customer_address;
yourObservable.map(allReports => allReports.map(report => report
.map(val => { this.customer_name = val.customer_name;
this.customer_address = val.customer_address } ));
Suppose you called your service in your component that returns an observable of the GET call.
In your Service,
getCall() {
return this.http.get(...).map((res) => res.json());
}
In your Component, (say you assign to a property),
responseObj: Observable<any>;
// say you receive values in ngOnInit(),
ngOnInit() {
this.responseObj = this.service.getCall();
}
In your html, use the async pipe for eternally binding it to the observable,
<div [innerHTML]="(responseObj | async)?.report[0].customer_name"></div>
<div [innerHTML]="(responseObj | async)?.report[0].customer_address"></div>
Hope it helps.
I am currently getting json object from server and the object also has many nested json object as well. So far, I've been using *ngFor = "let a of data | pipe" (the pipe to get deeply nested value) and single interpolation {{a.value['someValue']}} to get the deep nested value of json object for other situations but this isn't serving my purpose right now since I don't want to loop my json.
Is there any way to get deeply nested json value without using ngFor?
The part of json object I am getting from server.
UserProfile:
{name: 'Jess'
University: 'UC Berkley'
Major: 'Media Communication'
birthday: 1994}
categoryInfo:
["inish work with quality"]
currentArea
:"CA"
introInfo {
experience: [
0: {Company: 'Atlas', workingYears: 1, **recLetter**:'She was on time always,
never late. The quality of her work is very high-level.'}
1: {Company: 'Footstep', workingYears: 2, recLetter:'She was on time always,
never late. The quality of her work is very high-level.'}
]
introduction: "Hello I'm Jess"
}
And if I use aforementioned method, it will just loop 4 keys (UserProfile, categoryInfo, currentArea, and introInfo) which I don't want.
How can I get value that's in bold (recLetter) without using *ngFor?
in my component, I am doing this.
userInfo: UserDetailInfo[];
getUserDetail(): void {
this.userDetail.getUserDetail()
.subscribe
(
userInfo => this.userInfo = userInfo,
error => this.errorMessage = error
)
}
And I tried this in the html template but didn't work and I didn't know how to get 'recLetter'
{{userInfo.experience['0']}}
Please help!
Thank you in advance
For the starters, lets assume you get experience array always the same, with 2 elements.
The only thing that you need to do in the html is this:
{{ userInfo.experience[0].recLetter }}
In case you want to loop through the whole array exeperience and display recLetter you can do this:
<div *ngFor="let item of userInfo.experience">
{{item.recLetter}}
</div>
Try this
properties.pipe.ts
import {Pipe} from '#angular/core';
#Pipe({name: 'properties'})
export class PropertiesPipe {
transform(o: {}) {
return Object.entries(o).map(([key, value]) => ({
key,
value
}));
}
}
app.module.ts
import {propertiesPipe} from './properties.pipe';
#NgModule({
declarations: [PropertiesPipe, /* whatever else was here */],
// ... whatever else was here
}) export class AppModule { }
component.html
<ul>
<li *ngFor="property of userInfo | properties">
<span *ngIf="!Array.isArray(property.value)">
{{property.key}}: {{property.value}}
</span>
<span *ngIf="Array.isArray(property.value)">
{{property.key}}: <span *ngFor="value of property.value">{{value}}, </span>
</span>
</li>
</ul>
I'm writing a REST API on Laravel and I'm taking advantage of polymorphic relationships. So many resources have tied images. When I create the response, using eager loading like this:
return User::with('image')->find($id);
I get something like this:
{
id: 27,
image: {
id: 340,
url: "https://amazonbucket.com/2lBqWzDme.jpg",
created_at: "2015-01-28 17:21:17",
updated_at: "2015-01-28 18:28:42",
}
}
Instead, I want it to be automatically output just the url as a string like this, so it's easier on the frontend:
{
id: 27,
image: "https://amazonbucket.com/2lBqWzDme.jpg"
}
I tried to work with the Image::toJson() method but it didn't work. I Could use the User::toJson() method, but I'd had to do it on all the existing/upcoming instances that use images. Ideas?
As it happens usually, I found a better, less invasive way of doing this looking for something else:
Just add the $appends protected attribute to your model and set the getImageAttribute() method.
protected $appends = ['image'];
function getImageAttribute() {
return $this->images()->first()->url;
}
Check the bottom of http://laravel.com/docs/4.2/eloquent#converting-to-arrays-or-json.
It works like charm!
Overriding the Image::toJson() method won't help here. In this case, the truncated call stack would be User::toJson() --> User::toArray() --> Image::toArray().
So, now the problem becomes that in order to do what you want to do, you would have to override the Image::toArray() method so that it no longer returns an array, but instead returns a string containing the url value. I can't imagine this would go over too well, but I've never done it before.
If you're looking to only modify the Image class, the closest you could get would be to assign the Image::visible attribute to only include the url key. This would turn your JSON into:
{
id: 27,
image: {
url: "https://amazonbucket.com/2lBqWzDme.jpg",
}
}
Outside of that, you're looking at a solution where you'll need to modify each class that will be imageable. However, you could extract the required functionality out into a Trait or a base class that your other classes extend. In either case, you're looking to modify the toArray() method, not the toJson() method.
So, I found that array elements can be accessed in Handlebars using:
{{myArray.2.nestedObject}} and {{myArray.0.nestedObject}}
..to get the third and first elements for instance. (handlebars-access-array-item)
Is there a way to get the last element from an array?
I tried creating a helper for it:
Handlebars.registerHelper("lastElement", function(array) {
return array.last(); //Array.prototype extension
});
...and calling it as follows in the template:
{{lastElement myArray}} or even {{lastElement myArray.lastElement nestedArray}}
Sadly, this doesn't work. Helper functions return strings apparently. What I need is a way to be able to do this even with multi-dimensional arrays.
Should work, I've tested it.
Template:
{{last foo}}
Data:
{foo : [1,2,3,4,5,6]}
Helper:
Handlebars.registerHelper("last", function(array) {
return array[array.length-1];
});
The above piece of code works fine in all the cases. But if the array passed if a null array, possibility of the handlebar function throwing error is there. Instead perform a null check and then return the value accordingly.