ui-grid button in cell - angularjs

So I have been struggling with this for like 2 days and still no clue on what to do...
First of all I'm very new to angular and javascript in general so everything I wrote can (and may) be extremely wrong.
sample data:
{
'title' : 'awesome title',
'date' : '19-05-2015',
'place' : 'nice place',
'person' : 'Juliet Peterson',
'status' : 'OK'
}
What I need is, in an existing ui-grid, add a column containing either
if myData.person contains only one persone, this person
if myData.person is empty the text 'No person'
if myData.person contains multiple persons the text 'Choose from' wich on click will popup a list for selection.
My problem is that I am curently unable to get that working. the 2 first cases work fine but i'm struggling for the last one.
We are using bootstrap so my heart goes for either a dropdown list or a popover button
here is my current code:
html:
<div id="grid_post_id" ui-grid="gridPostOpts" ui-grid-edit class="grid"></div>
js:
$scope.choosePerson = function(a) {
if (a.length == 0) {
return 'No person';
} else {
var split = a.split(',');
if (split.length > 1) {
var res = '<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="right" data-content="';
for (var i = 0; i < split.length; i++) {
res = res + split[i];
}
res = res + '">Choose from</button>'
return res;
} else {
return a;
}
}
}
$scope.gridPosteOpts = {
columnDefs : [
{
name : 'Title',
field : 'title',
enableCellEdit : false
},
[...]
{
name : 'Person',
cellTemplate : '<div class="ui-grid-cell-contents">{{grid.appScope.choosePerson(row.entity.person)}}</div>',
enableCellEdit : false
}
this code is almost working as i got the rigth data in my cell but it is displayed instead of being interpreted as html (ie my cell contains the string '<button type="bu...')

Use
cellTemplate : `
<div class="ui-grid-cell-contents">
<button type="button" class="btn btn-default" data-container="body"
data-toggle="popover" data-placement="right"
data-content="{{grid.appScope.choosePerson(row.entity.person)}}">
Choose from
</button>
</div>`
and have choosePerson() just return the data.
You can't inject elements into the DOM using angulars {{}}. It will always be text-only

Finnaly managed to do it by myself using a mix of differents approaches found on SO!
I used #j2L4e template for the case "button needed" and just the text for the othercases; i choose between the two with an ng-if
here is the "final" code:
$scope.containsComma = function(a) {return a.contains(',')};
$scope.choosePersonBis = function(a) {
if (a.length == 0) {
return 'No person';
} else {
var split = a.split(',');
if (split.length > 1) {
var res = '';
for (var i = 0; i < split.length; i++) {
res = res + split[i];
}
return res;
} else {
return a;
}
}
;
$scope.gridPosteOpts = {
columnDefs : [
{
name : 'Title',
field : 'title',
enableCellEdit : false
},
[...]
{
title : 'Person',
cellTemplate : `
<div ng-if="grid.appScope.containsComma(row.entity.person)"
class="ui-grid-cell-contents">
<button type="button" class="btn btn-default"
popover="{{grid.appScope.choosePersonBis(row.entity.person)}}"
popover-placement="right"
popover-trigger="focus">
Choose person
</button>
</div>
<div ng-if="grid.appScope.notContainsComma(row.entity.person)"
class="ui-grid-cell-contents">
{{grid.appScope.choosePersonBis(row.entity.person)}}
</div>`
}
};

try replace this code
<div class="ui-grid-cell-contents">{{grid.appScope.choosePerson(row.entity.person)}}</div>
with
<div class="ui-grid-cell-contents" ng-bind-html="grid.appScope.choosePerson(row.entity.person)"></div>

Related

AngularJS 1.4.4: ng-click not working in grid

I have a simple Web UI build in angularJS 1.4.4. I am stuck in a basic problem of the click event in UI grid. On click of eye button and file button in the grid, successnotification() function defined in the controller is not called. Whereas for refresh button, button click is working fine. Below is my code of HTML and Controller
class AccesspointListController {
/*#ngInject*/
/* Read me documentation for Grid : http://www.ag-grid.com/documentation.php */
constructor($rootScope, GridFilters,AccesspointListService, SnapshotController) {
this.name = 'accesspointlist';
Object.assign(this, {$rootScope, GridFilters,AccesspointListService, SnapshotController});
this.columnDefs = [
{
headerName: "Sr No.",
//field: "accessPointDetailsId",
field: "no",
width:15,
filter: 'number',
filterParams: { apply: true },
cellStyle:{'text-align': 'center'},
unSortIcon:true
},
{
headerName: "IP",
//field: "accessPointIP",
field: "ip",
filter:'text',
width:80,
unSortIcon:true
},
{
headerName: "Actions",
field: "",
width:35,
suppressSorting: true,
suppressMenu:true,
cellRenderer: function(params) {
return '<button class="btn primary" ng-click="grid.appScope.successnotification(row)"><i class="fa fa-eye"></i></button>'+
'<button class="btn primary" ng-click="grid.appScope.vm.successnotification(row)"><i class="fa fa-file-image-o"></i></button>';
}
}
];
this.allOfTheData = require("json!./favCFIC.json")['items'];
this.pageSize = '10';
this.gridOptions = {
columnDefs: this.columnDefs,
//rowData: $scope.allOfTheData,//Load data here for static non paging data.
groupHeaders: false,
enableColResize: false,
enableSorting: true,
suppressRowClickSelection: true,
headerHeight:40,
rowHeight:40,
angularCompileHeaders: true,
angularCompileFilters: true,
enableFilter: true,
icons: {
// use font awesome for menu icons
sortAscending: '<i class="fa fa-sort-amount-asc"/>',
sortDescending: '<i class="fa fa-sort-amount-desc"/>'
}
};
}//end constructor
loadData() {
let allOfTheData = this.allOfTheData;
if(allOfTheData.length==0) {
$rootScope.alerts.push({
type: 'danger',
msg: 'There is an issue building the data table.',
targetState: 'forms'
});
} else {
let dataSource = {
rowCount: (this.allOfTheData.length),
pageSize: parseInt(this.pageSize), // changing to number, as scope keeps it as a string
getRows: function (params) {
// this code should contact the server for rows. however for the purposes of the demo,
// the data is generated locally, a timer is used to give the experience of
// an asynchronous call
setTimeout(function() {
// take a chunk of the array, matching the start and finish times
let rowsThisPage = allOfTheData.slice(params.startRow, params.endRow);
// see if we have come to the last page. if we have, set lastRow to
// the very last row of the last page. if you are getting data from
// a server, lastRow could be returned separately if the lastRow
// is not in the current page.
let lastRow = -1;
if (allOfTheData.length <= params.endRow) {
lastRow = allOfTheData.length;
}
params.successCallback(rowsThisPage, lastRow);
}, 500);
}
};
this.gridOptions.api.setDatasource(dataSource);
this.gridOptions.api.sizeColumnsToFit()
}
successnotification(){
this.notf1.show('Success! You\'ve clicked the Add button.', "success");
};
}
export default AccesspointListController;
This is my html file.
<section class="container">
<div class="col-md-10 cf">
<div ncy-breadcrumb></div>
<br />
<div id="notificationSpot">
</div>
<br />
<br class="cf"/>
<div class="col-sm-8">
<div class="panel panel-default" id="content-formatting">
<div class="panel-heading" align="right">
<label id="lastUpdated">Last Updated : </label>
<label id="lastUpdatedValue">19/2/2018 12:20 AM </label>
<button type="button" class="btn 0" ng-click="vm.redirect()"><i class="fa fa-refresh"></i></button>
</div>
<div ag-grid="vm.gridOptions" class="ag-fresh" style="clear:both; height: 430px; width:100%;" ng-init="vm.loadData()"></div>
</div>
</div>
<span kendo-notification="vm.notf1" k-position="{ top: 110}" ></span>
<span kendo-notification="vm.notf2" k-append-to="'#notificationSpot'" k-auto-hide-after="0"></span>
</div>
</section>
Solution: Modify cell renderer code as per below code snippet
cellRenderer: function(params) {
return $compile('<i class="fa fa-eye"></i>')($scope)[0];
}
You can define and register the callback (onSelectionChanged) in ag-grid:
var gridOptions = {
columnDefs: columnDefs,
suppressRowClickSelection: false, // Important! allow to select the rows
rowSelection: 'single', // option, if you need only one row selected
onSelectionChanged: onSelectionChanged // callback
};
This callback will fire each time the selection of the ag-grid is changed. You can define the callback:
function onSelectionChanged() {
var selectedRows = gridOptions.api.getSelectedRows();
var selectedRowsString = '';
selectedRows.forEach( function(selectedRow, index) {
if (index!==0) {
selectedRowsString += ', ';
}
selectedRowsString += selectedRow.athlete;
});
document.querySelector('#selectedRows').innerHTML = selectedRowsString;
}
The information is taken from official documentation:
Link to documentation

AngularJs change button text as well as color

Hi I am trying to change color as well as text of a button i.e. switch between two texts & colors. Suspend/unsuspend text and red/green color.
What I want to do is for the moment (because later, server will give info about whether user is suspended or not) randomly give them any one of these two texts&colors, and I click on button it should turn to other text & color.
I tried but I am wrong somewhere and I cant find it. Please help. And if there is any better way then please suggest to me, I am new to angular.
HTML:
<button type="button" class="btn btn-danger" ng-if="checkStatus(person.isSuspended)" ng-click="person.isSuspend=suspendUser(!person.isSuspend)">{{suspendText}}</button>
<button type="button" class="btn btn-primary" ng-if="checkStatus(!person.isSuspended)" ng-click="person.isSuspend=suspendUser(person.isSuspend)">{{suspendText}}</button>
Javascript:
$scope.checkStatus = function (bool) {
if (bool) {
$scope.suspendText = "UNSUSPEND"
return true;
} else {
$scope.suspendText = "SUSPEND"
return false;
}
}
$scope.suspendUser = function (bool) {
if (bool) {
if ($window.confirm("Are You Sure Want to Unsuspend ?")) {
$scope.suspendText = "SUSPEND"
return !bool;
}
else {
return bool;
}
} else {
if ($window.confirm("Are You Sure Want to Suspend ?")) {
$scope.suspendText = "UNSUSPEND"
return !bool;
} else {
return bool;
}
}
}
Check this Plunkr: http://plnkr.co/edit/DEbTtpwu749sVT6iSojd?p=preview
Asumming you are through a User List with name & status (boolean true: Active - false: inactive):
user = {name:'John', status: true}
Here you can check how change the status, text & button color. In a short angular Way.
<li ng-repeat="user in users">
({{ user.active ? 'Active' : 'Inactive'}})
{{ user.name }}
<div ng-class="user.active? 'btn btn-danger' : 'btn btn-primary' " ng-click="user.active=!user.active">
{{ user.active ? 'Suspend' : 'Unsuspend'}}
</div>
</li>
<body ng-controller="MainCtrl as vm">
<button class="btn"
ng-class="{ 'btn-primary': vm.isSuspended , 'btn-danger': !vm.isSuspended }"
ng-bind="vm.text"
ng-click="vm.toggleSuspend()">
</button>
</body>
angular.module('app', [])
.controller('MainCtrl', function() {
var vm = this;
vm.toggleSuspend = function() {
vm.isSuspended = !vm.isSuspended;
vm.text = vm.isSuspended ? 'unsuspend' : 'suspend';
};
vm.isSuspended = true;
vm.toggleSuspend();
});
You will just need one button instead of two. A better way of showing the colors would be using ng-class, where you can write expressions to toggle the class.
<button type="button" class="btn" ng-class="person.isSuspended? 'btn-danger':'btn-success'" ng-click="person.isSuspended = !person.isSuspended">{{suspendText}}</button>
Note the way ng-class is written. I am using ternary operator to check if person.isSuspeneded is true, if it is, apply the class btn-danger, else apply btn-success. Now you have got a way cleaner code.
Attached is the plnkr - http://plnkr.co/edit/Uk2rHFacMFLbXyxGzK1b?p=preview
Try to replace your ng-if with ng-show in button.
<div ng-controller="MyCtrl">
<button type="button" class="btn btn-danger"
ng-show="person.isSuspend"
ng-click="person.isSuspend=suspendUser(!person.isSuspend)">{{suspendText}}</button>
<button type="button" class="btn btn-primary"
ng-show="!person.isSuspend"
ng-click="person.isSuspend=suspendUser(person.isSuspend)">{{suspendText}}</button>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope, $window) {
// first you need to initialize your scope if you didn't
$scope.person = {
isSuspend: false
};
$scope.suspendText = "SUSPEND";
$scope.suspendUser = function (bool) {
if (bool) {
if ($window.confirm("Are You Sure Want to Unsuspend ?")) {
$scope.suspendText = "SUSPEND"
$scope.person.isSuspended = false;
}
else {
$scope.person.isSuspended = true;
}
} else {
if ($window.confirm("Are You Sure Want to Suspend ?")) {
$scope.suspendText = "UNSUSPEND"
$scope.person.isSuspended = true;
} else {
$scope.person.isSuspended = false;
}
}
}
}
angular change button text

Angular expressions in formlyConfig.setType

The biggest question here is: Why would an expression like this class='{{prodStatusTextColor}}' not be updated in the view if the value of the variable scope.prodStatusText is indeed getting the new values?
Explanation:
I have an application that has a model that is loaded after search results and can be altered using a history function to load another dataset. The history function is contained in another grandparent scope. I have been able to confirm that the obj values that I am using for the dom changes do indeed update and hold the correct values when they should, (in the console) on the first load of the data and on the history change.
I am using formly-form and have a template set that contains 2-way bound variables to change styles and icons based on another fields values.
Templates follow:
formlyConfig.setType({
name: 'prodStatus',
template: "<label class='control-label'>Production Status</label><br><span class='{{prodStatusTextColor}}'><i class='{{prodStatusStatusIcon}}'></i> <span class='{{prodStatusTextColor}}'>{{model[options.key]}}</span> <!--i class='{{prodStatusInfoIcon}}'></i--></span>"
});
//term date
formlyConfig.setType({
name: 'termDate',
template: "<div data-ng-show='inclTermDate'><label class='control-label text-danger'>Termination Date</label><i class=fa fa-lg fa-calendar-times-o></i> <span class='text-danger'> {{model[options.key] | date:'yyyy-MM-dd'}} </span></div>"
});
I tried to use a service and define a default value for the variables as part of an existing model that was working. I could see the value changes but they were not being added to the html. I think they are just not being instantiated by the time the page renders?
Rendered HTML:
<div formly-field="" ng-repeat="field in fields " ng-if="!field.hide"
class="formly-field ng-scope ng-isolate-scope col-sm-6 col-lg-3 col-md-4
formly-field-prodStatus" options="field" model="field.model || model" fields="fields" form="theFormlyForm" form-id="formly_10" form-
state="options.formState" index="$index"><label class="control-label ng-scope">Production Status</label><br class="ng-scope"><span class=""><i class="">
</i> <span class="">Inactive</span> <!--i
class='{{prodStatusInfoIcon}}'></i--></span></div>
<label class="control-label ng-scope">Production Status</label><br class="ng-scope">
<span class=""><i class=""></i> <span class="">Inactive</span> <!--i class='{{prodStatusInfoIcon}}'></i--></span></div>
partial formly obj:
{
className: 'col-sm-6 col-lg-3 col-md-4',
key: 'AGENT_LNAME',
type: 'input',
templateOptions: {
type: 'text',
label: 'Last Name',
placeholder: 'Agent Last Name'
},
expressionProperties: {
'templateOptions.tabindex': '4'
},
watcher: {
expression: function(field, scope) {
return field.formControl && field.formControl.$viewValue;
},
listener: function(field, newValue, oldValue, scope, stopWatching) {
agentPersInfoModel.chng = {
prodStatusTextColor: "text-success",
prodStatusStatusIcon: "fa fa-lg fa-check-circle-o fa-lg",
prodStatusInfoIcon: "fa fa-lg fa-info-circle",
isActiveStatus : false,
inclTermDate : false
};
scope.prodStatusTextColor = agentPersInfoModel.chng.prodStatusTextColor;
scope.prodStatusStatusIcon = agentPersInfoModel.chng.prodStatusStatusIcon;
scope.prodStatusInfoIcon = agentPersInfoModel.chng.prodStatusInfoIcon;
scope.inclTermDate = agentPersInfoModel.chng.inclTermDate;
scope.isActiveStatus = agentPersInfoModel.chng.isActiveStatus;
if(newValue) {
console.log('Function Expression: ' + newValue);
console.log('Field: ');
console.log(field);
console.log('oldValue: ' + oldValue);
console.log(oldValue);
console.log('Scope: ');
console.log(scope);
console.log("agentPersInfoModel.chng");
console.log(agentPersInfoModel.chng);
console.log("agentModel prod status");
console.log(agentModel.singleAgent.PRODUCTION_STATUS);
if(agentModel.singleAgent.PRODUCTION_STATUS === 'Active' && agentModel.singleAgent.TERMINATION_DATE === "00010101") {
agentPersInfoModel.chng.isActiveStatus = true;
agentPersInfoModel.chng.prodStatusTextColor = "text-success";
agentPersInfoModel.chng.prodStatusStatusIcon = "fa fa-lg fa-check-circle-o fa-lg";
agentPersInfoModel.chng.prodStatusInfoIcon = "fa fa-lg fa-info-circle";
agentPersInfoModel.chng.inclTermDate = false;
console.log("============= in the listner (history) =====================");
console.log("we are active");
console.log("agentPersInfoModel.chng.prodStatusInfoIcon");
console.log(agentPersInfoModel.chng.prodStatusInfoIcon);
console.log("agentPersInfoModel.chng.prodStatusStatusIcon");
console.log(agentPersInfoModel.chng.prodStatusStatusIcon);
console.log("agentPersInfoModel.chng.prodStatusTextColor");
console.log(agentPersInfoModel.chng.prodStatusTextColor);
console.log("agentPersInfoModel.chng.inclTermDate");
console.log(agentPersInfoModel.chng.inclTermDate);
console.log("==================================");
} else if(agentModel.singleAgent.PRODUCTION_STATUS === 'Inactive') {
agentPersInfoModel.chng.prodStatusTextColor = "text-warning";
agentPersInfoModel.chng.prodStatusStatusIcon = "fa fa-ban fa-lg";
agentPersInfoModel.chng.prodStatusInfoIcon = " fa fa-lg fa-alert ";
agentPersInfoModel.chng.inclTermDate = false;
console.log("============= in the listner (history) =====================");
console.log("we are inactive");
console.log("agentPersInfoModel.chng.prodStatusInfoIcon");
console.log(agentPersInfoModel.chng.prodStatusInfoIcon);
console.log("agentPersInfoModel.chng.prodStatusStatusIcon");
console.log(agentPersInfoModel.chng.prodStatusStatusIcon);
console.log("agentPersInfoModel.chng.prodStatusTextColor");
console.log(agentPersInfoModel.chng.prodStatusTextColor);
console.log("agentPersInfoModel.chng.inclTermDate");
console.log(agentPersInfoModel.chng.inclTermDate);
console.log("==================================");
} else if(agentModel.singleAgent.TERMINATION_DATE !== "00010101") {
agentPersInfoModel.chng.prodStatusTextColor = "text-danger";
agentPersInfoModel.chng.prodStatusStatusIcon = "fa fa-times fa-lg";
agentPersInfoModel.chng.prodStatusInfoIcon = " fa fa-lg fa-alert ";
agentPersInfoModel.chng.prodStatusCalIcon = " fa fa-lg fa-calendar-times-o ";
agentPersInfoModel.chng.inclTermDate = true;
console.log("============= in the listner (history) =====================");
console.log("we are term'd");
console.log("agentPersInfoModel.chng.prodStatusInfoIcon");
console.log(agentPersInfoModel.chng.prodStatusInfoIcon);
console.log("agentPersInfoModel.chng.prodStatusStatusIcon");
console.log(agentPersInfoModel.chng.prodStatusStatusIcon);
console.log("agentPersInfoModel.chng.prodStatusTextColor");
console.log(agentPersInfoModel.chng.prodStatusTextColor);
console.log("agentPersInfoModel.chng.inclTermDate");
console.log(agentPersInfoModel.chng.inclTermDate);
console.log("==================================");
} else {
agentPersInfoModel.chng.isActiveStatus = false;
agentPersInfoModel.chng.prodStatusTextColor = "text-warning";
agentPersInfoModel.chng.prodStatusStatusIcon = "fa fa-ban fa-lg";
agentPersInfoModel.chng.prodStatusInfoIcon = " fa fa-lg fa-alert ";
agentPersInfoModel.chng.inclTermDate = false;
console.log("============= in the listner (history)=====================");
console.log("we didnt match");
console.log("agentPersInfoModel.chng.prodStatusInfoIcon");
console.log(agentPersInfoModel.chng.prodStatusInfoIcon);
console.log("agentPersInfoModel.chng.prodStatusStatusIcon");
console.log(agentPersInfoModel.chng.prodStatusStatusIcon);
console.log("agentPersInfoModel.chng.prodStatusTextColor");
console.log(agentPersInfoModel.chng.prodStatusTextColor);
console.log("agentPersInfoModel.chng.inclTermDate");
console.log(agentPersInfoModel.chng.inclTermDate);
console.log("==================================");
}
}
}
},
controller: /*#ngInject*/function($scope, AgentPersInfoModel) {
$scope.switchTermColors = function (status) {
};
}
}
console.log:
agentModel prod status
agentPersonalInfoFormly-service.js:221 Inactive
============= in the listner (history) =====================
agentPersonalInfoFormly-service.js:245 we are inactive
agentPersInfoModel.chng.prodStatusInfoIcon
agentPersonalInfoFormly-service.js:247 fa fa-lg fa-alert
agentPersInfoModel.chng.prodStatusStatusIcon
agentPersonalInfoFormly-service.js:249 fa fa-ban fa-lg
agentPersInfoModel.chng.prodStatusTextColor
agentPersonalInfoFormly-service.js:251 text-warning
agentPersInfoModel.chng.inclTermDate
agentPersonalInfoFormly-service.js:253 false
The history function simply fetches a new agent based on an id and loads that into the existing agentModel, does not actually touch these functions, these are supposed to change if the agentModel changes.
Any discussions or assistance would be greatly appreciated.
All the "code" inside your main div is actually in a new child scope created by the ng-repeat="field in fields " therefore any primitives (like prodStatusTextColor) that you use inside it are shadowed (they "hide" the original variable after they copy their initial value).
You should try using the . dot notation instead, as per angular recommandations (check this doc on scopes): you should encapsulate those variables inside an object and use them by reference.
Something like this:
in the listener you may have:
prodStatus = {
prodStatusTextColor: agentPersInfoModel.chng.prodStatusTextColor,
prodStatusStatusIcon: agentPersInfoModel.chng.prodStatusStatusIcon,
prodStatusInfoIcon: agentPersInfoModel.chng.prodStatusInfoIcon,
inclTermDate: agentPersInfoModel.chng.inclTermDate,
isActiveStatus: agentPersInfoModel.chng.isActiveStatus
};
and in the view markup you can use it like this:
<i class='{{prodStatus.prodStatusInfoIcon}}'></i>
Even simpler, you may want to put the agentPersInfoModel directly in the scope:
scope.agentPersInfoModel = agentPersInfoModel.chng;
and use it in the markup:
<i class='{{agentPersInfoModel.prodStatusInfoIcon}}'></i>
Hope this helps!
Code is not tested but should do the work.

Ng-grid search bar in the grid

Right now I am using ng-grid and the grid option, showFilter: true
This places a triangle drop down in the top right corner of the grid, which when clicked pops up a search bar.
Is there a way with ng-grid to have the search bar directly visible in the grid without the need to click the triangle?
Sure, just make a input text wherever you want in your view with your ng-grid:
<input type="text" data-ng-model="outside_search_text" placeholder="Search"
class="form-control" data-ng-change="search()">
<div class="gridStyle" data-ng-grid="gridOptions">
</div>
and then in your controller:
$scope.sortOptions = {
fields : ['id'],
directions : ['DESC']
};
$scope.totalServerItems = 0;
$scope.gridOptions['sortInfo'] = $scope.sortOptions;
$scope.gridOptions['columnDefs'] = [
{field : 'name', displayName: 'Name'}
];
$scope.refresh = function() {
var p = {
searchText : {name: $scope.outside_search_text},
pageNumber : $scope.pagingOptions.currentPage,
pageSize : $scope.pagingOptions.pageSize,
sortField : $scope.sortOptions.fields,
sortDirection : $scope.sortOptions.directions
};
YourServices.getAll(p).then(function(response) {
var content = getResponseData(response);
$scope.totalServerItems = content.count;
$scope.myData = content.result_set;
if (content['result_set'].length > 0) {
//whatever
} else {
//whatever
}
});
};

Angularjs bindings not being updated

I am facing a problem with my angular js bindings not being updated correctly.
I am trying to achieve a way to hide certain form elements and show others by clicking a "next" button.
I have setup some objects in my controller to hold values for input text fields and menu dropdowns, I also have setup a couple of button (next and previous and add) button to be able to add new objects and a next and previous buttons to be able to navigate between the different stored objects.
The problem that I am facing is that the input text field is being updated correctly when i press the next and previous button however the dropdown menus are not.
This is a link to a jsfiddle to help show the problem:
http://jsfiddle.net/bLs9yu3f/
Found two issues with the code in your Fiddle:
First, when assigning programOutcomes to the affects key of your objects (both when creating the initial one and pushing to add a new one) you where assigning programOutcomes directly, which assigns a pointer to the original array and doesn't create a copy. There are many ways to do this. I chose affects: JSON.parse(JSON.stringify(programOutcomes)). See the example below.
$scope.output.outcomes.push({
outcome: '',
affects: JSON.parse(JSON.stringify(programOutcomes))
});
Second, in the for loop of your addCourseOutcome function you refer to $scope.output.outcomes[0] instead of the latest $scope.output.outcomes you just pushed. The following code fixes this issue.
var lastest = $scope.output.outcomes.length - 1;
for (var i = 0; i < programOutcomes.length; i++) {
$scope.output.outcomes[lastest].affects[i].how = '';
}
This is a fork of your Fiddle with the corrections I mentioned above: http://jsfiddle.net/JohnnyEstilles/uz8zf2b0/.
angular.module('myapp', []).controller('ProgramsController', ['$scope',
function($scope) {
var programOutcomes = [{
outcome: 'po1'
}, {
outcome: 'po2'
}, {
outcome: 'po3'
}, {
outcome: 'po4'
}];
$scope.input = {
outcomeCounter: 0,
programOutcomes: programOutcomes,
actions: ['', 'I', 'E', 'R']
};
$scope.output = {
outcomes: [{
outcome: '',
affects: JSON.parse(JSON.stringify(programOutcomes))
}]
};
for (var i = 0; i < programOutcomes.length; i++) {
$scope.output.outcomes[0].affects[i].how = '';
}
$scope.nextOutcome = function() {
$scope.input.outcomeCounter++;
};
$scope.previousOutcome = function() {
$scope.input.outcomeCounter--;
};
$scope.deleteCourseOutcome = function() {
$scope.output.outcomes.splice($scope.input.outcomeCounter, 1);
$scope.input.outcomeCounter--;
};
$scope.addCourseOutcome = function() {
$scope.output.outcomes.push({
outcome: '',
affects: JSON.parse(JSON.stringify(programOutcomes))
});
/**
* create a 'how' property in the affects array
* to be used for storage of I, E, R
*/
var lastest = $scope.output.outcomes.length - 1;
console.log($scope.output.outcomes[lastest].affects);
for (var i = 0; i < programOutcomes.length; i++) {
$scope.output.outcomes[lastest].affects[i].how = '';
}
/**
* increment the outcomeCounter
*/
$scope.input.outcomeCounter++;
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myapp">
<div ng-controller="ProgramsController">
<div class="form-group">
<label for="outcome">Outcome</label>
<input id="outcome" placeholder="Outcome" class="form-control" ng-model="output.outcomes[input.outcomeCounter].outcome">
</div>
<div class="form-group">
<table class="table table-striped">
<tr ng-repeat="programOutcome in input.programOutcomes">
<td>{{programOutcome.outcome}}</td>
<td>
<select ng-model="output.outcomes[input.outcomeCounter].affects[$index].how" ng-options="value for value in input.actions">
</select>
</td>
</tr>
</table>
</div>
<div class="form-group">
<button class="btn" ng-click="addCourseOutcome()">Add outcome</button>
<button class="btn" ng-click="nextOutcome()"
ng-if="output.outcomes.length>1 && input.outcomeCounter !== (output.outcomes.length - 1)">
Next
</button>
<button class="btn" ng-click="previousOutcome()"
ng-if="output.outcomes.length>1 && input.outcomeCounter > 0">
Previous
</button>
<button class="btn btn-warning" ng-click="deleteCourseOutcome()">Delete outcome</button>
</div>
</div>
</body>

Resources