Check for "Not in" a list condition Angular 2 template - html

I have a template as below:
<span *ngIf="item.status !=='E1' && item.status !=='B' && item.status !=='R'">{{item.status_desc}}</span>
As above, i have a ngIf condition there, making no sense but somehow it working. What am trying to do there is check "status in [E1, B, R]" something like that. How can i do that in the html without going to the ts file. Any idea guys?

In your HTML, you could use includes(), which returns true if the element is found:
<span *ngIf="!['E1', 'B', 'R'].includes(item.status)">{{item.status_desc}}</span>
as JB Nizet suggested.
Or you could use a function, like this:
statusList = [E1, B, R];
checkStatus(item)
{
return (statusList.indexOf(item) != -1);
}
where your HTML now should llol like this:
<span *ngif="checkStatus(item.status)">{{item.status_desc}}</span>

If you really don't want to go to your TypeScript source, you can do something like this for increased readability.
<span *ngIf="!['E1', 'B', 'R'].includes(item.status)">{{item.status_desc}}</span>
But perhaps it's wiser to make a variable on your class with the 'undesired' statuses like:
public ignoreStatus: string[] = ['E1', 'B', 'R'];
and then
<span *ngIf="!ignoreStatus.includes(item.status)">{{item.status_desc}}</span>
but then it would be even better to make a reusable method out of this in your class:
public isIgnoreStatus(item: any): boolean {
return this.ignoreStatus.includes(item.status);
}
with
<span *ngIf="!isIgnoreStatus(item.status)">{{item.status_desc}}</span>

No you cannot do that with template, what you can do is create a function that does the job for you
statuses = [E1, B, R];
checkValid(item){
return (statuses.indexOf(item) != -1);
}
then in HTML
<span *ngIf="checkValid(item.status)">{{item.status_desc}}</span>

You can create a pipe to do all this filters.
In that pipe you can write any logic to remove all unwanted elements.
It will make your code look better and understandable.

Related

React: Skip row if json parent tag don't exist

I am in the following situation and have the following problems.
I am developing an application that reads data from a .json file. I store this data in rows. Now the .json files can be different and therefore I have to cover every case (there are many cases). If a tag is not present in the .json, this row should not be displayed.
Now it can happen that I have covered a case which searches for data.test.person but data.test is not present in the JSON.
A case could look like this:
<TestAntragsdatenAbschnitt
title={"Test"}
value={data.test}>
<TestAntragsdatenRow
label1={"Person"}
value1={data.test.person}
/>
</TestAntragsdatenAbschnitt>
This is my component.
export default class TestAntragsdatenAbschnitt extends React.Component {
value;
title;
render() {
this.value = this.props.value;
this.title = this.props.title;
return (
<>
<h4 className={"antragsdatenAbschnitt"}>
{checkAbschnitt(this.title, this.value)}
</h4>
{this.value != null &&
this.props.children
}
</>
);
}
}
With the query this.value != null && I have tried to work around the error.
The error I get: TypeError: Cannot read property 'person' of undefined
My question now is, how can I query JSON tags if they exist, if so the rows should be checked. If not all rows with this tag should be skipped.
Object.keys(data).includes('test')
can be a way of checking if json object has the property.
There are a lot of other ways as well. You can also try:
value1={data.test ? data.test.person : 'error'}
kind of approach for safe null-checking
There are a couple of things wrong with your code, but let's try to unpack everything:
You're passing two props, title and value to your component named TestAntragsdatenAbschnitt. This you should receive in props, and therefore there's no need for you to store them in fields (this.value = this.props.value) for example.
Rather, just do this in the render function:
const {title, value, children} = this.props;
And now you don't need to use this.title and this.value, but just title and value.
OK we got that out of the way, now let's figure out why your optional key isn't working:
Now you should be rendering your children like this:
{children}
Now you need to conditionally render particular rows, and this shouldn't be done here in this component at all. This should be done in the component that's rendering TestAntragsdatenAbschnitt (first component you wrote in the post).
So you'd do something like this:
<TestAntragsdatenAbschnitt
title={"Test"}
value={data.test}>
{data && data.test && data.test.person ? (
<TestAntragsdatenRow
label1={"Person"}
value1={data.test.person}
/>
) : (
<p>data.test.person is not valid</p>
)}
</TestAntragsdatenAbschnitt>
As you can see, I check with the ternary operator if data.test.person is not null, if it isn't then just render the row, if not then just do something else you'd like.
You could do this in other component, but this way it's way cleaner in my opinion.

Access filter result Angular 6

How can I access filteredArray in my .ts component? Because right now it is accessible only inside ng-container.
<ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray">
<tr *ngFor="let user of filteredArray">
<td>{{user.name}}</td>
<td>{{user.group}}</td>
</tr>
<div>Count: {{ filteredArray.length }}</div>
</ng-container>
How can I modify the code in order to obtain what I want? Thank you for your time!
To answer your question directly: it's not possible the way you describe it. But read on.
Pipes (sometimes still called "filters") should be used only to format data, i.e. prepare it in a human-readable form. For example, the build-in date pipe can be used to transform an ISO string to a string such as "March 21st, 1995", which is how a human from the USA might expect to read the date.
The way you're using pipes is not recommended, precisely because of the question you have. You've essentially put application logic inside a template, which is an anti-pattern and beats the purpose of having easy-to-read declarative templates, which Angular uses in order to figure out how to update DOM.
You should move the filtering logic back to the class. For example, instead of setting this.userList = xxx, you could have a function which you call every time, such as this.changeUserList(xxx).
changeUserList (list) {
this.userList = list
this.filteredArray = list.filter(...)
}
You can put this logic in a setter as well, which allows you to run custom code when you write the usual this.userList = list, but you'll need a separate (usually prefixed private) property on the class where you'd actually store the value. It's not really a limitation since you can also have a trivial getter, so you can still us this.userList normally as a getter without having to remember to use this._userList, essentially tucking this away as the get/set pair's implementation detail.
private _userList
public set userList (list) {
this._userList = list
this.filteredArray = list.filter(...)
}
public get userList (list) { return this._userList }
Observables could really come in handy here as well, since you could just rx.map the userList$ to filteredArray$ with an Array#filter.
public userList$
public filteredArray$ = this.userList$.pipe(map(arr => arr.filter(...))
Then in the template, you can use the async pipe.
*ngIf="filteredArray$ | async as filteredArray"
Avoid doing the following.... but it works for demo purposes 😃
Create a component (e.g. demo-element.component.ts) that takes a single #Input() value:any
Add this new component as the first child of the <ng-container>, and give it a template reference #containerRef e.g.:
<ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray">
<demo-element #containerRef [value]="filteredArray"></demo-element>
In your main component, add
#ViewChild('containerRef') ref;
ngAfterViewInit() {
this.filteredArray = this.ref.value; // Terrible pattern, but answers the question:-)
}
I hope this below code will help you.
<div class="rsrvtn_blk" *ngIf="(items | fSearch:firstname) as filteredItems">
<div class="col-md-3 pl-0" *ngFor="let item of filteredItems">
// you can display the filtered content here
</div>
</div>

angular 2+ component with attribute name and no parameters

I want to allow a user to provide a list of one-word attributes without parameter values. For example,
<container row crosscenter wrap spacearound ...>
which results in something like this in container.html
<div [ngClass]="{ 'flexDisplay': true, 'directionRow': isRow, 'directionCol': isCol, 'contentSpaceAround': isSpaceAround}" ...>
What I'm missing is how to set
#Input('row') isRow = false;
to true if 'row' was present in the container line.
Any ideas?
Thanks in advance.
Yogi
This can be handled in ngOnChanges. The value can be assigned either back to input property or to some object that will be passed to ngClass
ngOnChanges(changes: SimpleChanges) {
if ('foo' in changes) {
this.options.foo = true;
}
}
Since there's no way how inputs can become unassigned, there's no reason to provide bindings for them. #Attribute can be used instead:
constructor(#Attribute('foo') public foo: boolean|null) {
this.foo = (foo != null);
}
Using attributes for regular options isn't a good decision, design-wise. This prevents from setting them dynamically. Instead, it is always preferable to accept options input. If all options are supposed to be flags, it can be a string input that will be split and processed in ngOnChanges, like:
<container options="row crosscenter wrap spacearound">
or
<container [options]="'row crosscenter wrap spacearound'">
I think the answer to my question is to create directives for each of the "one-word" tags (attributes) I want to use.
:-)

how to use [ngClass] for multiple class conditions Angular4

currently my div looks like this:
<div class="class-a" [ngClass]="{'class-b': !person.something}">
now I want to have another condition...
so now I want this div to be of class-a If something class-b If something else class-c
how should I do this?
im using angular 4.
thanks!
Add it like properties to an object literal:
[ngClass]="{'class-b': !person.something, 'other-condition': isOther }"
Another option is to return a string from the component if you think you need more complex logic, or know there will only be one. This might be more testable.
Whatever string you return will be rendered as a class(es)
[ngClass]="renderClass()"
renderClass() {
switch(this.user.theme){
case "dark":
return "dark-theme"
case "light":
return "light-theme"
}
}
The better way for use this Syntax ngStyle Because,
it's Not Completed Answer.
If you want to toggle some classes like text-info Or text-danger for <i> tag (
some exp ? 'text-info' : 'text-danger'
).
The best answer is array not object.
[ngClass] = "[some exp ? 'text-info' : 'text-danger', ...]"
GoodLuck

IF condition with <div> in the view file of MVC

I am able to pass data to view file, but need to display them in different format with .
I try to use if statement to check the form name.
It returns error said "Operator '==' cannot be applied to operands of type 'System.Web.HtmlString' and 'string'".
Can I add blocks within IF condition? how to validate data within if condition in view file? Thank you! Here is the code:
#{ foreach (var form in #ViewBag.FormContent)
{
if (Html.Raw(form.Name) == "xyz") //pull up the title and text for the form
{
#Html.Raw(form.FormTitle)
<div class="panel-body">
<div style="height: 300px; overflow: auto; padding:15px;">
#Html.Raw(form.FormText)
</div>
</div>
}
}}
HTML.Raw returns an object that implements IHtmlString. It does not return a string. It doesn't even support ToString. Its only member is ToHtmlString(), which returns a string.
If you want to compare the output of Html.Raw with "xyz", you need to convert it to a string first. So instead of
if (Html.Raw(form.Name) == "xyz")
use something like
if (Html.Raw(form.Name).ToHtmlString() == "xyz")
Or... don't even bother with Html.Raw to begin with. Don't think you need it. Just write this:
if (form.Name == "xyz")