How will i shuffle input form elements in angular 4 - javascript

I am making an application which will have form with dynamic input elements. With dynamic i mean that those inputs will come from the server side. Here's the psedo sample html :
<form>
<section1>
<static-field-1></static-field-1>
<static-field-2></static-field-2>
<dynamic-field-1></dynamic-field-1>
<dynamic-field-2></dynamic-field-2>
</section1>
<section2>//'a' postfix is just for differenting
<dynamic-field-1a></dynamic-field-1a>
<static-field-1a></static-field-1a>
<static-field-2a></static-field-2a>
<dynamic-field-2a></dynamic-field-2a>
</section2>
</form>
In short all the form rendering including their position will be decided by the api response from the server.
The api response will be something like this:
[
{
"section_pos":1,
"name":"section1",
"fields":[
{
"type":"static",
"name":"static-field-1"
},
{
"type":"static",
"name":"static-field-2"
},
{
"type":"dynamic",
"name":"dynamic-field-1",
"input":"text",
"validation":"required"
},
{
"type":"dynamic",
"name":"dynamic-field-2",
"input":"number",
"validation":"required"
}
]
},
{
"section_pos":2,
"name":"section2",
"fields":[
{
"type":"dynamic",
"name":"dynamic-field-1a",
"input":"text",
"validation":"required"
},
{
"type":"static",
"name":"static-field-1a"
},
{
"type":"static",
"name":"static-field-2a"
},
{
"type":"dynamic",
"name":"dynamic-field-2a",
"input":"number",
"validation":"required"
}
]
}
]
How will i create the dynamic input form elements and shuffle them in their respective section in accordance with the data sent from the server.
With the static i mean , these will be already present/known to us. Dynamic fields are the once that are sent by the server. Server also will sent the order in which each fields(static/dynamic) should be aligned.
Need help in this task

Hope I understood your question right.
If you want to list all fields as INPUT tag in server's response order:
<div *ngFor="let section of mySuperProvider.getDataFromServer()">
<h2>{{section.name}}</h2>
<p *ngFor="let field of section.fields">
<b>{{field.name}}</b>
<input type="text" ... >
</p>
</div>
If you want to shuffle fields, you can do it in provider or:
<p *ngFor="let field of shuffle(section.fields)">
Where shuffle() is a function:
shuffle(array) {
return array.sort(function() { return 0.5 - Math.random() });
}
If you want to show only specific types, and exclude others, then you can use *ngIf.
If you need instead of INPUT tag use Configurable Reactive Forms, then see nice article in this link, with a main idea to create dynamic component and pass config to it:
<form
class="dynamic-form"
[formGroup]="form"
(ngSubmit)="submitted.emit(form.value)">
<ng-container
*ngFor="let field of config;"
dynamicField
[config]="field"
[group]="form">
</ng-container>
</form>

Related

Select/Unselect and get values of dynamically generated checkbox using ngModel directive in Angular

I have a set of data response from an API and dynamically generating checkboxes on my HTML file using DataView component of PrimeNG.
The goal is to have a functionality where I can select/unselect all checkbox via button click and store their values in an array, for example.
Here's what I have so far;
Component HTML
<p-dataView [value]="requestList" {...} >
<ng-template let-request pTemplate="listItem">
<p-checkbox
name="reqNo"
inputId="reqNo"
(click)="getCheckBoxValue()"
value="{{ request.requestNo }}"
[(ngModel)]="reqNo"
[ngModelOptions]="{ standalone: true }"
></p-checkbox>
</ng-template>
</p-dataview>
Main TS File
reqNo: any; reqNo is binded using ngModel.
Giving me arrays of values when consoled;
['R08000036', 'R08000002']
Each object in the API response looks like this;
{
requestNo: "R08000036",
requestBy: "SUPER_USER",
requestTs: "2021-02-18T04:27:05.710+0000",
collectTs: "2008-07-30T16:00:00.000+0000",
testReason: "After Visit",
noOfPrisoner: 2,
status: "Printed",
printTs: "2008-07-21T16:00:00.000+0000",
escortingOfficer: "00552",
}
getCheckBoxValue event handler;
getCheckBoxValue() {
console.log(this.reqNo);
}
I'm new to Angular and I think I'm using the ngModel wrong. Can someone please teach me?
You can select all values by setting a new value for reqNo by values from requestList.
selectAll() {
this.reqNo = this.requestList.map(item => item.requestNo);
}
unselectAll() {
this.reqNo = [];
}
Example

clicking should reveal the rest of the text

I am new to angular and I have a p tag with a list of paragraphs and data(nested array of objects) for p tag will be coming from the backend. I need to truncate the text after some character limit and show....show more. when a user clicks on the p tag it should show reveal the rest of the text. I have figure out the way to truncate the text and display ...show more.clicking should reveal that specific paragraph text but in my case, all other paragraphs texts which are truncated are also showing full texts and since the data is nested array of objects it is tricky for me and i am not able to figure out the solution. I am providing the stackblitz link below. any help will be appreciated.
stackblitz link
data = [
{
comments:[
{text:'this is comment',id:'1'},
{text:'this is comment',id:'2'}
]
},
{
comments:[
{text:'this is comment',id:'3'},
{text:'this is comment',id:'4'}
]
},
{
comments:[
{text:'this is comment',id:'5'},
{text:'this is comment',id:'6'}
]
}
]
showrest:boolean = false
<div *ngFor="let c of data">
<div *ngFor="let comment of c['comments']">
<p (click)="showrest=true">{{showrest?comment.text:(comment.text | slice:0:10)+'...Click to Read More'}}</p>
</div>
</div>
Here is how you could do, see this repro on stackblitz. You need a context for each comment so just create a component for it. Here is the code :
ts:
import { Component, Input } from "#angular/core";
#Component({
selector: "app-comment",
templateUrl: "./comment.component.html",
styleUrls: ["./comment.component.css"]
})
export class CommentComponent {
#Input() comment;
showrest = false;
}
html:
<p (click)="showrest=!showrest">{{showrest?comment.text:(comment.text | slice:0:10)+'...Click to Read More'}}</p>
and you call it as follow :
app.html
<div *ngFor="let c of data">
<div *ngFor="let comment of c['comments']">
<app-comment [comment]="comment"></app-comment>
</div>
</div>
Your problem was that each of your comment were bound to the same variable (Of the same component), so when yu click on a paragraph it update your variable and therefore all the binded element (here your paragraphs).
Another solution could be to add a property to your comment model, something like :
{text:'this is comment',id:'1', isFullyVisible: false},
This way your could handle the display of your comment with this variable. The best solution depends on what you intend to do with your comment. If it's just a display and nothing else then just adding a property to your model is good enough.

angularjs how to search in search in json object in html template

In Angularjs I am trying to search in json data which i am using in html template. My input json data is as below,
var data = JSON.parse(
'{
"Project": {
"_attributes": {
"gui": "ProjectGui",
"prjname": "MyProject"
},
"stringarr": [
{
"_attributes": {
"name": "Project.comments"
},
"_text": "MyComments"
},
{
"_attributes": {
"name": "Project.classpath"
},
"_text": "D:\\Project\\bin\\config.jar"
}
]
}
}'
);
And i am using this for displaying and editing name in my html template, which is working fine. When I edit input box , it reflects the changes in json data as well, that's exactly I want.
Name: <input type="text" ng-model="data.Project._attributes.prjname"><br>
But I also want to display and edit comments in same way but not getting idea how to achieve this. The difference is, I have to search within json data where data.Project.stringProp[i]._attributes.name is "Project.comments" and take "_text" as input for displaying & editing. I have tried following that is not working.
Comments: <input type="text" ng-repeat="x in data.Project.stringProp" ng-model="x">{{x._text}}<br>
Please suggest , what would be the best possible way to do this. I think it can be done using a get function and ng-change function but that approach will be lengthy and may put performance overheads.
Thanks
You can either implement a filter for filter _text value if the name is 'Project.comments', or you can simply add an ng-if statement.
Comments: <input type="text" ng-repeat="x in data.Project.stringarr" ng-if="x._attributes.name == 'Project.comments'">{{x._text}}<br>
I have resolved this by using ng-repeat and ng-if for specified condition.
<body ng-controller="Controller">
Name: <input type="text" ng-model="data[0].Project._attributes.prjname"><br>
check: <div ng-repeat="x in data[0].Project.stringarr" ng-if="x._attributes.name == 'Project.comments'">{{x._text}}</div><br><br>
comments : <input type="text" ng-repeat="x in data[0].Project.stringarr" ng-if="x._attributes.name == 'Project.comments'" ng-model="x._text"><br>
</body>
Please find the following plunkr
https://plnkr.co/edit/3jUaw73cHgAtbBZr8LuJ?p=preview

Use angularjs nested ng-repeat to construct complex table

I'm having trouble making proper table with nested ng-repeat.
What I wanted is this https://jsbin.com/razamagabo/1/edit?output
but I'm stuck at here https://plnkr.co/edit/d5voXIpzYL81sSl9BSY2?p=preview
I don't mind my markup is not table but I'm still stuck with div
<div class="row">
<div class="col-xs-6" ng-repeat="obj in data">
{{obj.date}}
<div ng-repeat="user in obj.users">
<br>
{{user.name}}
<br>
{{user.mark}}
</div>
</div>
</div>
In order for you to be able to display your data in the desired way, it will probably be easiest if you restructure your data in the JS before trying to render it.
It will be very complicated to try and match on the user names when they are in separate objects in the data array.
I would suggest processing your scope.data in the controller. (I'm assuming that you don't have much control on how you are receiving the data).
For example after you get your data...
$scope.data = [
{
date:'1-1-2016',
users:[
{
'name':'james',
'mark':18
},
{
'name':'alice',
'mark':20
}
]
},
{
date:'2-1-2016',
users:[
{
'name':'james',
'mark':60
},
{
'name':'alice',
'mark':55
}
]
}
]
var userData = {};
var possibleDates = [];
for (dataObj of Object.entries($scope.data)) {
for (userObj of dataObj) {
if ( !userData[userObj.name] ) {
userData[userObj.name] = {};
}
userData[userObj.name][dataObj.date] = userObj.mark;
if (dates.indexOf(dataObj.date) < 0) {
dates.push(dataObj.date);
}
}
}
$scope.users = userData;
$scope.dates = possibleDates;
this will give you an object like this on your scope
$scope.users = {
'james': {
'1-1-2016': 18,
'2-1-2016': 60
},
'alice': {
'1-1-2016': 20,
'2-1-2016': 55
}
};
$scope.dates = ['1-1-2016', '2-1-2016'];
This to me seems easier to structure for your template. Though this assumes each user has an entry for each date.
<div>
<div id='header-row'>
<div id='empty-corner></div>
<div class='date-header' ng-repeat='date in $scope.dates></div>
</div>
<div class='table-row' ng-repeat='{key, value} in $scope.users'>
<div class='user-name'>{{ key }}</div>
<div class='user-data' ng-repeat='date in $scope.dates>
{{ value[date] }}
</div>
</div>
</div>
As long as you apply inline-block styles to the rows/elements this should give you what you are looking for.
Though you can also think of ways to simplify your data even further. You could instead of having each user have an object where the dates are keys, you could just push the values into an array.
With your current data structure it is not possible to display it like you want. You are trying to loop over date-users objects in data array but then you want to display user from inside users array in separate rows. With ng-repeat you can loop through rows tr but not through columns. First you would need to map your data array to group elements that are supposed to be visible in 1 row into 1 object in array. Currently you have them in 2 separate objects:
James mark: 18 and James mark: 60.

Limit angular ng-repeat to certain rows

For example if i had the json dataset here of all languages of books:
$scope.data = [{
"title": "Alice in wonderland",
"author": "Lewis Carroll",
"lang": ["en"]
}, {
"title": "Journey to the West",
"author": "Wu Cheng'en",
"lang": ["ch"]
}]
And I simply wanted to display exclusively english books, would I be able to do this purely using a filter in ng-repeat?
E.g.
<div ng-repeat="d in data | filter:d.lang='en'" style="margin-bottom: 2%">
{{d.title}}
</div>
I do not want to do it via any sort of form control (radio button etc). Would this be possible?
-EDIT- Thanks #GrizzlyMcBear for leading me down the right path! I got it to work with a slightly different filter function (which I'll paste below)
app.filter('MyFilter', function() {
var out = [];
angular.forEach(input, function(i) {
if (i.lang[0] === 'en') {
out.push(i);
}
})
return out;
}
});
and in the HTML
<div ng-repeat="d in data | MyFilter" style="margin-bottom: 2%">
{{d.title}}
</div>
Try like this
<div ng-repeat="d in data | filter: { lang : 'en'} " style="margin-bottom: 2%">
DEMO
You should use angular's filter,
I would also suggest that you use a function in the filter:
<div ng-repeat="item in collection | filter:filteringFunction" style="margin-bottom: 2%">
{{d.title}}
</div>
This way gives you more freeeeeedom (you're more than welcome to shout it Mel Gibson style ;-) )
in filtering your data by introducing more complex filtering logic.
var filteredLang = "en";
function filterByBookLanguage(collectionItem) {
var result = false;
if (collectionItem.lang[0] === filteredLang) {
result = true;
}
return result;
}
$scope.filteringFunction = filterByBookLanguage;
Now If you wish, you can also change the comperator function - filterByBookLanguage
(my terminology).
Say that your boss suddenly wants you to change the filtering logic from filtering books
into filtering by the author's name. Now all you have to do is add this condition:
if (bossWantsToChangeFilter) {
$scope.filteringFunction = filterByAuthorName;
} else {
$scope.filteringFunction = filterByBookLanguage;
}
All you have to remember is to write the comperator function with the current filtered item
as an argument and update the compared value of the language/author name
in the location you've found convenient ($scope, local variable, service etc.).

Categories

Resources