Why does this ng-model variable not work? - angularjs

So I'm trying to create a program in Angular for displaying train schedules. Here's my question, why does the "firstTT" variable break the program?
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-app = "myApp" ng-controller = "myCtrl">
<table>
<tr><th>Train Name</th><th>Destination</th><th>Frequency</th><th>Minutes Away</th><th>Next Arrival</th><th></th></tr>
<tr ng-repeat="x in trainArray">
<td>{{ x.name }}</td>
<td>{{ x.dest }}</td>
<td>{{ x.freq }}</td>
<td></td>
<td></td>
<td>X</td>
</tr>
</table>
{{dayMinutes}}
<form>
Train Name: <input type = "text" ng-model = "name"/>
<br>
<br>
Destination: <input type = text" ng-model = "dest"/>
<br>
<br>
First Train Time: <input type="text" placeholder = "(ex. 08:45 for 8:45pm)" ng-model = "firstTT">
<br>
<br>
Frequency (Minutes): <input type="text" style = "width: 50px;" ng-model = "freq">
<br>
<br>
<input type = "submit" value = "submit" id = "submit" data-toggle = "tool-tip" title = "add train">
</form>
</div>
<script>
var dayMillis = moment().diff(moment().startOf('day'));
//minutes since start of day
var dayMinutes = dayMillis/60000;
//why the hell does it say this is "not defined" and break the program?
// console.log(firstTT);
//regardless, I need to get the diff between this and the start of the day.
//that should look something like this:
// var startMinutes = moment(firstTT, "hh:mm").diff(moment().startOf('day'));
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope){
$scope.firstTT = "";
$scope.dayMinutes = dayMinutes;
$scope.trainArray = [
{
name: "Thomas",
dest: "New York",
first: "05:00",
freq: 30
},
{
name: "Duncan",
dest: "Boston",
first: "06:00",
freq: 45
}]
})
</script>
</body>
</html>
It claims the variable is undefined, and that causes all the AngularJS to cease to function. I tried adding the "init" and the "$scope", but that didn't work. What's the deal?

The variable firstTT is indeed undefined, you never declare it. It seems that you are expecting firstTT to be the value of the input field where you have declared ng-model="firstTT". In angularjs the model part of the model-view-controller is a $scope, this means that ng-model="firstTT" binds to $scope.firstTT which can only be accessed in your controller implementation.
My guess is that you would like to calculate startMinutes when the value in the input field changes, to do this you need to do a couple of things.
First implement a method which can be called when the input value changes, you must do this on the $scope so that you can bind the method in the view.
$scope.onFirstTrainTimeChange = function() {
var startMinutes = moment($scope.firstTT, "hh:mm").diff(moment().startOf('day'));
};
Then use the ng-change directive on your input field to call the method above when the input changes.
<input ... ng-model="firstTT" ng-change="onFirstTrainTimeChange()">
You may also want to do other things, like handle errors when the input is not in the correct format, and use the ng-model-options directive to control when the model gets updated (e.g. debounce) so that the method isn't called until the user stops making changes to the input field.

Related

Construct json from dynamic key and values in angularjs

i am developing a web application where am creating textboxes dynamically using the attributes from server. I am successfull in displaying the attribute values as html table inside modal. I need to create json object using the attributes in controller and make two way binding using angularjs. I am very new to angularjs.I need a json using the key and values like
{"NAME": "",
"TYPE: "forest"} and make two way binding for this dynamically created textboxes.
<tr ng-repeat="(key, value) in prop['properties']">
<td ><label>{{ key}}</label></td>
<td><input type="text" ng-value="value"></td>
</tr>
Just put ng-model in your input text element and bind the value to it
html
<table>
<tr ng-repeat="(key,value) in prop">
<td ><label>{{key}}</label></td>
<td><input type="text" ng-model="prop[key]"></td>
</tr>
</table>
<div>
{{prop | json}}
</div>
in controller
$scope.prop = {"NAME": "", "TYPE": "forest"} ;
demo codepen
Use ng-modal for two way binding.
HTML:
<div ng-repeat="item in items">
<input ng-model="item.value" type="text" size="40">
</div>
JavaScript:
app.controller('MainCtrl', function($scope) {
$scope.items = [
{value:'First Item'},
{value: 'Second Item'}
];
$scope.addInputItem = function() {
$scope.items.push({value:''});
};
});
Working code here: http://plnkr.co/edit/KIR7AyoF553STjOx
<div ng-app="myApp" ng-controller="controller">
Name: <input ng-model="details.name">
</div>
<script>
var app = angular.module('myApp', []);
app.controller('controller', function($scope) {
$scope.details = {};
$scope.details.name = "John Doe";
});
</script>
This might i think you were asking for

AngularJS dynamic form input with fieldset via ng-repeat

Please click on the demo below
www.jsfiddle.net/rnnb32rm/1370
My problem is: "Add input" is working fine. But whenever i invoke "Add
Fields", the subsequent field will be sync with the first one. I want
the subsequent to be filled with only one input. Stuck for hours already. Please guide!
Picture to illustrate:
May be help you.
var app = angular.module("app",[]);
app.controller("MyCtrl" , function($scope){
$scope.data ={
items:[{ name:"",family:"",age:""}]
};
$scope.addRow = function(index){
var item = { name:"",family:"",age:""};
if($scope.data.items.length <= index+1){
$scope.data.items.splice(index+1,0,item);
}
};
$scope.deleteRow = function($event,item){
var index = $scope.data.items.indexOf(item);
if($event.which == 1)
$scope.data.items.splice(index,1);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MyCtrl">
<table>
<tr ng-repeat="name in data.items track by $index">
<td> <input type="text" ng-model="data.items[$index].name"></td>
<td> <input type="text" ng-model="data.items[$index].family"></td>
<td> <input type="text" ng-model="data.items[$index].age"></td>
<td> <input type="button" ng-click="addRow($index)" value="Add" ng-show="$last"></td>
<td> <input type="button" ng-click="deleteRow($event,name)" value="Delete" ng-show="$index != 0"></td>
</tr>
</table>
<span>{{data|json}}</span>
</div>
I answered this the first time you posted this question, but you deleted it.
You only have one choices array, and you are using it over and over:
$scope.addNewChoice = function() {
// ...
// Same array each time
$scope.choices.push({'id':'choice'+newItemNo});
};
<fieldset data-ng-repeat="choice in choices2">
<div data-ng-repeat="choice in choices "> <!-- Same array each time -->
You probably want one array for each entry in the choices2 array.
Also, both of your ng-repeat elements use the same variable name (choice) which is confusing.

Select All directive in AngularJS

I am using Checklist model directive with my App. I have issue, if -
I click select all button though it write up the array but its not
selecting checkbox. Same issue with Uncheck All though it empty the
model but it doesn't uncheck checkboxes.
If I select 2 or 3 randomly checkbox and click Select All Button it doesn't select All check-boxes.
Though its writing values to pushArray. But issues are checking and unchecking checkboxes.
$scope.items = [{id:1, name:"abc"},{id:2, name:"def"},{id:3, name:"ghi"}];
$scope.pushArray = [];
<table>
<tr ng-repeat="e in items">
<td class="text-right">
{{e.id}}
<input type="checkbox" checklist-change="imChanged()" checklist-model="pushArray" checklist-value="e.id" >
</td>
</tr>
</table>
I think you are pushing the complete list of object which is wrong. You just need to map the list and pass the id to the $scope
Edit: Works fine when you use $scope.pushArray as an object instead of array.
Working Plnkr
HTML
<body ng-controller="selection">
<table>
<tr ng-repeat="e in items">
<td>
<input type="checkbox" checklist-model="pushArray.ids" checklist-value="e.id"> {{e.name}}
</td>
</tr>
</table>
{{pushArray.ids | json}}
<br />
<button ng-click="select_all();">Select All</button>
<button ng-click="unselect_all();">Unselect All</button>
</body>
JS
var app = angular.module('app', ["checklist-model"]);
app.controller('selection', ['$scope', function($scope) {
$scope.items = [{
id: 1,
name: "abc"
}, {
id: 2,
name: "def"
}, {
id: 3,
name: "ghi"
}];
$scope.pushArray = { ids: []}; // Works fine when using it as an object
//$scope.pushArray = [];
$scope.select_all = function() {
$scope.pushArray.ids = $scope.items.map(function(item) { return item.id; });
};
$scope.unselect_all = function() {
$scope.pushArray.ids = [];
};
}]);
Hope it works for you!
I updated the examples on checklist-model and fix this issue. Check them out http://vitalets.github.io/checklist-model/

Angular Js two way binding

I have few text boxes and would like to implement the simple calculation like:
Value of (TextBox1 + TextBox2 + textBox3) = Value of TextBox4.
(TextBox4 + TextBox5)= value of TextBox6
How can achieve this in AngularJS?
See below for an example:
<table ng-app="" ng-controller="acadController">
<tr ng-repeat="x in semone">
<td>{{ x.SubjectName }}</td>
<td>{{ x.Credit }}</td>
<td><input type="number" ng-model="pt1" /></td>
<td><input type="number" ng-model="pt2"/></td>
<td><input type="number" ng-model="ia"/></td>
<td><input type="number" value="{{pt1+pt2+ia}}" ng-model="semMarks" class="marks_catotal" /></td>
<td><input type="number" ng-model="endSemMarks" class="marks_endsem" /></td>
<td><input type="text" class="marks_total" value="{{ semMarks + endSemMarks }}"/></td>
</tr>
<table>
You can use use $scope.watch to watch those variables and their sum and if it changes add it to $scope.marks
$scope.$watch('pt1 + pt2', function(v) {$scope.marks = v;});
See this plnkr: http://embed.plnkr.co/p0rQkUBMfEmYZkKh4Nrt/
You can use the $watch function to react to user changes :
Inside your controller, you can fire a function when a value is modified.
function calculateResult(){
$scope.semMarks = $scope.pt1 + $scope.pt2 + $scope.ia;
}
$scope.$watch('pt1', calculateResult);
$scope.$watch('pt2', calculateResult);
Instead of your watch, you could also have registered a function that will be fired directly when you modify a value :
$scope.calculateResult = calculateResult;
And then, inside your view, you would just have to call this function when an input is modified.
<td><input type="number" ng-model="pt2" ng-change="calculateResult()"/></td>
Most of the time, using an event consumes less processor than using a $watch. Angular evaluates every $watch and check if there were any changes during each $digest cycle. Angulars uses also a lot of $watches itself, so adding too much $watches can significally decrease performances.
Consider introducing a controller for endSemMarks and marks_total.
You should simply bind to a function that evaluates the sum of the values. As mentioned above, $watch is more expensive and a little harder to understand.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.tb1 = 0;
$scope.tb2 = 0;
$scope.tb3 = 0;
$scope.tb5 = 0;
$scope.sum123 = function() {
return $scope.tb1 + $scope.tb2 + $scope.tb3;
}
$scope.sum45 = function() {
return $scope.tb1 + $scope.tb2 + $scope.tb3 + $scope.tb5;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="plunker" ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div>1 :
<input type="number" ng-model="tb1">
</div>
<div>2 :
<input type="number" ng-model="tb2">
</div>
<div>3 :
<input type="number" ng-model="tb3">
</div>
<div>4 :
<input type="number" ng-value="sum123()">
</div>
<div>5 :
<input type="number" ng-model="tb5">
</div>
<div>6 :
<input type="number" ng-value="sum45()">
</div>
</body>

angular.js, can't edit dynamically created input fields

Using angular.js, I have a dynamic list of form fields I want to display to the user for editing (and later submission):
var app = angular.module('app', []);
app.controller('Ctrl', function($scope) {
$scope.fields = {
foo: "foo",
bar: "bar",
baz: "baz"
};
});
And the HTML:
<div ng-app="app" ng-controller="Ctrl">
<table>
<th>key</th>
<th>value</th>
<th>fields[key]</th>
<tr ng-repeat="(key,value) in fields">
<td>{{key}}:</td>
<td><input type="text" ng-model="value"/></td>
<td><input type="text" ng-model="fields[key]"/></td>
</tr>
</table>
</div>
See this fiddle. For a reason I don't understand, the text input boxes aren't editable. I've tried two different ways as seen above: value and fields[key]. value isn't editable at all, and fields[key] will allow one keystroke and then it blurs. What am I doing wrong? Thank you.
SET answered why it's happening, but a work-around to achieve the desired behavior would be to maintain a separate array of your keys, and run ng-repeat off those keys. I added some text fields for testing to add more properties to $scope.fields
You could use $watch to dynamically set the keys when the property count changes, if your requirements were that the field count may change.
http://jsfiddle.net/aERwc/10/
markup
<div ng-app="app" ng-controller="Ctrl">
<table>
<th>key</th>
<th>value</th>
<tr ng-repeat="key in fieldKeys">
<td>{{key}}:</td>
<td><input type="text" ng-model="fields[key]"/></td>
</tr>
</table>
<div><h6>Add a field</h6>
key: <input type="text" ng-model="keyToAdd" /><br />
value: <input type="text" ng-model="valueToAdd" />
<button ng-click="addField()">Add Field</button>
</div>
</div>
controller
var app = angular.module('app', []);
app.controller('Ctrl', function($scope) {
$scope.fields = {
foo: "foo",
bar: "bar",
baz: "baz"
};
$scope.fieldKeys = [];
$scope.setFieldKeys = function() {
var keys = [];
for (key in $scope.fields) {
keys.push(key);
}
$scope.fieldKeys = keys;
}
$scope.addField = function() {
$scope.fields[$scope.keyToAdd] = $scope.valueToAdd;
$scope.setFieldKeys();
$scope.keyToAdd = '';
$scope.valueToAdd = '';
}
$scope.setFieldKeys();
});
It is editable but after each key press your text field losing focus so that you have to click on it again to put another char.
And that happens because whole you template being re-rendered after each change in any of models. And after template re-rendered, currently there is no way to know which input should be focused. So you should create that way and you may want to write directive to hold focus on selected input.
You need to use an array of objects. Hopefully you can rework your model:
$scope.fields = [
{label: "foo", value: "foov"},
{label: "bar", value: "barv"},
{label: "baz", value: "bazv"}
];
<tr ng-repeat="field in fields">
<td>{{field.label}}:</td>
<td><input type="text" ng-model="field.value">
Fiddle.

Resources