I used yeoman angular generator to scaffold my app, and now I'm working on a specific view.
I'm building a simple ng-repeat function, in order to clean up my html and avoid repeating the same markup 4 times.
I am making content panels, and want to add a class specific to the content, so I can style it with Sass. I tried using ng-class but so far I've been unable to make it work.
I am new to AngularJS, I tried reading the official ng-class documentation and this writeup but so far I'm unable to make it work. I guess it just takes a while to get used to Angular.
here's my view:
<div data-ng-controller="LandingController">
<div ng-repeat="panel in panels">
<div class="panel-heading">
{{ panel.title }}
</div>
<div class="panel-content" ng-class="{{panel.contentClass}}" data-ng-bind-html="panel.content">
</div>
</div>
</div>
and the controller:
'use strict';
angular.module('angularApp')
.controller('LandingController', function ($scope) {
$scope.panels = [
{
title: 'lokacija',
contentClass: 'location',
content:'<p>Institut Jožef Stefan<br>Jamova 30<br>1000 Ljubljana</p><br><div class="map-container"></div>'
},
{
title: 'kontakt',
contentClass: 'location',
content: ''
},
{
title: 'delovni čas',
contentClass: 'location',
content: ''
},
{
title: 'katalog',
contentClass: 'location',
content: ''
}
];
});
ng-class operates in three different ways, depending on which of three types the expression evaluates to:
If the expression evaluates to a string, the string should be one or more space-delimited class names. (this will match with your case)
If the expression evaluates to an object, then for each key-value pair of the object with a truthy value the corresponding key is used as a class name.(EX: 'ng-class="{'my-class': (true/false expression)}")
If the expression evaluates to an array, each element of the array should either be a string as in type 1 or an object as in type 2. This means that you can mix strings and objects together in an array to give you more control over what CSS classes appear. See the code below for an example of this. (EX: ng-class="[style1, style2, style3]") refer ng-class doc and you will get more info.
so you can directly use ng-class="panel.contentClass" this will match with the first list item witch is If the expression evaluates to a string, the string should be one or more space-delimited class names..
you can simply change ng-class="{{panel.contentClass}}" to ng-class="panel.contentClass" because ng-class accepts a expression not a interpolated value.
<div class="panel-content" ng-class="panel.contentClass" data-ng-bind-html="panel.content">
DEMO
You are using ng-class in wrong way. ng-class is use to add/remove class conditionally and dynamically.
Here is full documentation of ng-class.
ng-class requires json in which key is class name and value is condition. if value satisfies, it add that class else not.
for Ex.
<div class="panel-content" ng-class="{'my-class':panel.contentClass}" data-ng-bind-html="panel.content">
it adds my-class if value of panel.contentClass is true.
if you are taking class name from panel.contentClass then better way to use class attribute only instead of ng-class. Here is reference.
For ex.
<div class="panel-content, {{panel.contentClass}}" data-ng-bind-html="panel.content">
Related
In Angular, how to use *ngIf to check whether a JSON value includes a certain string, and then show them a certain URL ? In my case I have a object name called campaigns.description which has a value that includes a description. I want to see whether a given string, for example "one beam" is included in that description and show an URL based on that.
So not the way that the value equals a certain string, but the text that is held within the value includes a certain string.
You can use indexof() function to check the existence of some substring inside a string. This function returns '-1' if the substring is not present in the string.
<label *ngIf="campaigns.description.indexOf('One Beam') != -1 ? true : false">{{urlToShow}}</label>
You could generally use indexOf to check whether a string contains a sub-string.
console.log("Sample string".indexOf('string'));
console.log("Sample string".indexOf('not'));
The Angular part:
Trivial (not recommended)
Trivial solution is to check directly in the *ngIf condition
<div *ngIf="campaigns.description.indexOf('one beam') !== -1; else other">
<!-- contains the sub-string -->
</div>
<ng-template #other>
<!-- does not contain the sub-string -->
</ng-template>
However binding a function to *ngIf directive with default change detection strategy would trigger the function for each change detection cycle. It might lead to performance issues.
Additional property (recommended)
You could introduce additional property to hold the result of the condition in the controller and use it in the template.
Controller (*.ts)
// I assume `campaigns` is initialized in a subscription
ngOnInit() {
someObservable.subscription(
(res: any) => {
this.campaigns = {
...res,
subString: res.description.indexOf('one beam') !== -1
}
},
(error: any) => { }
);
}
Template (*.html)
<div *ngIf="campaigns?.subString; else other">
<!-- contains the sub-string -->
</div>
<ng-template #other>
<!-- does not contain the sub-string -->
</ng-template>
I am struggling using enum values in list filters in Thymeleaf.
I know how to iterate over enum values and how to compare them against constant values. However, I want to compare it against a 'variable' value. How can I achieve it?
In my example below, I want to iterate trough all colors (enum) and then filter a list of cars by the current color enum and display their names.
How do I specify the list filter in the second <div> correctly?
<div th:each="currentColorEnum : ${T(de.my.enum.color).values()}">
<div th:each="currentCar, carStatus : ${model.carList.?[#this.colorEnum eq __${currentColorEnum}__]}">
<textarea th:field="*{carList[__${carStatus.index}__].carName}"></textarea>
</div>
</div>
Current error message:
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'red' cannot be found on object of type 'de.my.class.car' - maybe not public or not valid?
No need for preprocessing in this case. It's failing because ${model.carList.?[#this.colorEnum eq __${currentColorEnum}__]} resolves to ${model.carList.?[#this.colorEnum eq red]}. Which means it looking for cars where car.colorEnum == car.red -- hence the error field 'red' cannot be found on object of type 'de.my.class.car'.
Your Thymeleaf should look something like:
<div th:each="currentColorEnum : ${T(de.my.enum.color).values()}">
<div th:each="currentCar, carStatus : ${model.carList.?[colorEnum eq #root.currentColorEnum]}">
<textarea th:field="*{carList[__${carStatus.index}__].carName}"></textarea>
</div>
</div>
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>
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.
:-)
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")