Using jQuery to change css in option tags does not work until second click in Angular - angularjs

I have a edit button that opens a bootstrap modal to edit a record:
<a href="#" ng-click="getAction(action.id, 'edit')"
data-toggle="modal" data-target="#modalEditAction"
class="functionalLinks">
<i class="glyphicon glyphicon-pencil"></i>
EDIT
</a>
getAction(action.id, 'edit') looks like this:
$scope.getAction = function (actionId, populateObject) {
$http.get(actionUrl + '/' + actionId)
.then(function (response) {
// Test front end exception message;
// throw "test exception";
switch (populateObject) {
case "details":
$scope.data.actionDetails = response.data;
break;
case "edit":
$scope.data.editAction = response.data;
$scope.dimAt1EditActionRecommendedByLerSelections();
break;
}
})
.catch(function (error) {
$scope.data.actionDetailsError = error;
});
}
So we are interested in the "edit" case of the switch:
The response data looks like this (Look at the actionsRecommendedByLer property):
{
"caseId": 8,
"case": null,
"actionTypeId": 1,
"actionType": {
"actionTypeName": "Actions Taken By Management",
"caseType": 1,
"id": 1
},
"dateCreated": "2017-05-08T14:55:23.797",
"actionStatus": 3,
"notes": "bngfhfrg455",
"actionType1Id": null,
"actionType1": {
"actionId": 1,
"actionsRecommendedByLer": [
{
"actionType1Id": 1,
"lookUpDetailId": 4,
"name": "Alternative Discipline Agreement in lieu of # day Suspension",
"id": 1
},
{
"actionType1Id": 1,
"lookUpDetailId": 7,
"name": "Arbitration",
"id": 2
},
{
"actionType1Id": 1,
"lookUpDetailId": 13,
"name": "Complaint - EEO Formal",
"id": 3
}
],
"actionProposedBySupervisorId": 4,
"actionProposedBySupervisor": {
"id": 4,
"displayValue": "Alternative Discipline Agreement in lieu of # day Suspension",
"code": "",
"parentId": null,
"externalSystemId": null,
"isInactive": false,
"inactivDate": null,
"sortOrder": null,
"discriminator": "",
"lookupMaster": null,
"lookUpMastersId": 1
},
"actionTakenBySupervisorId": 8,
"actionTakenBySupervisor": {
"id": 8,
"displayValue": "AWOL Letter (CORPS)",
"code": "",
"parentId": null,
"externalSystemId": null,
"isInactive": false,
"inactivDate": null,
"sortOrder": null,
"discriminator": "",
"lookupMaster": null,
"lookUpMastersId": 1
},
"actionCharges": [
{
"actionType1Id": 1,
"lookUpDetailId": 211,
"name": "Creating hostile work environment",
"id": 1
}
],
"actionEffectiveDate": "2017-05-16T04:00:00",
"id": 1
},
"actionType17Id": null,
"actionType17": null,
"id": 1
}
And I use the response data to show a table with detete buttons basically showing which Select Options are chosen like this:
<select class="pull-right" id="editActionRecommendedByLerSelect"
ng-model="data.editAction.actionType1.actionRecommendedByLerId">
#*<option value="0"></option>*#
<option value="{{managmentAction.id}}"
ng-repeat="managmentAction in data.managementActions">
{{managmentAction.displayValue}}
</option>
</select>
<br />
#*{{data.editAction.actionType1.actionRecommendedByLerId}}*#
<table class="table table-condensed table-bordered">
<tr ng-show="data.editAction.actionType1.actionsRecommendedByLer.length == 0">
<td colspan="2" class="noResultText">No Actions Recommended By LER</td>
</tr>
<tr ng-repeat="actionRecommendedByLer in data.editAction.actionType1.actionsRecommendedByLer">
<td>
<a href="#" ng-click="removeAnEditActionRecommendedByLerFromActionType1(actionRecommendedByLer.id)"
class="functionalLinks text-danger">
<i class="glyphicon glyphicon-trash"></i>
DELETE
</a>
</td>
<td>#*{{actionRecommendedByLer.id}} :*# {{actionRecommendedByLer.name}}</td>
</tr>
</table>
The select is populated by an array of choices. The choice ID will match the appropritate lookupDetailId of the actionRecommendedByLer id. in the collection.
This line in the "edit" case of the switch in $scope.getAction(actionId, populateObject):
$scope.dimAt1EditActionRecommendedByLerSelections();
calls this:
$scope.dimAt1EditActionRecommendedByLerSelections = function () {
console.log("In dimAt1EditActionRecommendedByLerSelections");
console.log("Length: " + $scope.data.editAction.actionType1.actionsRecommendedByLer.length);
var element;
for (i = 0; i < $scope.data.editAction.actionType1.actionsRecommendedByLer.length; i++) {
console.log("i: " + i);
console.log("id: " + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId);
element = $('#editActionRecommendedByLerSelect option[value = "' + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId + '"]');
element.attr('disabled', 'disabled');
element.css('background-color', 'yellow');
}
}
Basically I am using jQuery to make the multiple options already selected in the select drop down yellow and disabled. This was easy from my Add Bootstrap modal.
But loading it all back in for an edit and hightlighting the right ones is proving to be way more difficult.
This works and shows in the Select Drop Down the choice names highlighted in yellow and disabled in the model being rendered in a table next to a delete button. The only problem is it doesn't show the choices hightlighted until I click the edit button again.
How can I get this to hightlight and disable the options on the first edit click?
UPDATE 1 (In response to comment user1120808)
I tried your answer but it is not working. In fact now it does not even work on the second click.
Does this look like correct implementation of your suggestion?
$scope.dimAt1EditActionRecommendedByLerSelections = function () {
console.log("In dimAt1EditActionRecommendedByLerSelections");
console.log("Length: " + $scope.data.editAction.actionType1.actionsRecommendedByLer.length);
var element;
for (i = 0; i < $scope.data.editAction.actionType1.actionsRecommendedByLer.length; i++) {
console.log("i: " + i);
console.log("id: " + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId);
// force scope digest.
$scope.$evalAsync(() => {
element = $('#editActionRecommendedByLerSelect option[value = "' + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId + '"]');
element.attr('disabled', 'disabled');
element.css('background-color', 'yellow');
});
}
}

The reason you don't see it, is because jquery runs outside of angular and angular doesn't pick it up. It isn't evaluated anymore. But with the second click you trigger a digest cycle, so you see it then. So what you need to do is force a digest cycle. You can use $evalAsync for this.
try the following:
$scope.$evalAsync(() => {
element = $('#editActionRecommendedByLerSelect option[value = "' + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId + '"]');
element.attr('disabled', 'disabled');
element.css('background-color', 'yellow');
});

$timeout service solved the problem:
$scope.dimAt1EditActionRecommendedByLerSelections = function () {
console.log("In dimAt1EditActionRecommendedByLerSelections");
console.log("Length: " + $scope.data.editAction.actionType1.actionsRecommendedByLer.length);
$timeout(function() {
var element;
for (i = 0; i < $scope.data.editAction.actionType1.actionsRecommendedByLer.length; i++) {
console.log("i: " + i);
console.log("id: " + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId);
element = $('#editActionRecommendedByLerSelect option[value = "' + $scope.data.editAction.actionType1.actionsRecommendedByLer[i].lookUpDetailId + '"]');
element.attr('disabled', 'disabled');
element.css('background-color', 'yellow');
}
}, 1000);
}

Related

Angular (1.x) - Programmatically access NG-REPEAT from code

I'm trying to setup hotkeys for an old project that still uses angular 1.x and one of the features I was trying to add would be to select the first row from a table that is created with an NG-REPEAT. I've been able to add in other functionality such has moving the selected row up / down because I pass in the selected row on ng-click="setSelected(this)" which then lets me save the row and move it with selectedRow.$$prevSibiling or selectedRow.$$nextSibiling.
What I'm having a hard time figuring out is how can I set the selectedRow from the controller.
Here is a quick example:
http://plnkr.co/edit/6jPHlYwkgF5raRWt?open=lib%2Fscript.js
JS:
App.controller('ActivitiesCtrl', [function() {
var vm = this;
vm.selectedRow = "Not set";
vm.activities = [
{
"id": 1,
"code": "ABC",
"person": "Joe"
},
{
"id": 2,
"code": "DFF",
"person": "Sally"
},
{
"id": 3,
"code": "ABC",
"person": "Sue"
},
{
"id": 4,
"code": "124",
"person": "Sam"
},
];
vm.setSelected = function(row) {
vm.selectedRow.selected = false;
vm.selectedRow = row;
vm.selectedRow.selected = true;
}
vm.moveNext = function() {
vm.setSelected(vm.selectedRow.$$nextSibling)
}
vm.setFirst = function() {
vm.setSelected("How do I set it...");
// How to set it? vm.setSelected(?????)
}
}]);
HTML:
<div ng-controller="ActivitiesCtrl as vm">
<table>
<thead>
<th>Id</th>
<th>Code</th>
<th>Person</th>
</thead>
<tbody>
<tr ng-repeat="activity in vm.activities track by activity.id" ng-click="vm.setSelected(this)" ng-class="{info: selected}">
<td>{{activity.id}}</td>
<td>{{activity.code}}</td>
<td>{{activity.person}}</td>
</tr>
</tbody>
</table>
{{vm.selectedRow | json}}
<hr />
<button ng-click="vm.setFirst()">Set First</button>
<button ng-click="vm.moveNext()">Next</button>
</div>
You can do this by setting the actual object from the array as selectedRow rather than using this and set the class by checking if selectedRow === activity in the ng-class.
This approach doesn't require mutating the objects
<tr
ng-repeat="activity in vm.activities track by activity.id"
ng-click="vm.setSelected(activity)"
ng-class="{info: vm.selectedRow == activity}"
>
Then you can use Array#findIndex() to get the current selectedRow index in the array and if a next one exists use it or go back to the first.
For the setFirst() you just use vm.activities[0]
vm.selectedRow = null;
vm.setSelected = function (row) {
vm.selectedRow = row;
};
vm.moveNext = function () {
const {selectedRow:curr, activities:act} = vm;
if (curr !== null) {
let idx = act.findIndex(e => e == curr) + 1;
let next = act[idx] || act[0];
vm.setSelected(next);
}
};
vm.setFirst = function () {
vm.setSelected(vm.activities[0]);
};
Working plunker
Here is the link with test working example.
enter code here
http://plnkr.co/edit/7mTvRB0ZlHOQwOIc?preview

How to handle 'ng-repeat' together 'ng-model' for individual object.?

I have an array with objects with 'SALARY' field. I want to manage 'CREDIT' amount using ng-model. so i am create a function and work fine with object id. but in my case when i am change value of any input field it is change all input's values.
Please any one tell me how to possible change input value only desire input field.
this is my html >
<div ng-repeat="obj in myObj">
{{obj.id}} /
{{obj.name}} /
{{obj.salary}} /
<input type="text" ng-model="credit.amount" />
<button ng-click="updateBalance(obj)">Balance</button>
</div>
and this is my script >
var app = angular.module('myApp',[]);
app.controller('employee', function($scope) {
$scope.myObj = [
{ "id" : 1, "name" : "abc", "salary" : 10000 },
{ "id" : 2, "name" : "xyz", "salary" : 15000 }
]
$scope.credit = {"amount" : 0};
$scope.updateBalance = function(obj){
console.log(obj.name + "'s current balance is : ");
console.log(obj.salary - Number($scope.credit.amount));
}
});
and this is my PLNKR LINK.
Values in all input fields are changing because you are binding $scope.credit.amount to all of them. Instead you need to maintain them separately. Following should work:
Html
<tr ng-repeat="obj in myObj">
<td>{{obj.id}} </td>
<td>{{obj.name}} </td>
<td>{{obj.salary}} </td>
<td>
<input type="number" ng-model="credits[obj.id].amount" />
</td>
<td>
<button ng-click="updateBalance(obj)">Balance</button>
</td>
</tr>
Controller
var app = angular.module('myApp', []);
app.controller('employee', function($scope) {
$scope.myObj = [{
"id": 1,
"name": "abc",
"salary": 10000
}, {
"id": 2,
"name": "xyz",
"salary": 15000
}]
$scope.credits = $scope.myObj.reduce(function(acc, object) {
acc[object.id] = { amount: 0 };
return acc;
}, {});
$scope.updateBalance = function(obj) {
var balance = obj.salary - Number($scope.credits[obj.id].amount)
alert(obj.name + ' balance is : ' + balance);
}
});

Design pattern for AngulasJS controller with 'parameter' from HTML

I am new to AngularJS, and I am used to program in C/C++, so I feel a little lost in the new AngulaJS world.
I have the var DefMenuItem holding information to display a table with menu items.
var DefMenuItem =
{
"menuItemsNumF32": [
{
"kind": "Num",
"obj": "eDefRegKinDriverMidObjItemIdHumWaterHardness",
"msg": "eDefGuiMsgIdNumWaterHardness",
"opt": null,
"hide": null
},
{
"kind": "Num",
"obj": "eDefRegKinDriverMidObjItemIdHumKeepWarmOffset",
"msg": "eDefGuiMsgIdNumKeepWarmOffset",
"opt": null,
"hide": null
},
{
"kind": "Num",
"obj": "eDefRegKinDriverMidObjItemIdHumFlickerCorrection",
"msg": "eDefGuiMsgIdNumFlickerCorrection",
"opt": null,
"hide": null
}
],
"menuItemsNumU32": [
{
"kind": "Num",
"obj": "eDefRegKinDriverMidObjItemIdHumKeepWarmTimeStart",
"msg": "eDefGuiMsgIdNumKeepWarmTimeStart",
"opt": null,
"hide": null
},
{
"kind": "Num",
"obj": "eDefRegKinDriverMidObjItemIdHumKeepWarmTimeEnd",
"msg": "eDefGuiMsgIdNumKeepWarmTimeEnd",
"opt": null,
"hide": null
},
{
"kind": "Num",
"obj": "eDefRegKinDriverMidObjItemIdBathDuration",
"msg": "eDefGuiMsgIdNumBathDuration",
"opt": null,
"hide": null
}
]
}
And two AngularJS controllers to link one of the lists to a with the table
kinAngularApp.controller('kinServiceNumF32Controller', [function () {
var self = this;
var menuItemProperty = "menuItemsNumF32";
self.objectItemList = buildObjectItemList(DefMenuItem[menuItemProperty]);
self.refresh = function () {
self.objectItemList = buildObjectItemList(DefMenuItem[menuItemProperty]);
refreshServicePage();
}
self.save = function () {
saveObjectItemListValues(DefMenuItem[menuItemProperty], self.objectItemList);
}
}]);
kinAngularApp.controller('kinOverviewNumU32Controller',[ function() {
var self = this;
var menuItemProperty = "menuItemsNumU32";
self.objectItemList = buildObjectItemList(DefMenuItem[menuItemProperty]);
self.refresh = function() {
self.objectItemList = buildObjectItemList(DefMenuItem[menuItemProperty]);
refreshOverviewPage();
}
self.save = function() {
saveObjectItemListValues(DefMenuItem[menuItemProperty], self.objectItemList);
}
}]);
The only difference of the controllers is the menu item list they use by intialising menuItemProperty.
What is the correct design pattern to select the menu item list on the HTML side and use multiple instances of only one controller?
Is it the correct pattern to use only one controller with service, factory or whatever?
Here ist the HTML side
<div id="menuItemMainService"
ng-controller="kinServiceNumF32Controller as kinServiceCtrl"
class="w3-panel rte-menuPage" style="display: none">
<table id="idTableObjectItem" class="w3-table">
<tr>
<th>Name</th>
<th>Value</th>
<th>Unit</th>
<th>Object Id</th>
</tr>
<tr ng-repeat="objectItem in kinServiceCtrl.objectItemList" >
<td>{{objectItem.name}}</td>
<td><input ng-model="objectItem.value" class="w3-input w3-right-align" type="text"></td>
<td>{{objectItem.unit}}</td>
<td>{{objectItem.objectId}}</td>
</tr>
</table>
<button id="buttonRefreshService" class="w3-btn" ng-click="kinServiceCtrl.refresh()">Refresh</button>
<button id="buttonSaveService" class="w3-btn" ng-click="kinServiceCtrl.save()">Save</button>
</div>
You could use routing.
For example you could use standard angular ngRouter. In this case you could move menuItemProperty in controller arguments list.
kinAngularApp.controller('kinOverviewController',['menuItemProperty ', function(menuItemProperty ) {
var self = this;
self.objectItemList = buildObjectItemList(DefMenuItem[menuItemProperty]);
self.refresh = function() {
self.objectItemList = buildObjectItemList(DefMenuItem[menuItemProperty]);
refreshOverviewPage();
}
self.save = function() {
saveObjectItemListValues(DefMenuItem[menuItemProperty], self.objectItemList);
}
}]);
Here example: http://codepen.io/anon/pen/zBQKZk

AngularJs - Changing the value of property

I am new to anuglar Js. I am trying to figure out How do i change the property value.
$scope.dashboards = {
"1": {
"widgets": [{
"row": 0,
"col": 0,
"sizeX": 2,
"sizeY": 1,
"name": "Canvas 1",
"canvas": "canvas_1",
"show": true
}, {
"row": 0,
"col": 2,
"sizeX": 2,
"sizeY": 1,
"name": "Canvas 2",
"canvas": "canvas_2",
"show": true
}]
}
}
On ng-click , i want to change the value of key "show" to false.
<div gridster-item="widget" ng-repeat="widget in dashboard.widgets">
<i ng-click="changeValue(widget)" class="glyphicon glyphicon-trash">
</div>
$scope.changeValue= function (widget) {
.....How to??
};
Since you are already passing the current widget as a parameter, you can do Like this
$scope.changeValue= function (widget) {
widget.show = false;
};
I would not pass the widget. Instead I would pass its $index:
<i ng-click="changeValue($index)" class="glyphicon glyphicon-trash">
And then:
$scope.changeValue= function (index) {
$scope.dashboards.1.widgets[index].show = ...
};
It's look like your array in ng-repeat is undefined in scope.
You should update your codes like below;
<div gridster-item="widget" ng-repeat="widget in dashboards.1.widgets">
<i ng-click="changeValue(widget)" class="glyphicon glyphicon-trash">
</div>
After you get related object and update show property in $scope like below:
$scope.changeValue= function (widget) {
$scope.dashboards.1.widgets[widget].show = false;
};
Simply,
$scope.changevalue= function(widget){
for (var i = 0; i < $scope.dashboards.length; i++) {
for (var j = 0; j < $scope.dashboards[i].widgets.length; j++) {
if ($scope.dashboards[i].widgets[j].ID == widget.ID){
$scope.dashboards[i].widgets[j].show= false
}
};
};
}

AngularJS filter already selected option from dynamic field

I have a form where you can add x number of fields. Each field contains option select. I want to filter out the already chosen option when this option is already chosen in one or multiples field before. Each field has a remove button and the form has 1 add button.
How can I filter out the dynamic fields?
Any help,guidance is most welcome.Thanks in advance. :)
This is how my HTML looks like:
<div data-ng-repeat="choice in choices">
<select data-ng-model="choice.option"
data-ng-options="item as item.Value for item in options">
</select>
<button data-ng-click="removeChoice(choice)">Remove choice</button>
<div>
<button data-ng-show="choices.length <= 4" data-ng-click="addNewChoice()">Add Choice</button>
</div>
</div>
And my controller:
$scope.options = [
{
"Key": "0",
"Value": "Select an option"
},
{
"Key": "Option1",
"Value": "Option1"
},
{
"Key": "Option2",
"Value": "Option2"
},
{
"Key": "Option3",
"Value": "Option3"
},
{
"Key": "Option4",
"Value": "Option4"
},
{
"Key": "Option5",
"Value": "Option5"
}
];
$scope.choices = [{ id: '1' }];
$scope.addNewChoice = function () {
var newItemNo = $scope.choices.length + 1;
$scope.choices.push({ id: newItemNo, option: $scope.option, value: $scope.value });
};
$scope.removeChoice = function () {
var index = $scope.choices.indexOf(choice);
$scope.choices.splice(index, 1);
};
ok
i can give simple recommendation which will be this.
1: add variable $scope.selectedOptions = [];
this will contain list of already selected options from all select elements .
2: create function $scope.AddSelectedOption(item);
this will add the selected object when we change option from any select element because we are going to use for all selects ng-change= "AddSelectedOption(item);"
3: add checkIfSelected(item); this will check if given object value is already selected or not ..
will user in
hope you understand what it will do just check like this
$scope.checkIfSelected = function (item) {
$scope.selectedFound = $scope.selectedOptions.filter(function
(option) {
if(option.value == item.value)
{
return day;
}
});
if($scope.selectedFound.length == 0 ) { return false; } else {
return true; }
}
This will return true if give item found in the options.
if not out.. you can invite me to help again .
This is possible. I'm explaining a basic version of this requirement. See the working example here http://plnkr.co/edit/S9yZpjhY55lXsuifnUAc?p=preview
What wer are doing is maintaining another options which is the copy of the original options. Copying the options will make it to not reference existing options since objects are pass by reference in Javascript.
The main logic is in this function, which modify the options on selection:
$scope.optionSelected = function(choice) {
$scope.availableOptions = $scope.availableOptions || angular.copy($scope.options);
if (choice.option) {
var index = -1;
// See if available options has that key
angular.forEach($scope.availableOptions, function(item, i) {
if (item.Key === choice.option.Key) {
index = i;
}
});
if (index > -1) {
// And then remove it
$scope.availableOptions.splice(index, 1);
}
}
};

Resources