What is wrong in this binding? - angularjs

I have an angular ui-grid in which data is displayed and in every row there is an "Edit" and "Delete" button. When the user clicks on the "Edit" then a modal window is displayed and the fields are populated and the user can edit the data. This is the user scenario I have. When the user clicks on the "Edit" button I pass through the id value and when angular creates the modal window I query the data from the server.
As you can see in the html the controller is not bound because it is managed by another controller. If it would be bound here then it would be called twice.
The fields are populated properly and when I change something in it and I want to save it then breeze says that nothing has changed in the entity and the log in the save method gives back the original values of the entity.
I assume something is wrong with the data binding but I don't know what.
According to the pluralsight video and breeze's documentation breeze tracks the changes in the entity.
What I'm doing wrong?
The form:
<form class="form-horizontal" name="editModuleModalForm">
<legend>Edit module</legend>
<div class="control-group">
<label class="control-label">Module name</label>
<div class="controls">
<input type="text"
placeholder="Module name here..."
data-ng-model="vm.sysmodule.name"
data-z-validate />
</div>
</div>
<div class="control-group">
<label class="control-label">Module Sort number</label>
<div class="controls">
<input type="text"
placeholder="Module sort number"
data-ng-model="vm.sysmodule.sortNo"
data-z-validate />
</div>
</div>
<div class="control-group">
<label class="control-label">Route</label>
<div class="controls">
<input type="text"
placeholder="Module route comes here..."
data-ng-model="vm.sysmodule.route"
value="vm.sysmodule.route"
data-z-validate />
</div>
</div>
<div class="control-group">
<button type="submit" class="btn btn-success" data-ng-click="vm.save()">Save</button>
<button type="submit" class="btn btn-danger" data-ng-click="vm.cancel()">Cancel</button>
</div>
</form>
Angular controller for the form
(function () {
'use strict';
var controllerId = 'editModuleController';
angular
.module('dilibApp')
.controller(controllerId, ['$scope', '$modalInstance', 'selectedModuleId', 'common', 'datacontext', editModuleController]);
function editModuleController($scope, $modalInstance, selectedModuleId, common, datacontext) {
/* jshint validthis:true */
var vm = this;
vm.title = 'editModule';
vm.sysmodule = undefined;
vm.cancel = cancel;
vm.save = save;
activate();
function activate() {
onDestroy();
common.activateController([getModulePropertiesToBeEdited()], controllerId);
}
function getModulePropertiesToBeEdited() {
return datacontext.sysmodule.getById(selectedModuleId)
.then(function (result) {
vm.sysmodule = result[0];
});
}
function onDestroy() {
$scope.$on('$destroy', function () {
datacontext.cancel();
});
}
function cancel() {
$modalInstance.dismiss('cancel');
}
function save() {
console.log(vm.sysmodule);
if (datacontext.hasChanges) {
datacontext.saveChanges();
console.log('Changes are saved!');
} else {
console.log('There are no changes to be saved!');
}
$modalInstance.close();
}
}
})();

Nothing is wrong with the binding. The root if the problem is that the datacontext doesn't have hasChanges method due to that the breeze's entityManager is wrapped in it. I had to rework the code a little bit, rethinking the responsibilities and it is working now.

Related

AngularJS error: TypeError: v2.login is not a function

I would like to call the login function when I click the login button but keep getting the error message in the title. Can someone point out the error in my script?
login.js code below:
/*global Firebase, angular, console*/
'use strict';
// Create a new app with the AngularFire module
var app = angular.module("runsheetApp");
app.controller("AuthCtrl", function ($scope, $firebaseAuth) {
var ref = new Firebase("https://xxxxx.firebaseio.com");
function login() {
ref.authWithPassword({
email : "xxxxx",
password : "xxxx"
}, function (error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
And the code for login.html is also below:
<div class="container" style="max-width: 300px">
<form class="form-signin">
<h2 class="form-signin-heading" style="text-align: center">Please Sign In</h2>
<input type="text" class="form-control" name="username" ng-model = "username" placeholder="Email Address" required="" autofocus="" />
</br>
<input type="password" class="form-control" name="password" ng-model = "password" placeholder="Password" required=""/>
</br>
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="login()">Login</button>
</form>
</div>
Edge case here, but I want to mention it for posterities' sake. I got this same error when using the controllerAs pattern with a form name with the same value as ng-submit. For example:
<form name="authCtrl.signUp" ng-submit="authCtrl.signUp()">
Throws: TypeError: v2.signUp is not a function
The solution was to change the name of the form to something different:
<form name="authCtrl.signUpForm" ng-submit="authCtrl.signUp()">
In my case, I was having an exact same issue as yours. However, coming across gkalpak's answer to such a scenario helped me out.
Turned out to be what I was calling was addBuddy() function, from a form named "addBuddy". The solution was to change the name of either of the two things to make one stand out or differentiable from the other. I changed the name of the form to "addBuddyForm" and voila! My function worked!
Here's a snippet of my case:
<form name="addBuddy" class="form-horizontal" novalidate>
...
<button class="btn btn-sm btn-info" ng-click="addBuddy()>Submit</button>
Which, I changed to:
<form name="addBuddyForm" class="form-horizontal" novalidate>
...
<button class="btn btn-sm btn-info" ng-click="addBuddy()>Submit</button>
...and it worked! :)
In AngularJS call the function from view it must be in the $scope.
JS
// exposes login function in scope
$scope.login = login;
HTML
<div class="container" ng-controller="AuthCtrl" style="max-width: 300px"> <!-- I notice here for include ng-controller to your main div -->
<form class="form-signin">
<h2 class="form-signin-heading" style="text-align: center">Please Sign In</h2>
<input type="text" class="form-control" name="username" ng-model = "username" placeholder="Email Address" required="" autofocus="" />
</br>
<input type="password" class="form-control" name="password" ng-model = "password" placeholder="Password" required=""/>
</br>
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="login()">Login</button>
</form>
This may not be specific to your problem, but I was also getting this error and it took a bit to figure out why.
I had named both a function and a variable the same, with the variable assigned in the function, and so the assignment of the variable was overriding the function and it was exploding on a second run.
You'll notice in the example the uploadFile() function as an upload.uploadFile = true; This was a wonderful file that was meant to be upload.uploadingFile - a flag used to control the behavior of a spinner. Once that was fixed, the issue went away.
Example:
(function()
{
'use strict';
angular.module('aumApp.file-upload')
.controller('FileUploadCtrl', FileUploadCtrl);
function FileUploadCtrl($scope, $http)
{
upload.uploadFile = function()
{
upload.uploadFile = true;
var backendUrl = '/ua_aumcore/events/api/v1/events/uploadFile';
var fd = new FormData();
fd.append('file', upload.src);
$http({ url: backendUrl, data: fd, method: 'POST', transformRequest : angular.identity, headers: { 'Content-Type' : undefined } })
.then(function uploadSuccess(response)
{
upload.data = response.data;
upload.message = "Uploaded Succesfully.";
upload.uploadSuccess = true;
upload.uploadingFile = false;
},
function uploadFailure(response)
{
upload.message = "Upload Failed.";
upload.uploadSuccess = false;
upload.uploadingFile = false;
});
};
}
FileUploadCtrl.$inject = ['$scope', '$http'];
})();
To be callable from the view, a function must be in the $scope. Add
$scope.login = login;
to the JS code of the controller.
You also need to actually use that controller. Change
<div class="container" style="max-width: 300px">
to
<div ng-controller="AuthCtrl" class="container" style="max-width: 300px">
This is all fundamental stuff. My advice would be to learn from an AngularJS tutorial before going further.
Two enable two-way binding you have to assign your login function to $scope. Replace your code for function with this:
$scope.login=function() {
ref.authWithPassword({
email : "nick.koulias#gmail.com",
password : "Jaeger01"
}, function (error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});
}
It may be a late answer by me.
But It working for me
Check form name you set
e.g. ng-form="login"
and function name
e.g. ng-click="login()"
Then it will not work . You have to change one of them.
e.g. ng-form="loginForm"
Explanation:
AngularJS 1.x registers any form DOM element that has a name property in $scope via formDirectiveFactory. This directive automatically instantiates form.FormController if the above is true:
If the name attribute is specified, the form controller is published onto the current scope under
from: angular.js:24855
Hence if you have a <form name=myForm> it will override your $scope.myForm = function() { ... }

Select some data and then persist to next controller/view in Angularjs

I am bringing in some simple data via a service that uses angular-resource like so:
angular.module('InvoiceService',
['ngResource'])
.factory('InvoiceService', function ($resource) {
return $resource('data.json');
})
.controller("DashboardListCtrl", function (InvoiceService) {
var vm = this;
InvoiceService.query(function (data) {
vm.invoices = data;
});
vm.submit = function (form) {
console.log(form)
};
});
And the html:
<form name="invoices" role="form" novalidate>
<ul>
<li ng-repeat="invoice in vm.invoices">
<input type="checkbox" id="{{'id-' + $index}}" />
<p><strong>Order:</strong></p>
<p>{{invoice.order}}</p>
</li>
<input type="submit" value="Continue" ng-click="vm.submit(invoices)" />
</ul>
</form>
Everything works fine; the data is displays in the view as expected.
The question:
What I'd like to do is be able to select a checkbox, grab the bit of data associated with that checkbox, and pass it along to the next controller/view on submit. How can I do this?
So, what do I do next? Am I on the right track?
**EDIT: added all angular code to help clarify
Posting answer as reply too big to be useful.
You should be using $scope to isolate the controller's data from the rest of the page.
Read up about ng-model http://docs.angularjs.org/api/ng/directive/ngModel and how to use it to two-way-bind checkbox value to a controller variable. No need to use theFormName if you call $scope.submit = function() { } as your ng-model variable will be available in $scope already.
angular.module('InvoiceService',
['ngResource'])
.factory('InvoiceService', function ($resource) {
return $resource('data.json');
})
.controller("DashboardListCtrl", function ($scope, InvoiceService) {
InvoiceService.query(function (data) {
$scope.invoices = data;
});
$scope.submit = function () {
// FIXME to access a property of each $scope.invoices
console.log('checkbox1=' + $scope.invoices[0].checkbox1);
};
});
Then the HTML:
<form role="form" novalidate ng-controller="DashboardListCtrl"><!-- EDIT: added ng-controller=, remove name= -->
<ul>
<li ng-repeat="invoice in invoices"><!-- EDIT: remove 'vm.' -->
<input type="checkbox" id="{{'id-' + $index}}" ng-model="invoice.checkbox1" /><!-- EDIT: added ng-model= -->
<p><strong>Order:</strong></p>
<p>{{invoice.order}}</p>
</li>
<input type="submit" value="Continue" ng-click="submit()" /><!-- EDIT: remove 'vm.' -->
</ul>
</form>

angular view not updating when scope changes in controller (after form submit)

I'm very new to angular so I may be going about this all wrong but here goes. I have a form
<form name="search_form" novalidate ng-submit="searchForm(search_form.$valid)" >
<div class="maincontainer">
<div class="formcontainer">What to eat?</div>
<div class="formcontainer"><input type="text" name="food_type" ng-model="food_type" placeholder="Enter a search term" required></div>
<div class="formcontainer">Where to look?</div>
<div class="formcontainer"> <input type="text" name="cityname" ng-model="trader.cityname" value="cityname" googleplace="" placeholder="Enter a location" required>
</div>
<div class="formcontainer">
<button type="submit" class="btn-main2" >Submit</button>
</div>
</form>
that when I submit I want to grab the results based on the location I get from google and display them in a new view
myControllers.controller('SearchCtrl',['$scope','Search','$location', function ($scope,Search,$location) {
$scope.setSearchLocation = function(place){
$scope.lat = place.geometry.location.lat();
$scope.lng = place.geometry.location.lng();
}
$scope.searchForm = function() {
// check to make sure the form is valid
if (!$scope.search_form.$valid) {
alert('Please fill out all fields');
}
else{
$scope.results = Search.do_search($scope.lat,$scope.lng);
$location.path('search-results');
}
};
}])
.directive('googleplace', function() {
return {
require : 'ngModel',
link : function(scope, element, attrs, model) {
var options = {
types : [],
};
scope.gPlace = new google.maps.places.Autocomplete(element[0],options);
google.maps.event.addListener(scope.gPlace, 'place_changed',function() {
var place = scope.gPlace.getPlace();
scope.setSearchLocation(place);
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
},
};
});
everything works as expected except the view does not update in the results view. If I set the $scope.results out side the searchForm() function everything renders properly. I realize this is because it exists before the page renders, just saying that part works.
when I try $scope.$apply() it says already in progress
<div id="results-container" ng-repeat="result in results">
<div id="picbox"><img src="../images/test.jpg" alt="" "/></div>
<div id="addressinfo">
<h4>John's Restaurant </h4>
<p>123 York Street, Toronto ON <br>
<span id="type">#
Burgers, #Poutine</span></p>
</div>
<div id="location">4.2m<br>
<img src="../images/heart.png" width="86" height="76" alt=""/><br>
</div>
</div>
</div>
When you call $location.path(...), $scope object of controller is always initialized.
My suggestion is ...
write the element of div#results-container on the same template where form[name=search_form] exists.
remove $location.path('search-results');
I hope this could help you.

Cannot read property $valid of undefined

I have a form like this -
<form name="myForm" novalidate>
There are some fields in the form which I am validating and then submitting the form like this -
<input type="button" ng-click="Save(data)" value="Save">
In the controller, I want to check if the form is not valid then Save() should show some error on the page. For that, I am setting up a watch like this -
$scope.$watch('myForm.$valid', function(validity) {
if(validity == false)
// show errors
});
But I am always getting this error on running it -
Cannot read property '$valid' of undefined
Can someone explain why?
Thanks
You just misspelled "myForm" in your controller code. In order to remove the error, Write "myform" instead of "myForm".
However I expect what you want is like this.
$scope.Save = function(data){
alert($scope.myform.$valid);
}
I setup jsfiddle.
In my case I was wrapping the form in a modal created in the controller and therefore got the same error. I fixed it with:
HTML
<form name="form.editAddress" ng-submit="save()">
<div class="form-group">
<label for="street">Street</label>
<input name="street" type="text" class="form-control" id="street" placeholder="Street..." ng-model="Address.Street" required ng-minlength="2" />
<div class="error" ng-show="form.editAddress.street.$invalid">
<!-- errors... -->
</div>
</div>
<button type="submit" class="btn btn-primary" >Save address</button>
</form>
JS
angular.module("app").controller("addressController", function ($scope, $uibModal, service) {
$scope.Address = {};
$scope.form = {};
$scope.save = function() {
if (modalInstance !== null) {
if (isValidForm()) {
modalInstance.close($scope.Address);
}
}
};
var isValidForm = function () {
return $scope.form.editAddress.$valid;
}
});

Adding hidden form field to array in Angular

I am trying to add a "hidden" field to a basic form in Angular (using Firebase as the backend). I'm having trouble figuring out how to include this field as part of the array when the form is submitted. I want to include {type: 'Basic'} as part of the array. I've looked at the other related posts on this site, but am still unsure how to apply to my particular situation.
Any suggestions on how to do this?
Javascript:
myApp.controller('NewProjectCtrl', function ($location, Projects) {
var editProject = this;
editProject.type = 'Basic'; //this is the hidden field
editProject.save = function () {
Projects.$add(editProject.project).then(function(data) {
$location.path('/');
});
};
});
HTML:
<form>
<div class="control-group form-group">
<label>Name</label>
<input type="text" name="name" ng-model="editProject.project.name">
</div>
<label>Description</label>
<textarea name="description" class="form-control" ng-model="editProject.project.description"></textarea>
<button ng-click="editProject.save()" class="btn btn-primary">Save</button>
</form>
You don't need a hidden form field, just submit your value in your controller like this:
editProject.save = function () {
editProject.project.type = 'Basic';
Projects.$add(editProject.project).then(function(data) {
$location.path('/');
});
};
All attributes of your editProject.project will be submitted, as you may notice in the developer console.
I would structure the controller a bit different.. here is an example (I am considering you are using angular-resource, where Projects returns a Resource?):
myApp.controller('NewProjectCtrl', function ($location, Projects) {
$scope.project = new Projects({type: 'Basic'});
$scope.save = function () {
$scope.project.$save().then(function(data) {
$location.path('/');
});
};
});
<form ng-submit="save()">
<div class="control-group form-group">
<label>Name</label>
<input type="text" name="name" ng-model="project.name">
</div>
<label>Description</label>
<textarea name="description" class="form-control" ng-model="project.description"></textarea>
<input type="submit" value="Save" class="btn btn-primary" />
</form>
The save function will $save the new project resource (this is an default method and will make a POST on the given resource URL).

Resources