I'm trying to create a more than average complex form with custom actions on some controls. The main controller of the view is holding the model that will be saved at the end. Inside this main controller, I have a separate controller for each input control that has some specific actions.
Here is a short example and the question is, if I want to implement the UserChoiceCtrl.selectLastUser function, how can I do without using $scope ?
More generally, how can I access to a model in the main controller in a child controller ? It's easy in the view, but how can I do in the controller code ?
#Plunker if you prefer
angular.module('myApp', []);
angular.module('myApp')
.controller('TaskCtrl', [
function() {
var viewModel = this;
// This is injected in controller in real life
viewModel.users = [
{login: 'Tom', password: '123'},
{login: 'Stanley', password: '123'},
{login: 'Joe', password: '123'},
{login: 'Katy', password: '123'},
{login: 'Kate', password: '123'},
{login: 'Tony', password: '123'}
];
viewModel.task = {
user: viewModel.users[0],
description: ''
};
viewModel.save = function() {
alert(angular.toJson(viewModel.task));
};
}
]);
angular.module('myApp')
.controller('UserChoiceCtrl', [
function() {
var viewModel = this;
viewModel.selectLastUser = function() {
// No way to access the task variable # TaskCtrl ?
// The following line is working but using $scope
// $scope.taskCtrl.task.user = $scope.taskCtrl.users[5];
};
}
]);
body {
font-family: 'Arial';
}
label,
input,
textarea {
display: block;
}
label {
margin-top: 8px;
}
<!DOCTYPE html>
<html data-ng-app="myApp">
<head>
<script data-require="angular.js#1.4.0" data-semver="1.4.0" src="https://code.angularjs.org/1.4.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body data-ng-controller="TaskCtrl as taskCtrl">
<h1>New task</h1>
<form name="taskCtrl.taskForm">
<label>Description</label>
<textarea data-ng-model="taskCtrl.task.description"></textarea>
<div data-ng-controller="UserChoiceCtrl as userChoiceCtrl">
<label>User</label>
<select data-ng-model="taskCtrl.task.user" data-ng-options="user as user.login for user in taskCtrl.users"></select>
<button data-ng-click="userChoiceCtrl.selectLastUser()">Last user of list</button>
</div>
<br>
<button data-ng-click="taskCtrl.save()">Save</button>
</form>
</body>
</html>
Try this
<button data-ng-click="userChoiceCtrl.selectLastUser(taskCtrl.task.user)">Last user of list</button>
viewModel.selectLastUser = function(user) {
alert(user);
// No way to access the task variable # TaskCtrl ?
// The following line is working but using $scope
// $scope.taskCtrl.task.user = $scope.taskCtrl.users[5];
};
A way could be creating the main controller as a service and injecting it to the children:
http://fdietz.github.io/recipes-with-angular-js/controllers/sharing-code-between-controllers-using-services.html
See if this serves the purpose,
basically i am getting an instance of the task controller using the $controller service.
angular.module('myApp')
.controller('UserChoiceCtrl', function ($controller) {
var task = $controller('TaskCtrl');
console.log("Task", task);
var viewModel = this;
viewModel.selectLastUser = function () {
// No way to access the task variable # TaskCtrl ?
// The following line is working but using $scope
// $scope.taskCtrl.task.user = $scope.taskCtrl.users[5];
task.task.user = task.users[5];
alert ( task.task.user.login);
};
console.log("User", viewModel)
});
Related
I have googled this a lot, and can't figure out what is wrong in my code. My ng-model is not updating on selecting an option from the select box.
<div ng-controller="UserRegistrationController as userReg">
<select name="selectOrganisation" ng-required="true" ng-model="userReg.candidateData.BusinessUnit">
<option>Select Organisation</option>
<option>ABC</option>
<option>XYZ</option>
<option>KLM</option>
</select>
</div>
Controller:
(function (window) {
'use strict';
angular.module('myApp.userRegistration.controllers')
.controller('UserRegistrationController', ['UserRegistrationService', '$scope', function (UserRegistrationService, $scope) {
var vm = this;
vm.candidateData = {
FirstName: '',
LastName: '',
Email: '',
PhoneNumber: '',
JobId: '',
PrimarySkills: '',
SecondarySkills: '',
BusinessUnit: '',
CreatedBy: 'SPAN54',
CreatedOn: new Date(),
resume: '',
OfferStatus: 1,
Remarks: ''
};
}]);
})(window);
On console.log(vm.candidateData.BusinessUnit) inside my post function(not visible here), I see " ".
UPDATE 1: The " is there in my code. I just overlooked it while pasting it here. Have updated above code now. But my code is still not working.
UPDATE 2: Finally, I figured the issue. I am using Materialize.css in my app, and my select element is getting wrapped up in a div and ul-li. So when user selects a value, he is actually modifying the ul-li, not the select element. But I am not able to figure out how I can capture the selected value like this. Any ideas? Thanks.
UPDATE 3: Added a fiddle.
Tried the code you posted. And its working for me. I added Working Code Snippet below.
Did these changes to make it work. If its still not working for you, could you share a working snippet.
Add double quote to end the ng-controller atrribute.<div ng-controller="UserRegistrationController as userReg>
Removed UserRegistrationService declaration from controller as its not defined or shared.
var app = angular.module('sample', []);
app.controller('UserRegistrationController', ['$scope', function($scope) {
var vm = this;
vm.candidateData = {
FirstName: '',
LastName: '',
Email: '',
PhoneNumber: '',
JobId: '',
PrimarySkills: '',
SecondarySkills: '',
BusinessUnit: '',
CreatedBy: 'SPAN54',
CreatedOn: new Date(),
resume: '',
OfferStatus: 1,
Remarks: ''
};
$scope.change = function() {
console.log(vm.candidateData.BusinessUnit);
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="sample">
<div ng-controller="UserRegistrationController as userReg">
<select ng-change="change()" name="selectOrganisation" ng-required="true" ng-model="userReg.candidateData.BusinessUnit">
<option>Select Organisation</option>
<option>ABC</option>
<option>XYZ</option>
<option>KLM</option>
</select>
</div>
</body>
Here is your Working code JS FIDDLE
You missed out the ' " ' in ng-controller
<div ng-controller="UserRegistrationController as userReg">
I am having issues finding out why the filter is undefined...
Uncaught ReferenceError: filter is not defined(anonymous function) # mainController.js:46(anonymous function) # mainController.js:76
What am I missing?
Index =
<!DOCTYPE html>
<html ng-app="app" ng-open="main.refreshData()">
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="Content/site.css">
<!--<link data-require="ui-grid#*" data-semver="3.0.0RC18" rel="stylesheet" type="text/css" href="Content/ui-grid.css">-->
<link data-require="ui-grid#*" data-semver="3.0.0RC18" rel="stylesheet" href="http://ui-grid.info/release/ui-grid-unstable.css" />
<script src="Scripts/jquery-2.1.4.min.js"></script>
<script src="Scripts/angular.min.js"></script>
<script src="Application/app.module.js"></script>
<script src="Application/mainController.js"></script>
<script src="Scripts/ui-grid.min.js"></script>
</head>
<body ng-controller="MainController as main">
<input type="text" ng-model="main.food" placeholder="Enter food" />
<p>Sriracha sauce is great with {{main.food}}!</p>
<br />
<br />
<input type="text" ng-model="main.filterText" ng-change="refreshData()" placeholder="Search..." />
<br />
<br />
<h>{{main.title}}</h>
<div class="grid" ui-grid='main.gridOptions' id="grid1"></div>
</body>
</html>
app =
//The app.module.js file houses a single application-level module for your application.
//In this example, your application has an application-level module that loads the
//other modules of the application. The purpose of adding app.module.js as a separate
//file is to introduce the concept that modules, controllers, services, directives,
//views, etc. should be defined in their own files.
//<<Immediately-invoked function expression>>
//Immediately-invoked function expressions can be used to avoid variable hoisting from
//within blocks, protect against polluting the global environment and simultaneously
//allow public access to methods while retaining privacy for variables defined within the function.
(function () {
'use strict';
angular.module('app', []);
})();
Controller =
(function () {
'use strict';
angular
.module('app', ['ui.grid'])
.controller('MainController', main);
function main() {
var self = this;
self.food = 'pizza';
self.myData = [{
name: "Moroni",
age: 50
}, {
name: "Tiancum",
age: 43
}, {
name: "Jacob",
age: 27
}, {
name: "Nephi",
age: 29
}, {
name: "Enos",
age: 34
}];
self.gridOptions = {
data: "main.myData",
enableGridMenu: true
};
self.title = "ng-grid Example";
self.filterText;
self.refreshData = function () {
self.gridOptions.data = self.filter('filter')(self.myData, self.filterText, undefined);
};
}
//Define a custom filter to search only visible columns (used with grid 3)
filter('visibleColumns', function () {
return function (data, grid, query) {
matches = [];
//no filter defined so bail
if (query === undefined || query === '') {
return data;
}
query = query.toLowerCase();
//loop through data items and visible fields searching for match
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < grid.columnDefs.length; j++) {
var dataItem = data[i];
var fieldName = grid.columnDefs[j]['field'];
//as soon as search term is found, add to match and move to next dataItem
if (dataItem[fieldName].toString().toLowerCase().indexOf(query) > -1) {
matches.push(dataItem);
break;
}
}
}
return matches;
}
});
})();
Translate your filter to function and bind it to application.
angular
.module('app', ['ui.grid'])
.controller('MainController', main)
.filter('visibleColumns', visibleColumns);
function visibleColumns () {...}
There is no function filter(), there is method of angular.module Object that called filter('name', function() { ... }).
More about custom filters
I have a model, which will be related to a number of other models. Think of a stack overflow question, for example, where it is a question related to tags. The final Object might look as follows before a POST or a PUT:
{
id: 28329332,
title: "checkboxes that append to a model in Angular.js",
tags: [{
id: 5678,
name: "angularjs"
}, {
id: 890,
name: "JavaScript"
}]
}
So far, I have the following controller:
.controller('CreateQuestionCtrl',
function($scope, $location, Question, Tag) {
$scope.question = new Question();
$scope.page = 1;
$scope.getTags = function() {
Tag.query({ page: $scope.page }, function(data) {
$scope.tags = data;
}, function(err) {
// to do, error when they try to use a page that doesn't exist
})
};
$scope.create = function() {
$scope.question.$save(function(data) {
$location.path("/question/" + data.id);
});
};
$scope.$watch($scope.page, $scope.getTags);
}
)
So I display all of the tags, paginated, on the page. I want them to be able to select the given tags and append it to my model so that it can be saved.
How can I create a checkbox interface where it updates the $scope.question with the selected other models?
EDIT: think I might be part of the way there
<div class="checkbox" ng-repeat="tag in tags.objects">
<label><input
type="checkbox"
ng-change="setTag(tag.id)"
ng-model="tag"
> {{ tag.name }}
</div>
Then on the controller
$scope.setTag = function(id) {
Tag.get({id: id}, function(data) {
// don't know what now
})
}
Basically, it takes a directive to approach your goal Take a look at the plunker I wrote for you. As you can see, in the list of selected tags the text property of each tag is displayed, it means that the object structure is kept. In your case, you would bind the $scope.question.tags array as the collection attribute and each tag from the $scope.tags as the element attribute.
Here a codepen for multiple check-boxes bound to the same model.
HTML
<html ng-app="codePen" >
<head>
<meta charset="utf-8">
<title>AngularJS Multiple Checkboxes</title>
</head>
<body>
<div ng:controller="MainCtrl">
<label ng-repeat="tag in model.tags">
<input type="checkbox" ng-model="tag.enabled" ng-change="onChecked()"> {{tag.name}}
</label>
<p>tags: {{model.tags}}</p>
<p> checkCount: {{counter}} </p>
</body>
</html>
JS
var app = angular.module('codePen', []);
app.controller('MainCtrl', function($scope){
$scope.model = { id: 28329332,
title: "checkboxes that append to a model in Angular.js",
tags: [{
id: 5678,
name: "angularjs",
enabled: false
}, {
id: 890,
name: "JavaScript",
enabled: true
}]
};
$scope.counter = 0;
$scope.onChecked = function (){
$scope.counter++;
};
});
I found a great library called checklist-model worth mentioning if anyone is looking up this question. All I had to do was this, more or less:
<div class="checkbox" ng-repeat="tag in tags">
<label>
<input type="checkbox" checklist-model="question.tags" checklist-value="tags"> {{ tag.name }}
</label>
</div>
Found this on googling "directives for angular checkbox".
I am using angularFire and trying to save data from a form to firebase with $add. Any help would be greatly appreciated. The console logs all work, I am able to retrieve the data in the console. Sorry for all of the code... I wanted to be sure I provided all the material needed.
app.js:
var creativeBillingApp = angular.module('creativeBillingApp', ['ngRoute', 'firebase']);
creativeBillingApp.constant('FIREBASE_URI', "https://XXXX.firebaseIO.com/");
creativeBillingApp.controller('MainCtrl', ['$scope', 'groupsService', function( $scope, groupsService, $firebase ) {
console.log('Works')
$scope.newGroup = {
name: '',
status: ''
};
$scope.addGroup = function(newGroup){
console.log(newGroup);
groupsService.addGroup();
$scope.newGroup = {
name: '',
status: ''
};
};
$scope.updateGroup = function (id) {
groupsService.updateGroup(id);
};
$scope.removeGroup = function(id) {
groupsService.removeGroup(id);
};
}]);
creativeBillingApp.factory('groupsService', ['$firebase', 'FIREBASE_URI',
function ($firebase, FIREBASE_URI) {
'use strict';
var ref = new Firebase(FIREBASE_URI);
return $firebase(ref).$asArray();
var groups = $firebase(ref).$asArray();
var getGroups = function(){
return groups;
};
var addGroup = function (newGroup) {
console.log(newGroup)
groups.$add(newGroup);
};
var updateGroup = function (id){
groups.$save(id);
};
var removeGroup = function (id) {
groups.$remove(id);
};
return {
getGroups: getGroups,
addGroup: addGroup,
updateGroup: updateGroup,
removeGroup: removeGroup,
}
}]);
index.html:
<form role="form" ng-submit="addGroup(newGroup)">
<div class="form-group">
<label for="groupName">Group Name</label>
<input type="text" class="form-control" id="groupName" ng-model="newGroup.name">
</div>
<div class="form-group">
<label for="groupStatus">Group Status</label>
<select class="form-control" ng-model="newGroup.status">
<option value="inactive">Inactive</option>
<option value="active">Active</option>
</select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
This is the error I am getting:
TypeError: undefined is not a function
at Scope.$scope.addGroup (http://localhost:9000/scripts/app.js:35:19)
and the app.js line 35 is in reference to groupsService.addGroup(); from the app.js code given above.
Firstly, you are returning in your service after you create your $FirebaseArray. You are also creating another $FirebaseArray after that.
return $firebase(ref).$asArray();
Remove that return statement. That is causing your service to return early and none of the attached methods will apply to your service.
In groupService.addGroup() you are calling push, which is not a function on $asArray. You need to call .$add(). The newGroup argument is also not being passed in the controller.
The $.push method is available on the base of the $firebase binding. When you using a $FirebaseArray the $add method pushes a new record into Firebase.
See the docs for more info.
Plunker Demo
var addGroup = function (newGroup) {
console.log(newGroup)
groups.$add(newGroup);
};
Then in your controller you can simply call:
$scope.addGroup = function(newGroup){
groupsService.addGroup(newGroup);
$scope.newGroup = {
name: '',
status: ''
};
};
Hi I have been getting investing alot of time in learning Knockout and have come to a point where I have to many properties in my application and I am in need to use the mapping pluggin.
It seems easy enought how it should be used but I mussed be missing something because it does not work.I have created a test example.This is my code:
function vm() {
var self = this;
this.viewModel = {};
this.getData = function() {
$.getJSON('/api/Values/Get').then(data)
.fail(error);
function data(ajaxData) {
console.log(ajaxData);
self.viewModel = ko.mapping.fromJS(ajaxData);
console.log(self.viewModel);
}
function error(jError) {
console.log(jError);
}
};
};
ko.applyBindings(new vm());
This is my html:
<ul data-bind="foreach: viewModel">
<li data-bind="text:FirstName"></li>
<input type="text" data-bind="value: FirstName"/>
</ul>
<button data-bind="click : getData">Press me!</button>
My ajax call succesfully retrieves this data from the server:
[
{
FirstName: "Madalina",
LastName: "Ciobotaru",
hobies: [
"games",
"programming",
"hoby"
]
},
{
FirstName: "Alexandru",
LastName: "Nistor",
hobies: [
"games",
"programming",
"movies"
]
}
]
It seems that after data function is called viewModel get's converted into an array but with no items in it.
What am I doing wrong?
I have taken your expected server data and created a jsfiddle here. You needed to change the viewModel property to be an observable array, and change the way the mapping is performed.
Here is a version of your script that will work:
function vm() {
var self = this;
this.viewModel = ko.observableArray([]);
this.getData = function() {
$.getJSON('/api/Values/Get').then(data)
.fail(error);
function data(ajaxData) {
console.log(ajaxData);
ko.mapping.fromJS(ajaxData, {}, self.viewModel);
console.log(self.viewModel);
}
function error(jError) {
console.log(jError);
}
};
};
ko.applyBindings(new vm());