Condition in ng-if becomes false after variable in it changes - javascript

I'm making a question editor and I have a page, where current question and its' answers are printed. There are 3 types of questions, so, I need different fields for different types.
On editor's page there is a Select (for type changing) and then there is ion-list with answers. Each answer is an ion-item with a label with ng-if (tried ng-show too), based on selected question type. Types are got from mySQL DB firstly.
Problem is, the page after init shows all fields correctly, but when I change the type of question, all I see is fields without ng-if. I tried to reload the view, setting ng-change on Select (function with $state.reload), but it doesn't help.
And now the code:
Select:
<select
ng-model="question.type" ng-options="type.id for type in types"
ng-change="onTypeChange(question)">
</select>
One of the ion-items:
<ion-list>
<ion-item ng-repeat="answer in answers">
<label ng-if="question.type == 1 || question.type == 4"
class="item item-input item-stacked-label">
<span class="input-label">Ответ:</span>
<input type="text" placeholder={{answer.answer}} ng- model="answer.answer">
</label>
</ion-item>
</ion-list>
onTypeChange():
$scope.onTypeChange = function(question){
var typeUpdateQuery = 'UPDATE `questions` SET "type" = "' + question.type.id + '" WHERE "id" = ' + question.id;
$cordovaSQLite.execute(db, typeUpdateQuery).then(
function(){
$scope.answers=[];
$scope.openQuestion();
$state.reload();
}, function (err){
error(err);
}
);
}
openQuestion preloads the view's data just taking it from DB (fills in the answers[]), nothing special.
So, what should I do?

Related

ngFor inputs copy values of eachother

I'm using Angular5 and would like to create a form where user can insert new records with a button. Each record has many controls in it and should be changed independently from other records. I have done this many times but now I'm getting weird results.
<form #newRequestForm="ngForm">
<a class="list-group-item" *ngFor="let detail of currentRequest.details; let index$ = index">
.
.
.
<ng-select [items]="products" [searchFn]="searchProduct" (change)="productChanged($event, detail)">
<ng-template ng-label-tmp let-item="item">
{{item.code}} - {{item.name1}}
</ng-template>
</ng-select>
<input class="form-control" name="productname1" type="text" [ngModel]="detail.product.name1" />
<input class="form-control" name="productname2" type="text" [ngModel]="detail.product.name2" />
<input class="form-control" name="productname3" type="text" [ngModel]="detail.product.name3" />
<input class="form-control" type="text" name="description" [(ngModel)]="detail.description" />
The problem is that the method called by ng-select onchange productChanged sets the product names of the current selected product. (Products has 3 seperate name fields in erp.) And when this happens all 3 name fields of all records in the form changes to the name fields of the currently selected product. No matter which ng-select I use, all has changed. All the other fields working seperately eg: description.
So I suppose the bug is in the method but it looks like this:
productChanged($event, detail) {
detail.product = $event;
console.log('-----------------------------------------');
this.currentRequest.details.forEach((d, i) => {
console.log(i, d.product !== null ? d.product.name1 : '');
});
}
Yes, I have tried to debug with the good old console.log and it says that the content of each of the detail.products are perfect, according to the last selection of that record's ng-select.
The input controls are still rewritten by whatever select I make. Why?
Because you didn't specify a track by function, so Angular doesn't really know how to keep track of your inputs.
<a class="list-group-item" *ngFor="let detail of currentRequest.details; let index$ = index; trackBy: customTB">
customTB(index, item) { return index + '-' + item.product.name1; }

Validating *ngFor used forms using angular 2 [formControl]

I have an angular Form and it used *ngFor for repeat an input box. Also, I have to validate dynamically all input boxes that are generated by *ngFor.
There is a JSON array called list that contains Questions. and I have to generate input boxes for answers and they should be validated. Validation information also in the list.
This is an example of the list
[{id:1,
mandatory:false,
questionDescription:"Blue flags are in position and contain words “Stop, Tank Car Connected” or “Stop, Men at Work”",
questionType:1,
validation:{maximum:100,minimum:0}},
{id:2,
mandatory:true,
questionDescription:"Wheels are chocked and handbrake(s) set” or “Stop, Men at Work",
questionType:2,
validation:{maxLength:10,minLength:3}}]
I tried to validate generated input boxes as described in here.
https://scotch.io/tutorials/angular-2-form-validation (see validation forms there).
So I have to give validation information for each input boxes like below.
(this example from given tutorial)
constructor(fb: FormBuilder){
this.complexForm = fb.group({
'firstName' : [null, Validators.required],
'lastName': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10)])]
})
this is the HTML for it.
<div class="form-group">
<label>First Name:</label>
<input class="form-control" type="text"[formControl]="complexForm.controls['firstName']">
</div>
<div class="form-group" >
<label>Last Name</label>
<input class="form-control" type="text" [formControl]="complexForm.controls['lastName']">
</div>
So in my case, all input boxes generate by *ngFor and I have to assign tags like firstName, lastName dynamically to those boxes.
So I used question.id as that tag and implemented control group as followed.
inspectionForm: FormGroup;
constructor(fb: FormBuilder) {
this.questionTemplate = templateService.getQuestionTemplate();
let objString: string = "{";
for (let item of this.questionTemplate.sectionList[0].questionList) {
if (item.questionType == 2) {
objString += '"' + item.id + '":null,';
}
}
objString = objString.slice(0, -1);
objString += '}';
let objJson = JSON.parse(objString);
for (let item of this.questionTemplate.sectionList[0].questionList) {
if (item.questionType == 2) {
objJson[item.id] = [null,Validators.compose([Validators.minLength(item.validation.minLength), Validators.maxLength(item.validation.maxLength)])];
}
}
this.inspectionForm = fb.group(objJson);
}
and here is my HTML code.
<form [formGroup]="inspectionForm" (ngSubmit)="submitForm(inspectionForm.value)">
<div *ngFor="let question of section.questionList" [ngSwitch]="question.questionType">
<ion-row *ngSwitchCase="QuestionType.String">
<ion-col width-80>{{question.questionDescription}}</ion-col>
<ion-col width-20>
<ion-input id="{{question.id}}" type="text" class="inspection-bottom-border" [formControl]="inspectionForm.controls['{{question.id}}']"></ion-input>
</ion-col>
</ion-row>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" [disabled]="!inspectionForm.valid">Submit</button>
</div>
</form>
The Problem is....
If I define [formControl] as [formControl]="inspectionForm.controls['{{question.id}}']"
I get an error like follows,
unhandled Promise rejection: Template parse errors:
Parser Error: Got interpolation ({{}}) where expression was expected at column 25 in [inspectionForm.controls['{{question.id}}']] in InspectionPage#36:91 ("idth-20>
<ion-input id="{{question.id}}" type="text" class="inspection-bottom-border" [ERROR ->][formControl]="inspectionForm.controls['{{question.id}}']"></ion-input>
then I searched for an answer in StackOverflow, I found someone has said don't use [] and {{}} in one time (Dynamic routerLink value from ngFor item giving error "Got interpolation ({{}}) where expression was expected")
But if I removed [] and defined as follows
formControl="inspectionForm.controls['{{question.id}}']"
I get an error as follows
EXCEPTION: Error in ./InspectionPage class InspectionPage - inline template:36:91 caused by: Cannot create property 'validator' on string 'inspectionForm.controls['2']'
(this '2' the is question.id of first type 2 question)
if I define as follows same error occurs
formControl="inspectionForm.controls['2']"
But the problem is if I define as follows with [formControl]
[formControl]="inspectionForm.controls['2']"
The app is Working Perfectly....
But I have to give question.id dynamically. How I can do that?
Are there someone have a solution for this error. Please give a suggestion.
Thank you very much.
just use [formControl]="inspectionForm.controls[question.id]"
Thanks #Sasxa for the answer

Update Item in List to be shown when tapped - Angular JS

I have a list that shows some items, here is the code that generates my list in my controller:
$scope.multipleOptions = [{ item: '1', checkmark:false}, { item: '2', checkmark:false},{ item: '3', checkmark:false} ];
Then in my HTML I have
<ion-list id="multiple-select-list" class=" ">
<ion-item class=" " ng-repeat="multipleOption in multipleOptions track by $index" ng-click="checkSelected($index)">
<p><b>{{multipleOption.item}}</b></p>
<p class="button-icon ion-checkmark" ng-show="{{multipleOption.checkmark}}"></p>
</ion-item>
</ion-list>
As you can see when the list loads the checkmarks for all the items are hidden, and I have a function called checkSelected($index) that is called when an item is tapped, in that function I want to set the checkmark to be shown, here is what I currently have
$scope.checkSelected = function(modalIndex) {
//this set the checkmark property to true
$scope.multipleOptions[modalIndex].checkmark = true;
//the line below does not work
document.getElementById("multipl-select-list").getElementsByTagName("ion-item")[modalIndex].getElementsByTagName("p")[1].show = true;
}
In the above method I am able to set the checkmark variable of the item to ture but what I am having troubel with is making it show right when the item is tapped? How can I set the ng-show of the checkmark so it shows right when it is tapped?
EDIT
Solutions both worked I just wanted to add some behabiour I noticed, when I had ng-show={{multipleOption.checkmark}} the ng-hide class would be added to the class of that <p> so even when I set ng-show to true it would still not be shown
Thanks for all the help
HTML :
<ion-list id="multiple-select-list" class=" ">
<ion-item class=" " ng-repeat="multipleOption in multipleOptions track by $index" ng-click="checkSelected(multipleOption)">
<p><b>{{multipleOption.item}}</b></p>
<p class="button-icon ion-checkmark" ng-show="multipleOption.checkmark"></p>
</ion-item>
</ion-list>
In javascript function :
$scope.checkSelected = function(data) {
data.checkmark = true;
}
I think the problem is with your syntax. Try removing curly brackets. Also take a look a this answer
ng-show="multipleOption.checkmark"

AngularUI-Bootstrap Typeahead: Grouping results

I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?
I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.
please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">

angularJs - Is it possible for 2 different models of different structures to sync or share states?

I have a list of checkboxes and values I"m loading from a list which comes back from the database.
Controller
listA = ['item1','item2'...'itemn']; //Master list of items
$scope.selectedItems = ["item1",... "item5"]; //selected items
$scope.attributesModel = [ //new model based on selected items
{"index":5,"attribute":"item1"},
{"index":10, "attribute":"item2"},
{"index":13, "attribute":"item3"},
{"index":21, "attribute":"item4"},
{"index":24, "attribute":"item5"}
];
View part 1
<td>
<div class="checkbox checkbox-notext">
<input checklist-model="selectedItems" checklist-value="key" type="checkbox" id="{{key}}" ng-disabled="exceededLimit && !checked" />
</div>
</td>
<td>
<label for="{{key}}">{{key}}{{$index}}</label>
</td>
view part 2
<div ng-repeat="(index, row) in attributesModel" >
<div class="margin10">
<div>Index<input ng-model="row.index" value="row.index" type="number" class="indexInputs"></input>{{row.attribute}}</div>
</div>
</div>
Now I would like to sync $scope.selectedItems and $scope.attributesModel. When a checkbox is deselected, both selectedItems and attributesModel models remove that item, and vice versa. So every time someone checks a new checkbox they are presented a attributesModel with an empty text field to type the index value.
catch The index key is null initially for every newly selected item that is added to attributesModel. The user must enter a new index # once the new item is created.
I've tried using watch but the problem I run into is when a new item is selected, I don't have access to the item itself. I only have access to the list without any idea whether the new item is X or if the item removed is Y in order to push/delete the right item.
So this might be a watch solution that I'm missing.
Let me know if I can clarify anything.
I am not sure what the problem is, but you could use ngChange on the checkboxes:
<input type="checkbox" ... ng-change="..." />
I asdume you have a checklist directive or something, so should do something there, but (since you don't share it with us) I can't tell what exactly :)
UPDATE:
Since the checklist directive is an external dependency, you could handle the ng-chage in your code:
<input type="checkbox" ... ng-change="changed(key)" />
/* In the controller: */
...
$scope.changed = function (key) {
if ($scope.selectedItems.indexOf(key) === -1) {
// The checkbox for `key` was unchecked...
} else {
// The checkbox for `key` was checked...
}
};

Categories

Resources