AngularJS: dynamic custom directive with multiple templates - angularjs

I'm a Java developer working on a side-project. I've decided to use AngularJS to consume my RESTful webservice.
My JSON structure is following:
[
{
"generatorName": "name1",
"constructorParams": [
{
"type": "Boolean",
"value": "true",
},
{
"type": "Integer",
"value": "2",
}
]
},
{
"generatorName": "name2",
"constructorParams": [
{
"type": "Integer",
"value": "10",
},
{
"type": "Integer",
"value": "10",
}
]
}
]
My goal is to display a specific (for ex. number, text etc.) input field based on the "type" of constructor param and initialise it with a "value". So, if the first generator was selected, I'd like to have something like this:
<html>
<select>
<option selected="selected" value="true">Yes</option>
<option value="false">No</option>
</select>
<input type="number" value="2">
<html>
I've read some threads and decided to use custom directive inside my loop:
<p ng-repeat="param in selectedGenerator.constructorParams">
<constructor-param param="{{param}}"></constructor-param>
</p>
Here's my custom directive:
app.directive("constructorParam", function() {
return {
scope : {
param : '#'
},
templateUrl : '/html_templates/constructor_param_template.html'
};
});
And here's template:
<div ng-switch="{{param.type}}">
<div ng-switch-when="Integer">
<input type="number" ng-attr-name="{{param.type}}" min="0" ng-attr-value="{{param.value}}">
</div>
<div ng-switch-when="Boolean">
<select ng-if="{{param.value}}" ng-attr-name="{{param.type}}">
<option selected="selected" value="true">Yes</option>
<option value="false">No</option>
</select>
<select ng-if="!{{param.value}}" ng-attr-name="{{param.type}}">
<option value="true">Yes</option>
<option selected="selected" value="false">No</option>
</select>
</div>
<div ng-switch-when="String">
<input type="text" ng-attr-name="{{param.type}}" ng-attr-value="{{param.value}}">
</div>
<div ng-switch-when="Double">
<input type="number" ng-attr-name="{{param.type}}" step="0.01" min="0" ng-attr-value="{{param.value}}">
</div>
</div>
These don't work. I can see in the Chrome developer's tools that the directive is run, but it doesn't provide any visible output. My questions are:
1) Do I pass the param object correctly in the custom directive element?
2) I'm not sure about the scope of the directive - I've also tried param : '=param' - it doesn't work either...
3) How should I read the passed object's properties in the template? I've tried: value="{{param.type}}", value={{param.type}} and ng-attr-value="{{param.value}}". None works, but there could be completely different cause to that...
4) Can I use prefix "ng-attr-" for all such element's HTML attributes as name and value?
5) My template's code is exactly what I've pasted - do I need to make it a valid HTML structure with head, body etc.? Do I have to attach <script> with AngularJS? I've done that, but once again, no change.
6) The usage scenario for the whole story is to choose a concrete generator from a drop-down list and display it's constructor params in the specified way. So it has to regenerate HTML with a generator's change. I assume that it's done in the ng-repeat loop but please confirm that.
Thank you very, very much for your input! :)

When you do param : '#' that's a "text binding". That's useful in case you want to tell Angular to not interpret what you're binding to your attribute as a property on the scope but rather as a string directly.
So if you'd do
<my-custom-directive param="hello"></my-custom-directive>
Then in your directive, param would be equal to the the string "hello". While if you bind to param using two-way binding param : '=', then Angular would look for the hello property on the scope instead of taking it as a string literal.
In your case, when you do param="{{param}}" Angular is first unpacking "param" by looking at the scope, into a string literal, then it creates the binding. So even though this might have worked if param was a string, in your case it's an Object, so I don't think it will play well. So I would just do a direct binding to it (see my final code below).
In your directive template, you're also using a few Angular directive which expect a scope binding, so I would try to bind without the curly brackets. See my example code below.
app.directive("constructorParam", function() {
return {
scope: {
param: '='
},
templateUrl: '/html_templates/constructor_param_template.html'
};
});
<!-- container -->
<p ng-repeat="param in selectedGenerator.constructorParams">
<constructor-param param="{{param}}"></constructor-param>
</p>
<!-- TEMPLATE -->
<div ng-switch="param.type">
<div ng-switch-when="Integer">
<input type="number" ng-attr-name="param.type" min="0" ng-attr-value="param.value">
</div>
<div ng-switch-when="Boolean">
<select ng-if="param.value" ng-attr-name="param.type">
<option selected="selected" value="true">Yes</option>
<option value="false">No</option>
</select>
<select ng-if="!param.value" ng-attr-name="param.type">
<option value="true">Yes</option>
<option selected="selected" value="false">No</option>
</select>
</div>
<div ng-switch-when="String">
<input type="text" ng-attr-name="param.type" ng-attr-value="param.value">
</div>
<div ng-switch-when="Double">
<input type="number" ng-attr-name="param.type" step="0.01" min="0" ng-attr-value="param.value">
</div>
</div>
If it still doesn't work, let me know so I can try it in a CodePen.

1) No, you need to remove the curly brackets when passing the values to directive
<constructor-param param="param"></constructor-param>
2,3) in directive use = instead of #. Because # take string value of the parameter and = take the parameter value
scope: {
param: '='
},
4) if you need to bind data then use ng-model instead of ng-attr
5) You don't need to add html or body tag inside constructor_param_template.html template.
6) ng-repeat does that. once the array get updated it will dynamically update the DOM
Demo

First of all, thank you both guys for your help! Unfortunately, I can't mark any of the given answer as correct, because I have to mixed them in order to get what I was looking for.
I wouldn't elaborate here, let me just share with you the final code:
<!-- container -->
<p ng-repeat="param in selectedGenerator.constructorParams">
<constructor-param param="param"></constructor-param>
</p>
<!-- template -->
<div ng-switch="param.type">
<div ng-switch-when="Integer">
<input type="number" name={{param.type}} min="0" value={{param.value}}>
</div>
<div ng-switch-when="Boolean">
<select ng-if="param.value" name={{param.type}}>
<option selected="selected" value="true">Yes</option>
<option value="false">No</option>
</select>
<select ng-if="!param.value" name={{param.type}}>
<option value="true">Yes</option>
<option selected="selected" value="false">No</option>
</select>
</div>
<div ng-switch-when="String">
<input type="text" name={{param.type}} value={{param.value}}>
</div>
<div ng-switch-when="Double">
<input type="number" name={{param.type}} step="0.01" min="0" value={{param.value}}>
</div>
</div>
And here's the directive, you both get it right:
app.directive("constructorParam", function() {
return {
scope: {
param: '='
},
templateUrl: '/html_templates/constructor_param_template.html'
};
});
Once again - thank you very much, I wouldn't solve this problem so quickly without your help!

Related

How to set angularjs ng-repeat on select option

At the moment I have a select, but i need to have custom styling for my options, that requires me to change my setup a bit.
At the moment I have
<select ng-model="vm.selectedStation"
ng-options="s.stationName for s in vm.nearbyStations" .....>
</select>
Now I am changing it to
<select ng-model="vm.selectedStation">
<option ng-repeat="s in vm.nearbyStations" value="{{s}}">
{{s.stationName}}
</option>
</select>
It visibly shows the same, but the value is different. I require ng-model to be s. to be the object, but instead it shows as the s.stationName May I ask how do I set the value properly
Use the ng-value directive:
<select ng-model="vm.selectedStation">
̶<̶o̶p̶t̶i̶o̶n̶ ̶n̶g̶-̶r̶e̶p̶e̶a̶t̶=̶"̶s̶ ̶i̶n̶ ̶v̶m̶.̶n̶e̶a̶r̶b̶y̶S̶t̶a̶t̶i̶o̶n̶s̶"̶ ̶v̶a̶l̶u̶e̶=̶"̶{̶{̶s̶}̶}̶"̶>̶
<option ng-repeat="s in vm.nearbyStations" ng-value="s">
{{s.stationName}}
</option>
</select>
Interpolation with curly brackets {{ }} converts the expression to a string. The ng-value directive evaluates the expression and retains its type.
For more information, see
AngularJS ng-value Directive API Reference
AngularJS <select> - Using ngValue to bind the model to an array of objects
Simply use ng-value on each <option> to assign directly to each constituent of vm.nearbyStations.
See an example usage below.
(As an aside, your HTML appears to have an unpaired </option>.)
angular
.module('app', [])
.controller('ctrl', function () {
this.nearbyStations = [
{ stationName: 'a' },
{ stationName: 'b' },
{ stationName: 'c' },
];
});
<script src="https://unpkg.com/angular#1.7.9/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl as vm">
<select ng-model="vm.selectedStation">
<option ng-repeat="s in vm.nearbyStations" ng-value="s">{{ s.stationName }}</option>
</select>
<pre>selectedStation = {{ vm.selectedStation }}</pre>
</div>

cannot set option of the select using angular js code?

I had following html
<div class="form-group ">
<label class="col-sm-3 control-label">Role</label>
<div class=" col-sm-8">
<select type="text" id="role" name="role" ng-model="role" class="form-control" required>
<option value="">Select Role</option>
<option ng-repeat="rol in rolelist" value="{{rol.id}}">{{rol.title}}</option>
</select>
<p class="ng-invalid" ng-show="addForm.role.$error.required">Role need to selected</p>
</div>
</div>
I want to set value of role dynamically clicking data-sets for the purpose of update and setting value of dom element (control); to do so i had following code inside controller
$scope.data_set=function(id)
{
BlockUi();
url=siteurl+'/admin/'+module+'/get-info';
$http({
url : url,
method : "POST",
data : {"id":id}
}).then(function(responseText){
data=responseText.data;
$scope.first_name=data.first_name;
$scope.user_id=data.id;
$scope.last_name=data.last_name;
$scope.user_name=data.user_name;
$scope.role=data.role;
$scope.email=data.email;
$scope.contact_number=data.contact;
$scope.image_file=data.image;
$scope.status=data.status;
UnblockUi();
},function(error){
UnblockUi();
UnknownError();
alert(JSON.stringify(error));
});
}
above code works for all but role model; I watched and follow other question's solutions but did not work for me?
and ng-required error is removed after this code;
May I suggest you look into the ng-options directive? You'll want to do something along the lines of:
<select ng-model="role" ng-options="rol as rol.Title for rol in rolelist">
<option value="">Select Role</option>
</select>
thanks for effort of everybody
Actually i strayed every where, but solution is simple ; its problem with datatype; actually select: role model contains string datatype and always ; i am setting the integer datatype to role model which is wrong; i convert the datatype of the data of json to string and set the value of role model works fine.

Using both <select>'s value AND text for ng-model

I currently have this:
<div>
<label for="market-type">Market Type</label>
<select id="market-type" type="text" ng-model="tradingFee.market_type">
<option value="stock">Stock Market</option>
<option value="otc">OTC Market</option>
</select>
</div>
which assigns the selected option's value to tradingFee.market_type. What I wish is to be able to do this plus assign the selected option's text to tradingFee.market_type_human_friendly_text, for example. Only being able to do one of the assignments is not enough. Is this possible somehow?
You could do this, but not with this syntax. use ng-options so that the ng-model holds both value and display name.
In your controller set array of objects:
$scope.marketType = [{id:"stock", displayName:"Stock Market"}, {id:"otc", displayName:"OTC Market"}];
and
<select id="market-type" type="text"
ng-model="tradingFee.market_type"
ng-options="mt.displayName for mt in marketType track by mt.id">
<option value="">--Select--</option>
</select>
Now the ng-model will have both id as well as value. i.e example:
tradingFee.market_type will be {id:"otc", displayName:"Stock Market"} if you select that specific item from the dropdown. With this you do not have to worry about maintaining 2 separate properties for displayName and id.
angular.module('app', [])
.run(function($rootScope) {
$rootScope.marketType = [{
id: "stock",
displayName: "Stock Market"
}, {
id: "otc",
displayName: "OTC Market"
}];
$rootScope.tradingFee = {
market_type: {
id: 'stock'
}
};
});
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app="app">
<select id="market-type" type="text" ng-model="tradingFee.market_type" ng-options="mt.displayName for mt in marketType track by mt.id">
<option value="">--Select--</option>
</select>
{{ tradingFee.market_type }}
</div>
You could just use ng-change on your select to fire a custom event handler that sets the secondary value.
<select id="market-type" type="text" ng-model="tradingFee.market_type"
ng-change="updateSecondary()">
<option value="stock">Stock Market</option>
<option value="otc">OTC Market</option>
</select>

AngularJS: Reuse markup for editing many properties of an object

I have an object with a lot of properties (all of type number). I want to edit these properties so for every property I have an example markup:
<div>
propertyA: <input type="number" step="0.1" ng-model="configuration.propertyA" required>
</div>
Plunker
I don't want to repeat the markup for every property. I would like to use ng-repeat or custom directive, but I don't know how to deal with ng-model="...".
Something like:
<div ng-repeat="property in properties">
{{property.???}}: <input type="number" step="0.1" ng-model="property.???" required>
</div>
or custom directive (I know how to transclude static text but what with ng-model):
<my-directive input-value="PropertyA???">PropertyA: </my-directive>
EDIT (maybe will explain more):
I have an configuration object from Server. I don't want to repeat markup I have at the top of the question. I want to have markup once and then loop for every property, so every property will be edited. At the end I want to post configuration back to server.
following the obj you have in your plunkr, it'd just be
<div ng-repeat="item in configuration">
{{item}} <input type="number" step="0.1" ng-model="item" required>
</div>
http://plnkr.co/edit/k1qXyANKRAHJs5drTmXt?p=preview
$scope.configuration = {
propertyA: $scope.(NgModel-Name) <----
}
Instead of value put this $scope, it is called 2 way binding.
And use ng-value to put init value of items. Do you get it ?
It is very easy to do using directive.
app.directive('myInput', function() {
return {
restict: 'EA',
template: '<input type="number" step="0.1" ng-model="value">',
scope: {
value: '=',
},
}
})
Markup:
<span my-input value="configuration.weight"></span>
http://plnkr.co/edit/M9o5A8JYnQeDvhjWDCKU

Set default selection of Dropdown using Backbone

My question is quiet similar to set-dropdown-selected-value-with-backbone, with minor difference that I don't want to manipulate the DOM thru jQuery.
For this what I have done is:-
Assuming my DropDown is of type UserType
var userType = backbone.Model.extend({
idAttribute: "TypeId",
defaults: {
TypeId: null,
TypeName: null,
Selected: false
}
});
Here, I have created an extra attribute as Selected, which I am using as below in my Handlebar template.
<select id="userType" name="UserTypeId" class="input-medium form-control">
{{#each MasterUserType}}
<option id="{{TypeId}}" selected="{{Selected}}">{{TypeName}}</option>
{{/each}}
</select>
Okay, now when I get my Data thru api- say(example)
[
{
"TypeId":1,
"TypeName":"Admin"
},
{
"TypeId":2,
"TypeName":"User"
},
{
"TypeId":3,
"TypeName":"Super Admin"
}
]
Now, I manipulate my variable MasterUserType to become something like below:-
[
{
"TypeId":1,
"TypeName":"Admin",
"Selected":""
},
{
"TypeId":2,
"TypeName":"User",
"Selected":"selected"
},
{
"TypeId":3,
"TypeName":"Super Admin",
"Selected":""
}
]
So my HTML which renders is :-
<div class="controls">
<select id="userType" name="TypeId" class="input-medium form-control">
<option id="" selected="">Admin</option>
<option id="" selected="selected">User</option>
<option id="" selected="">Super Admin</option>
</select>
</div>
But still it reflects the first Item....
Can anyone tell me what is wrong? Or any other alternative?
Lastly, can we achieve this requirement from some other better way?
I have tried replacing "selected with true" but it doesn't work.
Try updating your template as below:
<select id="userType" name="UserTypeId" class="input-medium form-control">
{{#each MasterUserType}}
{{#if Selected}}
<option id="{{TypeId}}" selected="{{Selected}}">{{TypeName}}</option>
{{else}}
<option id="{{TypeId}}">{{TypeName}}</option>
{{/if}}
{{/each}}
</select>

Resources