I've written the following code to read data from json file and draw a graph. now I want to get the data from user through input fields and update the json with new values. I wrote the code to copy the value and made it accessible outside the scope, I just wonder how to update the existent json with the new values.
json looks like this:
{
"nodes" : [
{ "id": 1, "label": "End Product", "level": 0 },
{ "id": 2, "label": "A 1", "level": 1 },
{ "id": 3, "label": "A 2", "level": 1 },
{ "id": 4, "label": "A 3", "level": 1 },
{ "id": 5, "label": "B 1", "level": 2 },
{ "id": 6, "label": "C 1", "level": 3 },
{ "id": 7, "label": "B 2", "level": 2 },
{ "id": 8, "label": "B 3", "level": 2 }
],
"edges" : [
{ "from": 1, "to": 2 },
{ "from": 1, "to": 3 },
{ "from": 1, "to": 4 },
{ "from": 2, "to": 5 },
{ "from": 5, "to": 6 },
{ "from": 3, "to": 7 },
{ "from": 4, "to": 8 }
]
}
var app = angular.module('bomApp', ['bomGraph']);
app.controller('bomController', ['$scope', 'appService', '$rootScope', function ($scope, appService, $rootScope) {
var get = function () {
appService.get().then(function (promise) {
$scope.graph = {
options: {
"hierarchicalLayout": {
"direction": "UD"
},
"edges": {
"style":"arrow-center",
"color":"#c1c1c1"
},
"nodes": {
"shape":"oval",
"color":"#ccc"
}
},
data: {
nodes: promise.nodes,
edges: promise.edges
}
};
});
};
$scope.newNode = {
id: undefined,
label: undefined,
level: undefined,
parent: undefined,
};
$scope.arrNode = {};
$scope.update = function (nodes) {
$scope.arrNode = angular.copy(nodes);
$rootScope.nodex = angular.copy(nodes);
};
$scope.newEdge = {
id: undefined,
from: undefined,
to: undefined
};
$scope.arrEdge = {};
$scope.updateE = function (edges) {
$scope.arrEdge = angular.copy(edges);
$rootScope.edgex = angular.copy(nodes);
};
get();
}]);
app.factory('appService', ['$q', '$http', '$rootScope', function ($q, $http, $rootScope) {
return {
get: function (method, url) {
var deferred = $q.defer();
$http.get('data.json')
.success(function (response) {
deferred.resolve(response);
})
return deferred.promise;
},
};
}]);
<body data-ng-app="bomApp">
<div data-ng-controller="bomController">
<h3>Create Your Own Graph</h3>
<div class="node">
<h4>Nodes:</h4>
<div class="panel">
<label>Id:</label>
<input type="text" id="idNode" data-ng-model="newNode.id"/>
<br />
<label>Label:</label>
<input type="text" id="label" data-ng-model="newNode.label" />
<br />
<label>Level:</label>
<input type="text" id="level" data-ng-model="newNode.level" />
</div>
<div>
<button id="addNode" data-ng-click="update(newNode)">Add</button>
</div>
</div>
<div class="node">
<h4>Edges:</h4>
<div class="panel" >
<label>Id:</label>
<input type="text" id="idEdge" data-ng-model="newEdge.id"/>
<br />
<label>From:</label>
<input type="text" id="from" data-ng-model="newEdge.from"/>
<br />
<label>To:</label>
<input type="text" id="to" data-ng-model="newEdge.to"/>
</div>
<div>
<button id="addEdge" data-ng-click="updateE(newEdge)">Add</button>
</div>
</div>
<div data-custom-dir="" id="graph" data="graph.data" options="graph.options"></div>
<div>
<h3>Monitor The Changes</h3>
<div class="node">
<h4>Nodes:</h4>
<div class="col">
<div id="nodes">
<pre>{{newNode | json}}</pre>
<pre>{{arrNode | json}}</pre>
</div>
</div>
</div>
<div class="node">
<h4>Edges:</h4>
<div class="col">
<div id="edges">
<pre>{{newEdge | json}}</pre>
<pre>{{arrEdge | json}}</pre>
</div>
</div>
</div>
</div>
</div>
</body>
You can add items to a javascript object using .push(). To make an update, you'll have to loop through the object until you find a matching id (or some other value).
Finally, you can use .appendTo() to update the section that indicates what changes have been made.
Example:
$('#addNode').click(function () {
// pseudo code to add item to javascript object:
// 'node' would be presumably nested based on the OP,
// but information on js object name is not provided
var nodeId = $('#idNode').val();
var nodeLabel = $('#label').val();
var nodeLevel = $('#level').val();
nodes.push({
"id": nodeId,
"label": nodeLabel,
"level": nodeLevel,
});
// psudeo code for updating js object
// loop through object to find matching id:
var locate = 0;
for (var i = 0; i < nodes.length; i++) {
if(node[i].id == nodeId){
locate = i;
node[i].label = nodeLabel;
node[i].level = nodeLevel;
}
}
// Adding results to element
var nodesElem = $('#nodes');
var addElem = "<pre> { newNode | " + JSON.stringify(node[locate]) + " }";
addElem.appendTo(nodesElem);
});
If you need it to be able to change existing records try :
$scope.graph.data.nodes[$scope.newNode.id] = $scope.newNode;
and
$scope.graph.data.edges.push($scope.newEdge);
These methods function calls should update the existing data with the newNode and the newEdge
Related
I have an object like this and I need to filter the rules within each group item, however I need to also return the group name next to the filtered rule
{
"id": "rulesCompany",
"group": [
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [
{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
},
]
}
For example, I need to return the group "Cadastral/Receita Federal" if I search for "Rece" in search field, but I don't know how to filter data inside data.
What I've done so far:
Module.vue
<template>
<div>
<slide-out :visible.sync="isVisible" :title="text.header">
<div slot="header">
<div>
<button class="neo-bt-icon--big slideout__close--button" #click="isVisible=false">
<svg><use xlink:href="/red/neo-assets/images/simple-line-icons.svg#arrow-2-left"></use></svg>
</button>
<h1 class="slideout__header--text">
{{ text.header }}
</h1>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input type="text" class="neo-form-field" placeholder="Buscar" v-model="searchQuery">
<input class="neo-form-toggle__field" :id="selectAllRules" #click="selectAllRules($event)" type="checkbox"/>
<label class="neo-form-toggle__label" :for="selectAllRules">selecionar tudo</label>
</div>
</div>
</div>
<div slot="content">
<div v-for="policyRule in filteredPolicyRules.group" :key="policyRule.name"
class="neo-form-group">
<li v-text="policyRule.name"></li>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="policyRule.name" #click="selectGroupRules(policyRule.rule, policyRule.name, $event)" type="checkbox" v-model="policyRules.name" />
<label class="neo-form-toggle__label" :for="policyRule.name">selecionar grupo</label>
</div>
<div class="neo-form-toggle-list neo-form-toggle-list--inline">
<div v-for="rule in policyRule.rule" :key="rule.title"
class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="rule.title" :value="rule" name="rule" type="checkbox" v-model="checkedRules"/>
<label class="neo-form-toggle__label" :for="rule.title">{{ rule.title }}</label>
<h6 class="neo-text-disabled-options">{{ rule.description }}</h6>
</div>
</div>
</div>
</div>
<div slot="footer">
<span>{{ checkedRules }}</span>
</div>
</slide-out>
</div>
</template>
<script>
import { mapState } from 'vuex';
import SlideOut from '#/components/shared/slideout/SlideOut.vue';
export default {
name: 'ModulePolicyRules',
props: [],
components: {
SlideOut,
},
data() {
return {
isVisible: false,
policyRules: [],
searchQuery: '',
checkedRules: [],
filteredRules: [],
};
},
computed: {
filteredPolicyRules() {
const me = this;
if (this.searchQuery) {
me.filteredRules.pop();
this.policyRules.group.filter((ruleGroup) => {
ruleGroup.rule.forEach((rule) => {
if (rule.title.startsWith(this.searchQuery)) {
console.log(me.filteredRules);
me.filteredRules.push(rule);
}
});
});
console.log(me.filteredRules);
return me.filteredRules;
// return this.policyRules.group.filter(item => item.name.startsWith(this.searchQuery));
}
return this.policyRules;
},
},
methods: {
async loadData() {
const rules = await this.$store.dispatch('policyrules/setPolicyRules');
this.policyRules = rules;
},
},
mounted() {
this.loadData();
},
};
</script>
<style lang="scss">
.neo-form-toggle__label {
text-transform: none;
font-weight: 600;
}
.neo-text-disabled-options {
text-transform: none;
}
</style>
Object expected result using "Rec" in search field:
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
Try this computed prop.
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = [];
rule.forEach(r => {
if (r.title.startsWith(this.searchQuery)) {
rules.push(r);
}
});
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
I'd suggest calling them groups and rules (plural) respectively, to avoid future confusion -- after all they are arrays.
Full demo:
const policyRules = {
"id": "rulesCompany",
"group": [{
"name": "Cadastral",
"rule": [{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
}]
};
new Vue({
el: '#app',
data() {
return {
searchQuery: '',
policyRules
}
},
computed: {
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = rule.filter(this.matchFilter);
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
},
methods: {
matchFilter(item) {
const
search = this.searchQuery.toLowerCase(),
term = (item.title || '').toLowerCase();
return term.includes(search);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="searchQuery" placeholder="Search..." />
<pre>{{ filteredPolicyRules }}</pre>
</div>
So first I've put your list into a map variable. Then I'm filtering that by checking if any of the wanted properties contain the search term. The array.filter Method returns a new array based on what entries returned true and what false.
I'm checking inside name, title and description. Also I made everything lower case so case doesn't matter.
Array.prototype.filter Reference
const term = 'CNE';
console.log(map.filter(e => {
const { name } = e;
if(contains(name, term)) return true;
for(const _r of e.rule) {
const { title, description } = _r;
if(contains(title, term)) return true;
if(contains(description, term)) return true;
}
return false;
}));
function contains(str, term) {
return str.toLowerCase().includes(term.toLowerCase());
}
And I would also suggest like Yom in his answer that you use groups and rules so you can name them better. So that then would be groups.filter(group => {[..]}) and for(const rule of group.rules)
Ui-Grid external filtering is not working and also didn't show me the data of an object Json. I am retreiving a data from server and it has complexity.
HTML--->
<div ng-controller="MainCtrl">
<input ng-model='filterValue'/>
<button ng-click='filter()'>Filter</button>
<button type="button" class="btn btn-sm btn-default"
ng-click="changeFilter('')">Filter Clear</button>
<button type="button" class="btn btn-sm btn-default"
ng-click="changeFilter('1')">Filter (1)</button>
<button type="button" class="btn btn-sm btn-default"
ng-click="changeFilter('5')">Filter (5)</button>
<button type="button" class="btn btn-success"
ng-disabled="!gridApi.grid.options.multiSelect"
ng-click="selectAll()">Select All</button>
<button type="button" class="btn btn-success"
ng-click="clearAll()">Clear All</button>
<br />
<div ui-grid="gridOptions" ui-grid-selection="" class="grid"></div>
<hr />
</div>
AngularJS---->
var app = angular.module('app', [
'ngTouch', 'ui.grid', 'ui.grid.selection']);
app.controller('MainCtrl', [
'$scope', '$http', '$log', '$timeout', 'uiGridConstants',
function ($scope, $http, $log, $timeout, uiGridConstants) {
$scope.gridOptions = {
enableFullRowSelection: true,
modifierKeysToMultiSelect: true,
enableRowSelection: true,
enableSelectAll: true,
showGridFooter:true,
enableFiltering: true
};
$scope.gridOptions.columnDefs = [
{ name: 'id' },
{ name: 'title'},
{ name: 'createdDate' },
{ name: 'description' }
];
$scope.gridOptions.multiSelect = true;
$scope.filter = function() {
$scope.gridApi.grid.refresh();
};
$scope.singleFilter = function( renderableRows ){
var matcher = new RegExp($scope.filterValue);
renderableRows.forEach( function( row ) {
var match = false;
[
'id',
'title',
'createdDate',
'description',
].forEach(function( field ){
if (row.entity[field].match(matcher)) {
match = true;
}
});
if (!match) {
row.visible = false;
}
});
return renderableRows;
};
$http.get('test.json')
.success(function(data) {
console.log(data);
$scope.gridOptions.data = data;
$timeout(function() {
if($scope.gridApi.selection.selectRow){
$scope.gridApi.selection.selectRow($scope.gridOptions.data[0]);
}
});
});
$scope.info = {};
$scope.selectAll = function() {
$scope.gridApi.selection.selectAllRows();
};
$scope.clearAll = function() {
$scope.gridApi.selection.clearSelectedRows();
};
$scope.changeFilter = function(val){
$scope.filterValue = val;
$scope.gridApi.grid.refresh();
}
$scope.gridOptions.onRegisterApi =
function(gridApi){
//set gridApi on scope
$scope.gridApi = gridApi;
gridApi.selection.on.rowSelectionChanged($scope, function (row) {
});
$scope.gridApi.grid.registerRowsProcessor( $scope.singleFilter, 200 );
gridApi.selection.on.rowSelectionChangedBatch($scope,function(rows){
var msg = 'rows changed ' + rows.length;
$log.log(msg);
});
};
}])
Server Response and simplified version of complexity--->;
[ {
"id": 1882,
"eventTypeId": 1,
"occuredDate": "2016-06-06T00:00:00",
"title": "Event Refresh",
"description": "Test for auto refresh",
"studyId": 2,
"statusId": 1,
"severityId": 3,
"priorityId": 2,
"status": 1,
"createdDate": "2016-06-06T13:53:42",
"$$hashKey": "uiGrid-0014"
},
{
"id": 1879,
"eventTypeId": 2,
"occuredDate": "2016-06-03T00:00:00",
"title": "Test one more timeout",
"description": "testing timeout",
"studyId": 4,
"statusId": 5,
"severityId": 2,
"priorityId": 2,
"status": 1,
"createdDate": "2016-06-06T13:53:42",
"$$hashKey": "uiGrid-001A"
},
{
"id": 1882,
"eventTypeId": 1,
"occuredDate": "2016-06-06T00:00:00",
"title": "Event Refresh",
"description": "Test for auto refresh",
"studyId": 2,
"statusId": 1,
"severityId": 3,
"priorityId": 2,
"status": 1,
"createdDate": "2016-06-06T13:53:42",
"$$hashKey": "uiGrid-0014"
}]
Here is an object i can reach any data of the server response but when the time has come to show data on ui-grid its kind of ignoring the whole data like as in the plunker that i created.
here is the not working plunker i have created one. Please have a look at that what I am missing here? and please update the plunk if any solution has come.
thanks
The code row.entity[field].match(matcher) is problematic as there is no match function, you need to use .test:
$scope.singleFilter = function( renderableRows ){
var matcher = new RegExp($scope.filterValue);
renderableRows.forEach( function( row ) {
var match = false;
['id','title','createdDate','description',].forEach(function( field ){
if (matcher.test(row.entity[field])) {
match = true;
}
});
if (match) {
row.visible = true;
}
else{
row.visible = false;
}
});
return renderableRows;
};
It's worth noting that your filter is in fact case-sensitive so if you search for 'event' you won't get anything, but 'Event' will get you three results.
Working plnkr
I want to modify this product angular / javascript filter
When user select for example: Processor - 2GHz and Memory - 32GB I want to show only product that have all those properties.
Right now filter shows all products with 2GHz and all products with 32GB.
Plunker
https://plnkr.co/edit/vr9o2aHkHvCL1cvgKILd?p=preview
angular.module('app', [])
.controller('Controller', function($scope) {
$scope.items = [{
"name": "Product - 2GHZ, 32GB, Black",
"tags": [{
"category": 1,
"tag": 2
}, {
"category": 2,
"tag": 4
}, {
"category": 3,
"tag": 8
}]
}, {
"name": "Product - 2GHz, 128GB, Black",
"tags": [{
"category": 1,
"tag": 2
}, {
"category": 2,
"tag": 5
}, {
"category": 3,
"tag": 8
}]
}, {
"name": "Product - 1GHz, 128GB, White",
"tags": [{
"category": 1,
"tag": 1
}, {
"category": 2,
"tag": 5
}, {
"category": 3,
"tag": 7
}]
}];
$scope.items_dup = $scope.items
// checkbox selection
$scope.selectionTag = [];
$scope.selectionCat = [];
$scope.toggleSelection = function toggleSelection(tag, category) {
var idxTag = $scope.selectionTag.indexOf(tag);
if (idxTag > -1) {
$scope.selectionTag.splice(idxTag, 1);
} else {
$scope.selectionTag.push(tag);
}
// this is not working, probably need twodimensional array
// category
var idxCat = $scope.selectionCat.indexOf(category);
if (idxCat > -1) {
$scope.selectionCat.splice(idxCat, 1);
} else {
$scope.selectionCat.push(category);
}
};
// filter list
$scope.filter = function() {
filterTag($scope.selectionTag, $scope.items);
function filterTag(selected, items) {
var out = [];
angular.forEach(items, function(value, key) {
angular.forEach(selected, function(inner_value, key) {
angular.forEach(value.tags, function(inner_value2, key) {
if (value.tags[key].tag === inner_value) {
if (out.indexOf(value) == -1) {
out.push(value)
}
}
})
})
})
if (out.length > 0) {
$scope.items_dup = out;
} else {
$scope.items_dup = $scope.items;
}
}
};
})
<!DOCTYPE html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="Controller">
<h1>Product filtering!</h1>
<li ng-repeat="item in items_dup">
{{item.name}}
</li>
<hr>
<p data-category="1">Processor</p>
<label>
<input type="checkbox" value="1" ng-click="toggleSelection(1,1)"> 1GHz
</label>
<br>
<label>
<input type="checkbox" value="2" ng-click="toggleSelection(2,1)"> 2GHz
</label><br>
<hr>
<p data-category="2">Memory</p>
<label>
<input type="checkbox" value="4" ng-click="toggleSelection(4,2)"> 32GB
</label>
<br>
<label>
<input type="checkbox" value="5" ng-click="toggleSelection(5,2)"> 128GB
</label><br>
<hr>
<p data-category="3">Color</p>
<label>
<input type="checkbox" value="7" ng-click="toggleSelection(7,3)"> White
</label>
<br>
<label>
<input type="checkbox" value="8" ng-click="toggleSelection(8,3)"> Black
</label><br>
<br><br>
<button ng-click="filter()">Filter list</button>
</div>
</body>
</html>
You could use Array#every to check if each selection appears in an item's tag list. If you want to switch back from an and to an or filter, you can use Array#some instead.
The good thing about these methods is that they return as soon as they know their result, ending the loop early when possible.
$scope.filter = function() {
$scope.items_dup = $scope.items.filter(function(item) {
return $scope.selectionCat.every(function(c) {
return item.tags
.some(function(t) {
return t.category === c &&
$scope.selectionTag.indexOf(t.tag) !== -1;
});
})
});
}
In a plunker: here
An example of how you could create a map, and how it would affect your filter:
var $scope.items = [ /* ... */ ].map(function(item) {
var tagMap = item.tags.reduce(function(result, t.tag) {
result[t.tag] = true; // Add the tag Id to the map
return result;
}, {});
// Add the tag map to the item
return Object.assign(item, { tagMap: tagMap });
});
// Now, you can filter like so:
var matches = selectedTagIds.every(function(id) {
return item.tagMap[id];
});
another approach is using filter with filter object on ng-repeat, in this case items are filtered immediately when check boxes state changes:
<li ng-repeat="item in items_dup | filter : mainFilter : true">
{{item.name}}
</li>
all check boxes use ng-model and trigger update function:
<label>
<input type="checkbox" ng-change="updateFilters()" ng-model="filters.processor['1GHZ']"> 1GHz
</label>
update function:
$scope.updateFilters = function updateFilters() {
$scope.mainFilter = {};
angular.forEach($scope.filters, function (category, categoryKey) {
angular.forEach(category, function (value, valueKey) {
if (value) {
if ($scope.mainFilter[categoryKey]) {
delete $scope.mainFilter[categoryKey];
}
else {
$scope.mainFilter[categoryKey] = valueKey;
}
}
});
});
};
each item has processor, memory and color attributes added.
plunker: https://plnkr.co/edit/6BDVYzV430N0MSaopGXt?p=preview
For some reason, when I splice an object into my ng-repeat array, it doubles what I splice in and hides the last object in the array.
However, if I click the toggle hide and "refresh" the ng-repeat, it shows the right data.
Does anyone know why this would be happening and what I can do to fix it?
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.workflow = {
flow: [{
"id": "1334f68db820f664",
"step_number": 1,
"tasks": [{
"id": "1334f68e3f20f665"
}]
}, {
"id": "134967a5ba205f5b",
"step_number": 2,
"tasks": [{
"id": "134972c5b420e027"
}]
}, {
"id": "1334f68e7d209ae6",
"step_number": 3,
"tasks": [{
"id": "1334f68ef6209ae7"
}]
}]
};
$scope.insertStep = function() {
var insertStepIndex = 1,
task_data = {
"id": null,
"step_number": (insertStepIndex + 2),
"tasks": []
};
//go through each item in the array
$.each($scope.workflow.flow, function(index, step) {
//if the step number is greater then the place you want to insert it into, increase the step numbers
if (step.step_number > $scope.workflow.flow[insertStepIndex].step_number) step.step_number++;
});
$scope.workflow.flow.splice((insertStepIndex + 1), 0, task_data);
}
$scope.toggleHide = function() {
$scope.hide = !$scope.hide;
}
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-click="insertStep()">Insert Step</div>
<br />
<br />
<div ng-click="toggleHide()">Toggle Repeat</div>
<br />
<br />
<div ng-if="!hide">
<div ng-repeat="data in workflow.flow | orderBy:'+step_number'" ng-init="$stepIndex = workflow.flow.indexOf(data)">
{{ workflow.flow[$stepIndex].step_number }}
</div>
</div>
</div>
I got the problem. This is a tricky but the simple part. The ng-init directive only execute once. So your assignment to $stepIndex = workflow.flow.indexOf(data) will not updated when you push a new data to the array/list.
So adding a scope function will fix this problem since Angular will auto call the function when it's returning value changes.
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.workflow = {
flow: [{
"id": "1334f68db820f664",
"step_number": 1,
"tasks": [{
"id": "1334f68e3f20f665"
}]
}, {
"id": "134967a5ba205f5b",
"step_number": 2,
"tasks": [{
"id": "134972c5b420e027"
}]
}, {
"id": "1334f68e7d209ae6",
"step_number": 3,
"tasks": [{
"id": "1334f68ef6209ae7"
}]
}]
};
$scope.insertStep = function() {
var insertStepIndex = 1
var task_data = {
"id": null,
"step_number": (insertStepIndex + 2),
"tasks": []
};
//go through each item in the array
angular.forEach($scope.workflow.flow, function(step, index) {
//if the step number is greater then the place you want to insert it into, increase the step numbers
if (step.step_number > $scope.workflow.flow[insertStepIndex].step_number) step.step_number++;
});
$scope.workflow.flow.splice((insertStepIndex + 1), 0, task_data);
}
// This is a new function which I added to fix the problem
$scope.getIndex = function(data) {
return $scope.workflow.flow.indexOf(data);
};
$scope.toggleHide = function() {
$scope.hide = !$scope.hide;
};
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-click="insertStep()">Insert Step</div>
<br />
<br />
<div ng-click="toggleHide()">Toggle Repeat</div>
<br />
<br />
<div ng-if="!hide">
<div ng-repeat="data in workflow.flow | orderBy:'+step_number'">
{{ workflow.flow[getIndex(data)].step_number }}
</div>
</div>
</div>
Live Code -- JSBIN Link
I have a nested lists of javascript objects Templates.schema
There will be many sections per page and I must keep track of them (via an index).
How do I make sure on submit of form, that addPage() function creates an array on the section via input ng-model="pageAdder.page.section
<form ng-model="pageAdder" ng-submit="addPage()">
<label for="page.name">PAGE</label>
<input type="text" ng-model="pageAdder.page.name">
<br>
<hr>
<label for="page.name">Section</label>
<select id="s1" ng-model="pageAdder.page.section" ng-options="item as item.type for item in items"></select>
<br>
<hr>
<input class="btn btn-primary" type="submit" value="add Page">
</form>
to...
templates: {
"schema": [
{
"page": {
"name": "asdfasdf",
"section": [{
"id": 1,
"type": "foo"
}]//adds array
}
}
]
}
controller
angular.module("ang6App")
.controller("MainCtrl", function ($scope) {
})
.controller("templatesCtrl", function ($scope, Templates) {
$scope.template = Templates;
$scope.addPage = function() {
$scope.template.schema.push($scope.pageAdder);
};
$scope.items = [
{ id: 1, type: 'foo' },
{ id: 2, type: 'bar' },
{ id: 3, type: 'blah' }];
$scope.pageAdder = {
};
//end of templates
});
add default value to $scope.pageAdder
$scope.pageAdder = {
"page": {
"name": null,
"section": [{
"id": null,
"type": null
}]
}
And it works fine:
http://jsbin.com/OGipAVUF/45/edit