Angularjs ng-if not working with ng-model - angularjs

I am using angularjs to integrate my api.
I am facing problem with using ng-if inside textbox.
so below is my snippet of HTML code:
<input type="text" value="" data-ng-if="edit" ng-model="name">
<input type="text" value="" data-ng-if="!edit" ng-model="singleAppDetails.name">
Here edit variable is there in scope
that is in my controller i have declared it like this:
$scope.edit = false;
So if edit is false it should get bind with ng-model="name"
and if edit is true it should get bind with ng-model="singleAppDetails.name"
But it is not binding it as expected.
Further I am using $http.post to post the data to server like below:
$scope.addApp = function(){
$scope.apps = [];
$scope.apps.push({'name':$scope.name, 'domain':$scope.domain, 'appId':$scope.appId, 'secret':$scope.secret});
// Writing it to the server
//
var dataObj = {
name : $scope.name,
domain : $scope.domain,
appId : $scope.appId,
secret : $scope.secret
};
var res = $http.post('http://192.168.1.30:8090/apps/', dataObj);
res.success(function(data, status, headers, config) {
$scope.message = data;
});
res.error(function(data, status, headers, config) {
alert( "failure message: " + JSON.stringify({data: data}));
});
// Making the fields empty
//
$scope.name='';
$scope.domain='';
$scope.appId = '';
$scope.secret = '';
};
But this always sends null data.

ng-if has its own scope. So the name attribute that is populated by the first input is in the ng-if scope instead of being in your controller scope.
The second input should work fine, provided that your controller initializes singleAppDetails to a non-null object:
$scope.singleAppDetails = {};
Rule of thumb: always have a dot in your ng-model. Always populate objects in the scope rather than the scope itself.

ng-if is creating a child scope because that the input elements does not see the scope variable defined in the controller you have two ways to solve this problem
use an object reference
ex :
$scope.user = { name : "" }
inside the template
<input type="text" ng-model='user.name' />
you can tell angular to look for the variable in parent scope instead child scope
<input type="text" ng-model='$parent.name' />

Related

Angular 1 dynamic form object

I'm using Angular 1 and creating a dynamic form. It works by looping through some objects and rendering dynamically binded input fields like:
<div class="quest-form form-group" ng-repeat="task in tasks">
<div ng-if="task.Class == 'TaskText'" ng-class="'task ' + task.Class">
<input ng-model="questForm.Task[task.ID].Value" ng-name="task_{{task.ID}}" ng-required="task.Required == 1" type="text" class="form-control" placeholder="{{task.Title}}" />
</div>
...
...
</div>
I also have a upload field in the loop:
<div ng-if="task.Class == 'TaskUpload'" ng-class="'task ' + task.Class">
<input class="btn btn-primary upload-btn" ngf-max-size="10MB" type="file" ng-model="upload" ngf-multiple="false" ngf-select="uploadFile(upload, task.ID, $invalidFiles)" />
<input class="" ng-model="questForm.Task[task.ID].FileUploadID" ng-required="task.Required == 1" ng-name="task_{{task.ID}}" type="text" />
</div>
When the file uploaded event is called I'm trying to set the value of the hidden field which is ng-model="questForm.Task[task.ID].FileUploadID" like this:
$scope.uploadFile = function(file,taskID) {
file.upload = Upload.upload({
url: assetsURL+'/home/UploadFile',
data: {file: file}
});
file.upload.then(function (response) {
$scope.questForm.Task[taskID].FileUploadID = response.data; // THIS MESSES UP
}, function (response) {
...
});
};
I get the following error, it's like $scope.questForm.Task[128] does not exist even though the hidden input looks correct and is binded to the $scope.questForm.Task[128].
angular.js:14362 TypeError: Cannot read property '128' of undefined
at file.upload.then.$scope.errorMsg (http://localhost/carl-mygps-app/js/controllers/quest-details-controller.js:120:26)
at processQueue (http://localhost/carl-mygps-app/bower_components/angular/angular.js:16689:37)
at http://localhost/carl-mygps-app/bower_components/angular/angular.js:16733:27
at Scope.$eval (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18017:28)
at Scope.$digest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:17827:31)
at Scope.$apply (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18125:24)
at done (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12233:47)
at completeRequest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12459:7)
at XMLHttpRequest.requestLoaded (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12387:9) Possibly unhandled rejection: {}
I have tried defining blank objects in the scope like:
$scope.questForm = [];
$scope.questForm.Task = {};
But I should not need to because they are created in the template? confused. Thanks all.
Actually nope. Having your template compiled does not mean all the ng-models are initialized. While ng-model is smart enough to create all the intermediate objects, if they don't exist, it doesn't do so until $viewValue is changed. In your case if you upload a file without editing any other input first, $viewValue for inputs has never changed, and thus you have to initialize questForm, questForm.Task, and questForm.Task[taksID] yourself.
if (!$scope.questForm) {
$scope.questForm = {};
}
if (!$scope.questForm.Task) {
$scope.questForm.Task = {};
}
if (!$scope.questForm.Task[taskID]) {
$scope.questForm.Task[taskID] = {};
}
$scope.questForm.Task[taskID].FileUploadID = response.data;
Or you can initialize questForm and questForm.Task at the beginning. And only check if questForm.Task[taskID] exists before initializing it.

Angular Nested Controller Data access

I don't know how to access the data from a nested ( child ) controller.
<form ng-controller="TestController as test" ng-submit="submit()">
<h5>Employee name :</h5>
<div ng-controller="mainCtrl" class="row-fluid">
<form class="row-fluid">
<div class="container-fluid">
<input type="text" ng-model="name" typeahead="name for name in names | filter:$viewValue" />
</div>
</form>
</div>
<h5>Comment : </h5>
<textarea ng-model="test.test_content"></textarea>
<input type="submit" id="submit" value="Submit" />
</form>
As you can see i have 2 controllers.The main one is a form , the second one is an input box that allows the user to search the name in a list using typeahead.
I want to be able to acces the content of ng-model="name" in my child controller (mainctrl) in my TestController.
I've tried to access it directly with $scope.name but it doesn't work.
I've also tried test.name as the ng-model and it didn't work either.
I'm sending the data of my TestController to my server and would like to send the data ( name ) from my mainCtrl aswell, directly from TestController. So that when my user click submit it send both the name + the test_content in $http.post request.
Anyone know how to do that ?
Thanks
I've found this but it didn't really help.. https://fdietz.github.io/recipes-with-angular-js/controllers/sharing-models-between-nested-controllers.html
edit:
my search controller
.controller("mainCtrl", function ($scope, $http) {
$scope.selected = '';
$http.get('/feedbacks/search.json').
then(function(response) {
$scope.succes = " ok "
$scope.names = response.data;
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.succes = " error"
});
});
my form controller :
angular.module('FeedbackTool')
.controller('TestController',['$scope', '$http', function($scope,$http) {
$http.get('/feedbacks.json').
then(function(response) {
$scope.succes = " ok "
$scope.list = response.data;
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.succes = " error"
});
$scope.submit = function() {
$http.post('/feedbacks.json', { data:this.test }).
then(function(response) {
$scope.succes = 'sent';
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.succes = 'fail';
});
};
}]);
The parent scope is accessible from within the child scope, but the child scope is not accessible from the parent. Typically the way to deal with this is to assign values from the child scope to properties already defined in the parent. In your case I think you just need to use test.name as the ng-model expression.

Angular - Have input save to variables in factory to be used elsewhere

I'm creating a single page application and need to be able to display input from one template onto another. The way I have my factory currently set up is not working. Can someone help with this?
Factory:
angular.module('BSG').factory('airplaneFactory', function() {
var name = '';
var last_name = '';
var color = '';
function setData(n, ln, c) {
name = n;
last_name = ln;
color = c;
}
return {
name: name,
last_name: last_name,
color: color,
setData: setData
}
})
Controller:
angular.module('BSG').controller('AirplaneCtrl', ['$scope', 'airplaneFactory', function($scope, airplaneFactory) {
'use strict';
function updateFactory() {
airplaneFactory.setData($scope.name, $scope.last_name, $scope.color);
}
}]);
Snippet from template where input is gathered:
<h2>Who is this story about?</h2>
<p><input type="text" placeholder="name" ng-model="name" ng-change="updateFactory()"></p>
<h2>What is {{ name }}'s last name?</h2>
<p><input type="text" placeholder="last name" ng-model="last_name" ng-change="updateFactory()"></p>
It doesn't work because there is no two-way binding in $scope.name = airplaneFactory.name;. That simply writes the factory property value into a scope variable.
To save data in your factory, you can create a setter function:
angular.module('BSG').factory('airplaneFactory', function() {
var name = '';
var last_name = '';
var color = '';
function setData(n, ln, c) {
name = n;
last_name = ln;
color = c;
}
return {
name: name,
last_name: last_name,
color: color,
setData: setData
}
})
And since the services are singletons, the data will remain the same on different controllers.
So to set the data from a controller, you'd do something like this:
airplaneFactory.setData('John', 'Doe', 'red');
To have the factory updates happen automatically, you should set $watchers on the controller scope properties or use onchange listeners on your input elements. For example:
<h2>Who is this story about?</h2>
<p><input type="text" placeholder="name" ng-model="name" ng-change="updateFactory()"></p>
<h2>What is {{ name }}'s last name?</h2>
<p><input type="text" placeholder="last name" ng-model="last_name" ng-change="updateFactory()"></p>
And then in the controller you'd have the updateFactory function which will call the factory setData method:
function updateFactory() {
airplaneFactory.setData($scope.name, $scope.last_name, $scope.color);
}
UPDATE
Here is a Plunker where everything is working, I've added a button to fetch the latest data from the factory, so you can test it: http://plnkr.co/edit/JQd22tEYSPuJwutCLH1B?p=preview
Don't forget that all ng-* attributes are working with scope properties, so:
function updateFactory() {...
won't be enough, since you need to have a scope property with that name:
$scope.updateFactory = updateFactory;
Or simply define the whole function like I did in the Plunker example:
$scope.updateFactory = function() {... etc
Use rootScope to access name, last_name and color in every view and controller (inject the $rootScoope...

Send values of ng-model to angular controller

I have four different inputs and want to send value of changed input to controller without pressing send button or whatever.
How i got it all inputs should to have next syntaxis.
<input ng-model="borderRadius" ng-change="change()">
<input ng-model="background" ng-change="change()">
Or not?
I want to get them in my app.js
control.controller('generatorOptions', function ($scope) {
$scope.buttonStyle = {
"border-radius" : " -- here is value of it -- ",
"background" : " -- here is value of it -- "
};
});
Update: That is working just fine, but how can i optimise code? https://github.com/tanotify/Button-style-generator/blob/master/public/assets/scripts.min.js
First you need to define in your controller:
control.controller('generatorOptions', function ($scope) {
$scope.buttonStyle={};
$scope.change = function() {
console.log("borderRadius value:"+$scope.buttonStyle.borderRadius);
console.log("background value:"+$scope.buttonStyle.background);
};
});
This is your view:
<input ng-model="buttonStyle.borderRadius" ng-change="change()">
<input ng-model="buttonStyle.background" ng-change="change()">
It means, that first you need define object(buttonStyle) in you controller, than this object is accesseble in you view and in view you will define new properties(radius and background) of your object which you can access in controller by object.

datalist is not getting populated in AngularJS

I have following code written in AngularJS (simple HTML and its controller) and I mapped them in my state file
main.html
<input class="serialSearch" data-ng-placeholder="Serial #" data-ng-model ="serialNum" list="suggestion"/>
<datalist id="suggestion">
<option data-ng-repeat="suggest in sugesstions.values"> {{suggest.title}}</option>
</datalist>
mainController
$scope.sugesstions = {};
var queryString= {"querystring" : ""+"q=name:"+$scope.serialNum+"*&qt=autocomplete-model&hl=true&hl.fl=name","$skip" : "0","$top" : "75"};
searchResource.getList(queryString).then(
function(data) {
$scope.sugesstions = data.items;
}
);
When I enter anything in my searchBox is calls the resource and returns me data, but in this case searchResource.getList is never getting called when there is anychange in searchBox content. WHY?
If you are changing textbox content and are expecting that the querying mechanism is fired again you need to attach ng-change event directive
<input class="serialSearch" data-ng-placeholder="Serial #" data-ng-model ="serialNum" list="suggestion" ng-change='search()'/>
In controller
$scope.search=function() {
var queryString= {"querystring" : ""+"q=name:"+$scope.serialNum+"*&qt=autocomplete- model&hl=true&hl.fl=name","$skip" : "0","$top" : "75"};
searchResource.getList(queryString).then(
function(data) {
$scope.sugesstions = data.items;
}
);
}

Resources