So basically I'm creating a simple app with two controllers. ControllerA button increments ControllerB number input and vicer versa.
The problem is that $scope.total is not updating after typing into number input manually, and I don't know what would be the best way to achieve this.
HTML
<div ng-app="tabsApp">
<div id="tabOne" class="tabcontent">
<div ng-controller="tabOneController as vm">
<input type="button" value="increment value in tab 2" ng-click="vm.sumar()"/>
<input type="number" ng-model="vm.totalB.value">
</div>
</div>
<div id="tabTwo" class="tabcontent">
<div ng-controller="tabTwoController as vm">
<input type="button" value="increment value in tab 1" ng-click="vm.sumar()"/>
<input type="number" ng-model="vm.total.value">
</div>
</div>
</div>
JS
var app = angular.module('tabsApp', []);
app.controller("tabOneController", controllerA);
app.controller("tabTwoController", controllerB);
app.service('myData', function() {
var data = {
value: 0
}, dataB = {
value: 0
};
this.addItem = function (value) {
data.value = value;
}
this.getItem = function() {
return data;
}
this.addItemB = function (value) {
dataB.value = value;
}
this.getItemB = function() {
return dataB;
}
});
function controllerA(myData){
var scope = this;
scope.total = 0;
scope.sumar = function(){
scope.total++;
myData.addItem(scope.total);
}
scope.totalB = myData.getItemB();
}
function controllerB(myData){
var scope = this;
scope.totalB = 0;
scope.sumar = function(){
scope.totalB = myData
scope.totalB++;
myData.addItemB(scope.totalB);
}
scope.total = myData.getItem();
}
Here's a working example based on your code : Plunker
function controllerA(myData){
var scope = this;
scope.total = 0;
scope.sumar = function(){
scope.total = myData.getItem().value; // added this line
scope.total++;
myData.addItem(scope.total);
}
scope.totalB = myData.getItemB();
}
function controllerB(myData){
var scope = this;
scope.totalB = 0;
scope.sumar = function(){
scope.totalB = myData.getItemB().value; // modified this line
scope.totalB++;
myData.addItemB(scope.totalB);
}
scope.total = myData.getItem();
}
scope.totalB = myData.getItemB(); // first controller
scope.total = myData.getItem(); // second controller
These will be called just once when controller is loaded. Place them inside the function sumar
Use vm.total and vm.totalB instead of vm.total.value and vm.totalB.value in html
You could try implementing required ng-change="controller.functionThatIncrementsValues" in your html.
Would something like this help:
HTML
<div ng-app="tabsApp" ng-controller="tabController as vm">
<div id="tabOne" class="tabcontent">
<div>
<input type="button" ng-click="vm.one++" />
<input type="number" ng-model="vm.two">
</div>
</div>
<div id="tabTwo" class="tabcontent">
<div>
<input type="button" ng-click="vm.two++" />
<input type="number" ng-model="vm.one">
</div>
</div>
<p>Total (method 1): {{vm.one + vm.two}}</p>
<p>Total (method 2): {{ total(vm.one, vm.two) }}</p>
</div>
JS
var app = angular.module('tabsApp', []);
app.controller("tabController", function() {
this.one = 0;
this.two = 0;
this.total = function(one, two) {
return one + two;
}
})
Unless you have a specific need for two controllers and a service I would just put this all in one controller. At the moment what you have is massive overkill.
Related
I am simply trying to create some checkboxes and inputs that get the data from database and saving it back to database after edit. But I am getting the following error:
Uncaught TypeError: Unable to process binding "if: function(){return $root.editAlarmValues }"
Message: Unable to process binding "enable: function(){return $root.editAlarmValues().setAlarmValues() }"
Message: $root.editAlarmValues(...).setAlarmValues is not a function
I am not sure what I am doing wrong. I checked in the console and the values get mapped correctly to the array but they don't seem to bind to view. Any help will be highly appreciated!
Here is the code:
<!--ko if: $root.editAlarmValues -->
<div class="row">
<div class="col-md-6">
<input type="checkbox" data-bind="iCheck: $root.editAlarmValues().setAlarmValues" class="large-check"/>
</div>
</div>
<div class="row">
<div class="col-md-5 form-inline">
<input type="checkbox" data-bind="iCheck: $root.editAlarmValues().setOutputCurrentPPLowValue, enable: $root.editAlarmValues().setAlarmValues()" class="large-check"/>
<input type="text" id="OutputCurrentPPLowValue" data-bind="value: $root.editAlarmValues().outputCurrentPPLowValue, enable: $root.editAlarmValues().setOutputCurrentPPLowValue()" class="form-control" maxlength="30" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="pull-right">
<button type="button" class="btn btn-primary btn-flat" data-bind="event: {click: $root.editSave}">Save</button>
</div>
</div>
</div>
<!-- /ko -->
and script:
var AlarmsViewModel = function (wellID) {
function EditAlarms(setAlarmValues, setOutputCurrentPPLowValue, outputCurrentPPLowValue) {
var self = this;
self.setAlarmValues = ko.observable(setAlarmValues);
self.setOutputCurrentPPLowValue = ko.observable(setOutputCurrentPPLowValue);
self.outputCurrentPPLowValue = ko.observable(outputCurrentPPLowValue);
}
var self = this;
self.wellID = ko.observable(wellID);
self.editAlarmValues = ko.observableArray();
self.init = function () {
self.editAlarmInit();
};
self.editAlarmInit = function () {
APIHelper.getData("/api/alarmapi/EditAlarms?wellID=" + self.wellID(), self.editAlarmsCallback, "");
};
self.editAlarmsCallback = function (data) {
//Map results
var temp = $.map(data.result, function (item) {
return new EditAlarms(item.setAlarmValues, item.setOutputCurrentPPLowValue, item.outputCurrentPPLowValue);
});
self.editAlarmValues(temp);
};
self.editSave = function () {
var jsonData = ko.toJSON(self.editAlarmValues);
APIHelper.postData("/api/alarmapi/EditAlarmsPost", jsonData);
};
self.init();
};
var wellID = #ViewBag.WellID;
ko.bindingHandlers.iCheck = {
init: function (el, valueAccessor) {
var observable = valueAccessor();
$(el).on("ifChanged", function () {
observable(this.checked);
});
},
update: function (el, valueAccessor) {
var val = ko.utils.unwrapObservable(valueAccessor());
if (val) {
$(el).iCheck('check');
} else {
$(el).iCheck('uncheck');
}
}
};
var vm = new AlarmsViewModel(wellID);
ko.applyBindings(vm);
You don't want a <!-- ko if: editAlarmValues -->, you want a <!-- ko foreach: editAlarmValues -->. The foreach will not run when the target array is empty, so it fundamentally fulfills the same function.
Inside the foreach, the binding context is the EditAlarms object in question, so you should refer to its properties directly (iCheck: setOutputCurrentPPLowValue instead of iCheck: $root.editAlarmValues().setOutputCurrentPPLowValue).
Also think about your naming. EditAlarms is not a good name for a single object. The prefix set... should refer to a method that sets something. In this case it's just an observable property. setAlarmValues should be called alarmValues, and because it's not an array, it probably should actually be called alarmValue. And so on.
<!-- ko foreach: editAlarmValues -->
<div class="row">
<div class="col-md-6">
<input type="checkbox" data-bind="iCheck: setAlarmValues" class="large-check">
</div>
</div>
<div class="row">
<div class="col-md-5 form-inline">
<input type="checkbox" class="large-check" data-bind="
iCheck: setOutputCurrentPPLowValue,
enable: setAlarmValues
">
<input type="text" id="OutputCurrentPPLowValue" class="form-control" maxlength="30" data-bind="
value: outputCurrentPPLowValue,
enable: setOutputCurrentPPLowValue
">
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="pull-right">
<button type="button" class="btn btn-primary btn-flat" data-bind="click: $root.editSave">Save</button>
</div>
</div>
</div>
<!-- /ko -->
Revised JS code (move the binding handler definitions to the top, don't nest viewmodels)
ko.bindingHandlers.iCheck = {
init: function (el, valueAccessor) {
var observable = valueAccessor();
$(el).on("ifChanged", function () {
observable(this.checked);
});
},
update: function (el, valueAccessor) {
var val = ko.unwap(valueAccessor());
$(el).iCheck(val ? 'check' : 'uncheck');
}
};
function EditAlarms(setAlarmValues, setOutputCurrentPPLowValue, outputCurrentPPLowValue) {
this.setAlarmValues = ko.observable(setAlarmValues);
this.setOutputCurrentPPLowValue = ko.observable(setOutputCurrentPPLowValue);
this.outputCurrentPPLowValue = ko.observable(outputCurrentPPLowValue);
}
function AlarmsViewModel(wellID) {
var self = this;
self.wellID = ko.observable(wellID);
self.editAlarmValues = ko.observableArray();
self.editAlarmInit = function () {
APIHelper.getData("/api/alarmapi/EditAlarms?wellID=" + self.wellID(), function (data) {
var alarms = data.result.map(item => new EditAlarms(
item.setAlarmValues,
item.setOutputCurrentPPLowValue,
item.outputCurrentPPLowValue
));
self.editAlarmValues(alarms);
});
};
self.editSave = function () {
APIHelper.postData("/api/alarmapi/EditAlarmsPost", ko.toJSON(self.editAlarmValues));
};
self.editAlarmInit();
}
var vm = new AlarmsViewModel(#ViewBag.WellID);
ko.applyBindings(vm);
I know how to do it Without controller as:
html
Let's assume I have a directive named ngUpperCase(either true or false)
<div ng-controller="myControl" >
<input type="text" ng-upper-case="isGiant" >
</div>
Js
myApp.directive('ngUpperCase',function(){
return{
restrict:'A',
priority:0,
link:function($scope,element,attr){
//---to retrieve value
var val = $scope[attr.ngUpperCase];
var anotherVal = $scope.$eval(attr.ngUpperCase);
$scope.$watch(attr.ngUpperCase,function(val){
//---to watch
})
}
};
})
How to make the directive if I'm using something like this?
<div ng-controller="myControl as ctl" >
<input type="text" ng-upper-case="ctl.isGiant" >
</div>
Since it's not very clear what you want to achieve, here is an example of doing what I understand you need: changing an input value to upper or lower case depending on a variable:
function ngUpperCase() {
return{
restrict:'A',
priority:0,
link:function($scope,element,attr){
//---to retrieve value
var val = $scope[attr.ngUpperCase];
var anotherVal = $scope.$eval(attr.ngUpperCase);
$scope.$watch(attr.ngUpperCase,function(val){
if(val) {
element[0].value = element[0].value.toUpperCase();
} else {
element[0].value = element[0].value.toLowerCase();
}
})
}
}
}
function myController() {
this.isGiant = true;
}
angular.module('myApp', []);
angular
.module('myApp')
.controller('myController', myController)
.directive('ngUpperCase', ngUpperCase);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myController as ctl" >
lower case<br>
upper case
<input type="text" ng-upper-case="ctl.isGiant" value="TeStiNg123" >
</div>
</div>
i have 10 more ng-click events, but i want to show only clicked element value where i have to change, but i updated in code there was so many true or false duplicates i have to write, pls help me that have to show only clicked ng-show values without using 'true or false' booleen functions in each click event.
var app = angular.module('myapp', ['ngSanitize']);
app.controller('AddCtrl', function ($scope, $compile) {
$scope.field = {single: 'untitled',single2:'default',single3:'enter'};
$scope.addName1 = function (index) {
var name1html = '<fieldset id="name1" ng-click="selectName1($index)"><label ng-bind-html="field.single"></label><input type="text" placeholder="Enter name"><button ng-click="removeName1($index)">-</button></fieldset>';
var name1 = $compile(name1html)($scope);
angular.element(document.getElementById('drop')).append(name1);
};
$scope.removeName1 = function (index) {
var myEl = angular.element(document.querySelector('#name1'));
myEl.remove();
};
$scope.selectName1 = function (index) {
$scope.showName1 = true;
$scope.showName2 = false;
$scope.showName3 = false;
};
$scope.addName2 = function (index) {
var name2html = '<fieldset id="name2" ng-click="selectName2($index)"><label ng-bind-html="field.single2"></label><input type="text" placeholder="Enter name"><button ng-click="removeName2($index)">-</button></fieldset>';
var name2 = $compile(name2html)($scope);
angular.element(document.getElementById('drop')).append(name2);
};
$scope.removeName2 = function (index) {
var myEl = angular.element(document.querySelector('#name2'));
myEl.remove();
};
$scope.selectName2 = function (index) {
$scope.showName2 = true;
$scope.showName1 = false;
$scope.showName3 = false;
};
$scope.addName3 = function (index) {
var name3html = '<fieldset id="name3" ng-click="selectName3($index)"><label ng-bind-html="field.single3"></label><input type="text" placeholder="Enter name"><button ng-click="removeName3($index)">-</button></fieldset>';
var name3 = $compile(name3html)($scope);
angular.element(document.getElementById('drop')).append(name3);
};
$scope.removeName3 = function (index) {
var myEl = angular.element(document.querySelector('#name3'));
myEl.remove();
};
$scope.selectName3 = function (index) {
$scope.showName3 = true;
$scope.showName1 = false;
$scope.showName2 = false;
};
});
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.2/angular.min.js"></script>
<script src="https://code.angularjs.org/1.5.0-rc.0/angular-sanitize.min.js"></script>
</head>
<body ng-controller="AddCtrl">
<div id="drop"></div>
<button ng-click="addName1($index)">Name1</button>
<button ng-click="addName2($index)">Name2</button>
<button ng-click="addName3($index)">Name3</button>
<form ng-show="showName1">
<div class="form-group">
<label>Field Label(?)</label>
<br/>
<input ng-model="field.single">
</div>
</form>
<form ng-show="showName2">
<div class="form-group">
<label>Field Label(?)</label>
<br/>
<input ng-model="field.single2">
</div>
</form>
<form ng-show="showName3">
<div class="form-group">
<label>Field Label(?)</label>
<br/>
<input ng-model="field.single3">
</div>
</form>
</body>
</html>
here is plunkr http://plnkr.co/edit/oFytWlQMIaCaeakHNk71?p=preview
You will need "ng-repeat" in the HTML. Set an Array on $scope and let the template determine what HTML elements to add. Typically, $index is only set by ng-repeat.
Read more here: https://docs.angularjs.org/api/ng/directive/ngRepeat
http://plnkr.co/edit/NDTgTaTO1xT7bLS1FALN?p=preview
<button ng-click="addRow()">add row</button>
<div ng-repeat="row in rows">
<input type="text" placeholder="name"><input type="tel" placeholder="tel">
</div>
I want to push new row and save all the fields but now I'm stuck at adding new rows. How to know the current number of row and do increment to push into the array?
Look at this example I created which allows you to generate up to eight unique input fields for Telephone and Text Entries.
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function($scope) {
$scope.rows = [];
var Row = function(tel, text) {
// Private data
var private = {
tel: tel,
text: text
}
// Expose public API
return {
get: function( prop ) {
if ( private.hasOwnProperty( prop ) ) {
return private[ prop ];
}
}
}
};
$scope.addRow = function(){
if($scope.rows.length < 8){
var newItemNum = $scope.rows.length + 1;
var row = new Row('item' + newItemNum, 'item' + newItemNum);
$scope.rows.push(row);
}
};
$scope.saveAll = function(){
// $scope.result = 'something';
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyCtrl">
<h2>Setting</h2>
<button ng-click="addRow()">Add Row</button>
<br />
<div ng-repeat="row in rows">
<input type="text" placeholder="Text" ng-model="row.textModel" >
<input type="tel" placeholder="Phone" ng-model="row.telModel" >
</div>
<br />
{{rows}}
</div>
</div>
Move functions inside controller 'Ctrl'.
In your script:
function Ctrl($scope) {
$scope.result = "something";
$scope.rows = ['1'];
$scope.addRow = function(){
if ($scope.rows.length < 8) {
$scope.rows.push($scope.rows.length + 1);
}
}
$scope.saveAll = function(){
// $scope.result = 'something';
}
}
My page has a form to add objects to an array. The array is displayed on the page with links to edit the array item.
When I add items I attach a primary key to be able to edit that item later in case it is deleted and its array index is changed.
The add functionality is working but the edit behavior is not. When I update the ng-model to which the form controls are bound the form does not display the record to be edited. It might be a $scope issue but I declared the model in the parent $scope specifically to achieve this.
Here is a plunker:
http://plnkr.co/edit/yDlPpFunxFLHPiI0kCdj?p=preview
<form ng-controller="formCtrl" novalidate ng-submit="submit()">
<input type="text" name="name" ng-model="student.name" placeholder="name">
<input type="number" name="age" ng-model="student.age" placeholder="age">
<input type="hidden" ng-value="pk">
<input type="submit">
</form>
<h1>students</h1>
<ul>
<li ng-repeat="item in students">{{item.name}} - {{item.age}}</li>
</ul>
var myApp = angular.module('myApp',[]);
myApp.controller("myCtrl", ['$scope', function($scope ){
$scope.student = {};
$scope.pk = 1;
$scope.index = 0;
$scope.students = [];
$scope.editStudent = function (id) {
for (var i = 0; i < $scope.students.length; i++) {
console.log("comparing "+$scope.students[i].pk+ " & " + id);
if ($scope.students[i].pk == id ) {
console.log("editing pk nr.: "+ id);
$scope.student = {
name: $scope.students[i].name,
age: $scope.students[i].age
};
$scope.index = id;
}
}
};
}]);
myApp.controller("formCtrl", ['$scope', function($scope) {
$scope.submit = function () {
if ($scope.index === 0) {
$scope.students.push({
name: $scope.student.name,
age: $scope.student.age,
pk: $scope.pk
});
$scope.pk++;
$scope.student = {};
} else {
for (var i = 0; i < $scope.students.length; i++) {
if ($scope.students[i].pk == $scope.index) {
$scope.students[i] = {
name: $scope.student.name,
age: $scope.student.age,
pk: $scope.index
};
}
}
$scope.index = 0;
}
};
}]);
Thanks
I have edited your plunkr, see the changes here.
The changes i made were:
Removed the form controller, no need for 2 controllers in your app.
Used the $index property that is available when using ng-repeat to edit the item you click.
Here is your HTML:
<div ng-app="myApp" ng-controller="myCtrl as ct">
<form novalidate>
<input type="text" name="name" ng-model="newStudent.name" placeholder="name">
<input type="number" name="age" ng-model="newStudent.age" placeholder="age">
<input type="hidden" ng-value="pk">
<input type="button" value="submit" ng-click="submit()">
</form>
<h1>students</h1>
<ul>
<li ng-repeat="item in students">{{item.name}} - {{item.age}}</li>
</ul>
</div>
And your JavaScript File:
var myApp = angular.module('myApp',[]);
myApp.controller("myCtrl", ['$scope', function($scope ){
$scope.newStudent = {};
$scope.students = [];
$scope.index = -1;
$scope.editStudent = function (index) {
console.log('Editing student: ' + $scope.students[index].name);
$scope.index = index;
$scope.newStudent = angular.copy($scope.students[index]);
};
$scope.submit = function () {
if ($scope.index < 0) {
$scope.students.push($scope.newStudent);
} else {
$scope.students[$scope.index] = $scope.newStudent;
}
$scope.newStudent = {
name: '',
age: ''
}
$scope.index = -1;
};
}]);
Edit:
I just modified the code for when editing the student, to use angular.copy so that when you are editing, it loses the binding to the $scope.students array, and the changes are only applied when you click on the submit button.