I've been reading a lot on angular scopes and inheritance but I can't get my head around this problem. Here is the HTML I'm using:
<div class="sensorquery-sensor" ng-repeat="sensor in query.sensors" ng-controller="SensorsCtrl">
<select class="form-control"
ng-model="selected.sensor"
ng-options="sensor.name for sensor in parameters.sensors">
</select>
<select class="form-control"
ng-model="selected.definition"
ng-options="definition.value for definition in definitions">
</select>
<select class="form-control"
ng-model="selected.operation"
ng-options="operation for operation in operations">
</select>
</div>
As you can see, I have an ng-repeat based on query.sensors. The values stored in this query.sensors array should be simple:
{
name: 'sensor1',
type: 'temperature'
}
But I want to use a child controller: SensorsCtrl to handle more logic per sensor and hide the complexitiy of sensors. A sensor can look like:
{
name: 'sensor1',
attributes: [
'model',
'brand'
],
definitions: [
{
datatype: 'double',
value: 'temperature'
},
{
datatype: 'integer',
value: 'pressure'
},
{
datatype: 'string',
value: 'color'
}
]
}
So it's in my SensorsCtrl controller where I want to put the selection logic:
$scope.$watch('selected.sensor', function(sensor) {
$scope.definitions = sensor.template.definition;
});
$scope.$watch('selected.definition', function(definition) {
if (definition.datatype === 'string') {
$scope.operations = ['Count'];
} else {
$scope.operations = ['Max', 'Min'];
}
$scope.selected.operation = _.first($scope.operations);
});
How do I keep the link with the parent query.sensors[$index] while transforming the sensor as the user selects different sensors and definitions?
Setting up a watcher on selected and updating the query.sensors array triggers an infinite $digest loop.
I found the solution which was right before my eyes:
<div class="sensorquery-sensor" ng-repeat="sensor in query.sensors" ng-controller="SensorsCtrl">
<!-- ... -->
</div>
The sensor is a reference to the original object of the parent query.sensors. An it's created in the scope of the sub-controller.
So in my SensorsCtrl controller, I can just watch:
$scope.$watch('sensor.definition', function(definition) {
/* ... */
});
So I can put hide some complexity in this controller while maintaining a proper link to the original element.
It does not answer the question of maintaining a less complex object but it's a different question I guess.
Related
I've really hit a brick wall with this, and I know I'm probably missing something here, but I'm stuck and need help. What I'm trying to do is use a service to populate the options in an ng-options directive; however, the ng-options are inside of a custom directive, and I've tried everything from track by, to testing it outside of the directive, inside the directive, etc. Can someone please take a look at this code and see if you can spot what I'm missing? Any help is greatly appreciated. It WILL work as far as executing the update to the ng-model; however, at page landing and record selection, it will not initially select the proper option, but if I take the track by out, it will initialize with the proper selection, it just won't update ng-model when/if I do that.
angular
.module('app')
.controller('mainCtrl', ['acctList', 'CONSTANTS', 'FORMFIELDS', function(acctList, CONSTANTS, FORMFIELDS) {
var mainCtrl = this;
mainCtrl.form = {};
mainCtrl.formFields = FORMFIELDS;
mainCtrl.currentRecord = null;
mainCtrl.editedRecord = {};
mainCtrl.setCurrentRecord = function(value) {
mainCtrl.currentRecord = value;
mainCtrl.editedRecord = angular.copy(mainCtrl.currentRecord);
};
mainCtrl.statuses = CONSTANTS.statuses;
}])
.value('FORMFIELDS', [
{
key: 'active_flag',
inputtype: 'select',
type: 'text',
class: 'form-control',
id: 'activeFl',
name: 'activeFl',
placeholder: 'Active Flag',
required: true,
maxlength: 1,
disabled: false,
labelfor: 'inputActiveFl',
labeltext: 'Active Flag',
field: 'mainCtrl.editedRecord.ACTIVE_FL',
options: 'list as list.desc for list in mainCtrl.statuses track by list.value'
}
])
.value('CONSTANTS',
{
statuses: [
{
id: 1,
value: "Y",
desc: "Active"
},
{
id: 2,
value: "N",
desc: "Inactive"
}
]
}
)
.directive('formTemplate', ['$compile', function($compile) {
function linker(scope, element, attr) {
scope.$watch(attr.modeltemp, function(modeltemp) {
// if ngModel already equals modeltemp or modeltemp doesn't exist, return
if (attr.ngModel == modeltemp || !modeltemp) return;
// remove all attributes to prevent duplication
element.removeAttr('placeholder');
element.removeAttr('type');
element.removeAttr('class');
element.removeAttr('id');
element.removeAttr('name');
element.removeAttr('ng-required');
element.removeAttr('maxlength');
element.removeAttr('ng-disabled');
// add the ng-model attribute presently tied to modeltemp
element.attr('ng-model', modeltemp);
// if modeltemp is blank, then remove ng-model, as it would be null
if (modeltemp == '') {
element.removeAttr('ng-model');
}
// Unbind all previous event handlers, this is
// necessary to remove previously linked models.
element.off();
// run a compile on the element, injecting scope, to reconstruct the element
$compile(element)(scope);
});
console.log(scope.acctCtrl);
}
// dynamic templating function associated with the templateUrl in the DDO
function template (tElement, tAttrs) {
// set the type variable equal to the value from the tAttr for 'inputtype' coming from the view
var type = tAttrs['inputtype'];
// just declaring the return variable for cleanliness
var tpl;
// begin the switch-case statement for each inputtype, then set it's return variable equal to the respective url
switch(type) {
case 'input':
tpl = '/common/directives/formTemplate/formTemplate.template.html';
break;
case 'select':
tpl = '/common/directives/formTemplate/formTemplateSelect.template.html';
break;
default:
tpl = '/common/directives/formTemplate/formTemplate.template.html';
break;
}
return tpl;
}
return {
restrict: 'EA',
replace: true,
templateUrl: template,
link: linker
};
}])
<form class="form-horizontal" ng-submit="submit()" name="mainCtrl.form.newAcctForm">
<div class="col-lg-6 form-fields" ng-repeat="fields in mainCtrl.formFields" ng-class="{ 'has-error': mainCtrl.form.newAcctForm.{{fields.name}}.$dirty }">
<label class="control-label" for="{{fields.labelfor}}">{{fields.labeltext}}</label>
<div form-template modeltemp="fields.field" inputtype="{{fields.inputtype}}"></div>
</div>
</form>
<select class="{{fields.class}}" id="{{fields.id}}" name="{{fields.name}}" ng-options="{{fields.options}}" ng-required="{{fields.required}}" maxlength="{{fields.maxlength}}" ng-disabled="{{fields.disabled}}">
<option value="">Please select...</option>
</select>
While this does work, did you consider using lifecycle hooks instead, waiting until after the view has loaded/initialized? Your solution works, but it's a bit like using a rocket launcher on an ant hill.
I am totally new to Bootstrap and angularjs world and I am stuck at this.
I have a simple select like this below
<div class="pull-left margin-left-20">
<select id="ddlPageSize" class="form-control form-ddl-adj"
ng-model="params.settings().countSelected"
ng-options="item.value as item.text for item in params.settings().countOptions"
ng-change="params.count(params.settings().countSelected)"></select>
</div>
In the controller, I have select values like this
var pageSizeList = [
{ value: 5, text: "5" },
{ value: 10, text: "10" },
{ value: 25, text: "25" },
{ value: 50, text: "50" },
{ value: 100, text: "100" }
];
5 is the option by default. Can some one tell me how to save user selection (say 10) in a cookie so that next time 10 should be the pagesize
You can use ngCookies module of AngularJS. You'll have to include angular-cookies.js in your code: https://code.angularjs.org/1.4.3/angular-cookies.min.js
Include ngCookies in your module declaration:
angular.module('testApp', ['ngCookies'])
Then you can save a cookie as:
$cookies.put('someKey', 'someVal');
You can retrieve a cookie as:
$cookies.get('someKey')
Updated:
Check this fiddle: http://jsfiddle.net/3xyf1bzo/
You can do this with ng-cookies
dropdown selection code html :
<div class="custom-param" ng-controller="themectrl">
<label id="default-lang" class="label">Default Theme:</label>
<select ng-change="themeChange(theme)" ng-model="theme" ng-options="theme.url as theme.name for theme in themes">
</select>
</div>
angularJS code :
var app = angular.module('themectrl', ['pascalprecht.translate','ngCookies'] );
app.controller('themeCtrl', function( $scope , $cookies ) {
$scope.theme = 'theme1.value';
// create the list of bootswatches
$scope.themes = [
{ name: 'Theme 1', url: 'theme1.value' },
{ name: 'Theme 2', url: 'theme2.value' }
];
// on dropdown change function
$scope.themeChange = function($selectedTheme) {
$cookies.put('ppcdfavtheme', $selectedTheme);
$scope.theme = $selectedTheme;
}
});
You can use Persistence Using local storage to save your data if you want I leave a link with working example Good luckAngularJS: Real Time Model Persistence Using Local Storage
I have these objects right here that I will use to save data from a form, and later send it to an api as JSON :
$scope.price = {}
$scope.item = {"price":$scope.price, };
I also have these field which will be used to dynamically generate inputs on a html page:
$scope.fields = [
{
name: $scope.item.title,
title: 'Title',
type: {
view: 'input'
}
},
{
name: $scope.price.regular,
title: 'Regualar Price',
type: {
view: 'input'
}
}
];
Now in order to generate the form I use this code:
<div class="form-group" ng-repeat="field in fields">
<label>{{ field.title }}:</label>
<span ng-switch on="field.type.view">
<span ng-switch-when="input">
<input
ng-model=field.name
type="text"
/>
</span>
</span>
</div>
And with this code, it is not assigning the values in the input to the objects. Is there a way to do it? I know I can do it this way:
ng-model="item[field.name]"
But that limits me to only one level of the object. I want to be able to bind nested objects. And I just can't seem to figure it out. Thank You!
I have a select box which is populated with some data from my controller. When an input value changes the contents of the select box should be filtered and a default value should be assigned based on the is default property of the data object.
Is there any way this can be done using angular directives or would it need to be done as a custom filter function doing something along the lines of
angular.forEach(vm.data,function(item){
if (vm.q == item.someId && item.isDefault) {
vm.result = item.value;
}
});
My html looks something like
<div ng-app="myApp" ng-controller="ctrl as vm">
<input type="text" ng-model="vm.q">
<select ng-options="item.value as item.description for item in vm.data | filter:{someId:vm.q}" ng-model="vm.result"></select>
</div>
and my controller looks like:
(function(){
angular.module('myApp',[]);
angular
.module('myApp')
.controller('ctrl',ctrl);
function ctrl()
{
var vm = this;
vm.data = [
{
someId: '1',
description: 'test1',
value: 100,
isDefault: true
},
{
someId: '2',
description: 'test2',
value: 200,
isDefault: false
},
{
someId: '3',
description: 'test3',
value: 100,
isDefault: true
},
];
}
})();
See my plunkr demo here: http://plnkr.co/edit/RDhQWQcHFMQJvwOyHI4r?p=preview
Desired behaviour:
1) Enter 1 into text box
2) List should be filtered to 2 items
3) Select box should pre-select item 1 based on property isDefault set to true
Thanks in advance
I'd suggest you include some 3rd party library, like lodash, into your project to make working with arrays/collections that much easier.
After that you could add ng-change directive for your input.
<input type="text" ng-model="vm.q" ng-change="vm.onChange(vm.q)">
And the actual onChange function in the controller
vm.onChange = function(id) {
var item = _.findWhere(vm.data, { someId: id, isDefault: true });
vm.result = item ? item.value : null;
};
And there you have it.
I have a drop down list that is bound to a SelectedFormat value. The lists options are populated from external source on load and matches the view models data.Format object base on id.
Take a look at the js fiddle
Can anyone tell me why the model updates but the UI is not updating with the correct Format.Name
Thanks.
HTML:
<div data-bind="text:data.Format.Name"></div>
<select data-bind="
options:Controls,
optionsText: 'Name',
value: data.SelectedFormat"></select>
Model:
var jsonData = {
Id: "abc-123",
Name: "Chicken Cheese",
Format: {
Id: 2,
Name: 'Medium',
Other: 'Bar'
}
};
var self = this;
self = ko.mapping.fromJS(data);
self.SelectedFormat = ko.observable(
//return the first match based on id
$.grep(vm.Controls,function(item){
return item.Id === self.Format.Id();
})[0]
);
//when changed update the actual object that will be sent back to server
self.SelectedFormat.subscribe(function (d) {
this.Format = d;
},self);
In your code, you have Format and SelectedFormat. The former isn't an observable and so can't trigger updates. You have to use SelectedFormat instead.
<div data-bind="text:data.SelectedFormat().Name"></div>
Example: http://jsfiddle.net/QrvJN/9/