I want to sent to backend an attribute of the model value. To do that, I have a directive that draws a select input, with all the selectable values
Context: We have a Grails domain class defined as
class Example {
String name
EnumObject desiredToTransform
}
There is a GET Rest service returning the JSON of an instance of this object as:
{
"id": 1,
"name": "Albert"
"desiredToTransform": {
"id": "EXAMPLE_ID",
"label":"Example value"
}
}
The create Rest service for this object, receives the following arguments: [name: String, desiredToTransform:String]. If we want to create an object of this instance, we must do the following:
curl -X POST <POST_URL> -d '{"name":"Jhon Doe", "desiredToTransform":"EXAMPLE_ID"}'
The problem:
The solution I found was to create a directive which receives the model and transform the result as desired
A plunker with the solution can be found here, and shown at next on snippet
// Code goes here
var app = angular.module("app", []);
function rselectForEnum($http, $compile) {
return {
link: function(scope, elem, attrs) {
var parent = angular.element(elem.parent());
var enumerativo = attrs.enum || attrs['data-enum'] || "";
var idElem = attrs["id"] || new Date().getTime().toString();
var nameElem = attrs["name"] || idElem;
var modeloElem = attrs.ngModel;
if (typeof modeloElem === "undefined") {
throw Error("De especificar el modelo");
}
//The original data receive a parameter for a REST service in order to gather the data
// $http.get(config.apiUrl + "enumerativo/getEnum?c=" + enumerativo).then(function (response) {
// scope[nameElem + "EnumList"] = response.data.lista;
// }, function (response) {
// });
//I am going to replace it with this code
scope[nameElem + "EnumList"] = [{
id: "EXAMPLE_1",
label: "Exampple 1"
}, {
id: "EXAMPLE_2",
label: "Exampple 2"
}, {
id: "EXAMPLE_3",
label: "Exampple 3"
}, {
id: "EXAMPLE_4",
label: "Exampple 4"
}, {
id: "EXAMPLE_5",
label: "Exampple 5"
}, ]
var htmlSelect = "<select id='" + idElem + "' " +
"name='" + nameElem + "' " +
" class='form-control'>" +
"</select>";
var elemento = angular.element(htmlSelect);
elemento.attr("data-ng-model", attrs.ngModel);
elemento.attr("ng-options", "item.id as item.label for item in " + nameElem + "EnumList track by item.id");
parent.append(elemento);
elem.remove();
$compile(parent)(scope);
}
}
}
app.directive('rselectforenum', ['$http', '$compile', rselectForEnum]);
app.controller("PruebaController", PruebaController);
/*#ngInject*/
function PruebaController($scope) {
var vm = this;
$scope.modelo = {
enumerativo: {
"id": "EXAMPLE_2",
"label": "Example 2"
}
};
vm.wizzard = {
};
init();
return vm.wizzard;
function init() {
// initialize tuffs here
}
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js
"></script>
<script src="script.js"></script>
</head>
<body>
<h1>How to transform the model with a select component?</h1>
<div ng-controller="PruebaController">
<div class="row">
<div class="col-md-3">
<label class="">Probando</label>
<div class="form-line">
<rselectForEnum
id="rselectForEnumId"
data-ng-model="modelo.enumerativo"
enum="EstadoCivil"
></rselectForEnum>
</div>
</div>
</div>
<div class="row">
This is the value:
{{modelo.enumerativo}}
</div>
<br/>
<div class="row">
This is the list:<br/>
{{rselectForEnumIdEnumList}}
</div>
</div>
</body>
</html>
The result turned on the following behaviors:
When the content is loaded, the select input is selected with the model value (DESIRED)
The model keep it original value (NOT DESIRED)
On change value, model got the id attribute (DESIRED)
At first modification on the select, the label turns blank, but value is right (NOT DESIRED)
If you do not change the select and sent the form to backend, the original value is sent (NOT DESIRED)
How can I fix this?
Related
I loop over a JSON object to populate dynamically a <ul> list.
Each <li> item has an <input> element with a click event attached to it.
Here follows my code
index.html
<div class="row">
<div class="col s12 l6">
<ul id ="moduleList" class> </ul>
</div>
</div>
js
var data = {
"modules": [
{
"name": "mod1",
"description": "mod1 description",
},
{
"name": "mod2",
"description": "mod2 description",
},
{
"name": "mod3",
"description": "mod3 description",
}
]
}
document.addEventListener('DOMContentLoaded', function() {
$.each(data.modules, function(i, field){
let module_name = field.name;
let module_description = field.description;
//build the html <li> element
// add name and description info
let li_html = "<div> Name: " + module_name + "</div>";
li_html += "<div> Description: " + module_description + "</div>";
//build the <input type="button"> element and add it to the li_html string
let select_button = "<div> <input class= \"set_inputs\" type=\"button\" value = \"select\"/> </div>";
li_html +=select_button;
// append the <li> item code to the <ul> element
$("<li />").html(li_html).appendTo("#moduleList");
//attach the click event to current field
$(".set_inputs").click(function(){
// save field info in the web browser
localStorage.setItem('moduleObj', JSON.stringify(field));
console.log("module id= " + i + " module_name= " + module_name );
window.location.href = 'select_inputs.html';
});
});
});
select_inputs.html
[..]
<div class="row">
<div class="col s12 l6">
<ul id ="moduleList" class> </ul>
</div>
</div>
[..]
<script>
document.addEventListener('DOMContentLoaded', function() {
// get the active field
var retrivedModuleObj = JSON.parse(localStorage.getItem('moduleObj') || '{}');
console.log("MODULE " + retrivedModuleObj.name);
// remove the field from the web browser
localStorage.removeItem("moduleObj");
});
</script>
When I load index.html, I get the expected name + description text and a select button for each JSON field.
But when I click any button, in the console I get:
// index.html
module id= 1 module_name= mod2
module id= 2 module_name= mod3
//select_inputs.html
MODULE undefined
I have two issues:
in index.html, why clicking on one mod1 button, mod2 and mod3 fire the click event?
in select_inputs.html, the active field element is not received
I'm pretty new to js and can't understand what is going on.
Any help would be really appreciated.
Thank you.
EDIT 1: as suggested, I change the code that retrieves the object field in select_inputs.html. This solves issue number 2, but something is still wrong in index.html since I always get
MODULE mod3
despite the button I click.
You serialised the value you put in to localStorage to JSON. Therefore you need to deserialise it again when you try and read the value:
var retrivedModuleObj = JSON.parse(localStorage.getItem('moduleObj') || '{}');
console.log("MODULE " + retrivedModuleObj.name);
In addition, due to the loop completing before the click handler can be executed the field variable will only ever hold the value of the final iteration. A better approach would be to use a data attribute to hold the field related to each button element, and a delegated event handler to manage the click event on them. Try this:
var data = {
"modules": [{
"name": "mod1",
"description": "mod1 description",
}, {
"name": "mod2",
"description": "mod2 description",
}, {
"name": "mod3",
"description": "mod3 description",
}]
}
jQuery($ => {
$.each(data.modules, function(i, field) {
let $li = $(`<li><div>Name: ${field.name}</div><div>Description: ${field.description}</div></li>`).appendTo('#moduleList');
$('<input class="set_inputs" type="button" value="select" />').data('field', field).wrap('<div />').parent().appendTo($li);
});
$('#moduleList').on('click', '.set_inputs', e => {
let $button = $(e.currentTarget);
console.log($button.data('field'));
// Uncomment these two lines in your production version. They are only commented
// here as they cause issues within SO snippets
//localStorage.setItem('moduleObj', JSON.stringify($button.data('field')));
//window.location.href = 'select_inputs.html';
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row">
<div class="col s12 l6">
<ul id="moduleList"></ul>
</div>
</div>
I defined a dropbox selector which after selecting a country gives me a list of cities as checkboxs. I am using jquery and ajax to preview it and select it as follow.
<div>
<div id="preview-items">
</div>
</div>
<script>
$("#id_country").change(function () {
var countryId = $(this).val(); // get the selected country ID from the HTML input
$.ajax({ // initialize an AJAX request
url: '/ajax/ajax_load_cities',
data: {
'countries': countryId // add the country id to the GET parameters
},
dataType: 'json',
success: function (data) { // `data` is the return of the `load_cities` view function
$('#preview-items').html('');
for (var i in data.tags) {
$('#preview-items').append(`
<label class="btn btn-primary mr-1">
<input type="checkbox" id="checklist_` + data.tags[i][0]+ `" value="` + data.tags[i][0] + `">
<span>
` + data.tags[i][1] + `
</span>
</label><br />`
).find("#checklist_" + data.tags[i][0]).on("change", function(e){
var cities_array = $(":checkbox:checked").map( function() { return $(this).next().html(); }).get();
$("#preview-items").html(cities_array.join(', '));
if (e.target.checked) {
localStorage.checked = true;
} else {
localStorage.checked = false;
}
}
}
});
});
</script>
and in django view.py:
def load_cities(request):
....
data = {
'tags': list(cities)
}
return JsonResponse(data)
The problem is that, it does not keep the selected cities after changing the country selectors. after googling, I found that cookies are a good choice. I wanted to know how to save selected checkboxes when dropbox items change?
I don't think you really need cookies or localstorage. I suggest you take another approach:
first you create the javascript code that builds the dropbox selector based on a state, in your example the list of cities.
then when you make the ajax call you just call that function again.
window.onload = function() {
var $container = $('#preview-items')
// when starting or loading the page I suggest either outputting the initial list
// of unchecked data in your markup somewhere as a json object
// or perform an initial ajax call. Here I just store it in a local variable.
var initialData = [
{id: 1, name: "Amsterdam", checked: false},
{id: 2, name: "Berlin", checked: true},
{id: 3, name: "Brussels", checked: false},
]
buildDropbox(initialData)
function buildDropbox(cities) {
$container.empty()
cities.forEach(function(city) {
let checked = city.checked? "checked=\"checked\"" : ""
$container.append(`
<label class="btn btn-primary mr-1" for="checklist_${city.id}">
<input type="checkbox" ${checked} id="checklist_${city.id}" value="${city.id}">
<span>${city.name}</span>
</label><br />`
)
var chk = document.getElementById("checklist_" + city.id)
chk.addEventListener("change", function(e) {
saveChange(e.currentTarget.name, e.currentTarget.value, e.currentTarget.checked)
})
})
}
function saveChange(chkName, chkValue, chkChecked) {
console.log("POST ajax for " + chkValue + " with checked: " + chkChecked);
// do your ajax call here, and in the success
// be sure to return the same list as the
// initialData, but with the updated 'checked'
// value, obviously.
// here, for testing purposes, I reuse the initialData
// and check all checkboxes randomly
let updatedData = initialData.map(function(c) {
return {...c, checked: Math.round(Math.random()) == 0}
})
buildDropbox(updatedData)
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<div id="preview-items"></div>
</body>
</html>
Here's an example. It may not work here due to restrictions, but you can set it up to work for you.
It saves every 5 seconds and checks if its true, and if it is, save it.
var check = document.getElementById("checkbox1");
function change() {
window.setTimeout(function() {
change();
if (check.checked == true) {
localStorage.setItem("isChecked", check.checked);
}
}, 5000);
}
change();
window.onload = function() {
check.checked = localStorage.getItem("isChecked")
}
h1 {
font-family: "Comic Sans MS", sans-serif;
}
<h1>Click the check box</h1>
<br><br>
<input type="checkbox" id="checkbox1"> <!--Your Checkbox-->
I hope it works for your app!
I am fairly new to AngularJS and I have been reading some answers here but nothing worked out. I have a json file from a controller that I display in a select. I want to set the selected value based on the text value.This is what I have so far.
HTML:
<div ng-app="userModule" ng-controller="userCtrl">
<div class="row">
<div class="col-md-6">
<label>User Name:</label> <br />
<select ng-model="users.selectedUser" class="form-control" ng-options="item.UserName as item.UserName for item in users.availableOptions"></select>
</div>
Controller:
<script>
var _$http;
var _$scope;
var oldUser = #Html.Raw(Json.Serialize(ViewData["UserName"]));
var oldRole = #Html.Raw(Json.Serialize(ViewData["RoleName"]));
angular.module('userModule', [])
.controller('userCtrl', xConstructor);
function xConstructor($scope, $http) {
_$http = $http;
_$scope = $scope;
$http.get("/RoleManagement/GetUserData").then(xReceive);
$http.get("/RoleManagement/GetRoleData").then(roleReceive);
_$scope.submit = function () {
//alert("Here:" + _$scope.selectedUser);
$http.get("/RoleManagement/PutUserRoleData?UserId=" + _$scope.selectedUser.UserId + "&RoleId=" + _$scope.selectedRole.RoleId).then(writeSuccess);
}
}
function xReceive(userObject) {
_$scope.users = {
availableOptions: userObject.data,
**selectedUser: { UserId: oldId, UserName: oldUser } //What to put here?**
};
alert(JSON.stringify(JSON.stringify(_$scope.users.selectedUser));
}
</script>
Or any other suggestions on how to do this?
The problem is you are not mapping the model to any element in the array you have.
Assuming you have the id of the user you want to select this is what you do:
function xReceive(userObject) {
_$scope.users = {
availableOptions: userObject.data,
selectedUser: null
};
let selectedUser;
for (let i = 0; i < userObject.data.length; i++) {
if (userObject.data[i].id === oldId) {
selectedUser = userObject.data[i];
break;
}
}
if (selectedUser) {
_$scope.users.selectedUser = selectedUser;
}
alert(JSON.stringify(JSON.stringify(_$scope.users.selectedUser));
}
Also note, you can do this to just select the first one:
_$scope.users.selectedUser = _$scope.users.availableOptions[0];
I'm new to AngularJS, so sometimes when I do some mistake that is obvious, I still can't figure out what is going wrong with my code. So saying, here is my doubt:
HTML code:
<body ng-controller = "Ctrl">
<script id="Page6.html" type="text/ng-template">
<div class="list card" style="background-color: beige">
<div class="item item-icon-left">
<i class="icon ion-home"></i>
<input type="text" placeholder = "Enter display name" ng-model="user.nam">
</div>
<a ng-click = "saveedit(user)"<button class="button button-clear">SAVE DETAILS</button></a>
</div>
</script>
</body>
CONTROLLER.JS
.controller('Ctrl',function($scope,$rootScope,ContactService){
$rootScope.saveedit=function(user) {
ContactService.save({names: user.nam, image:"images.jpg"},ContactService.getid("Donkey"));
}
});
THIS IS THE SERVICE:
.service('ContactService', function () {
var items = [
{ id: 1, names: 'Dolphin', image: 'dolphin.jpg',}, { id: 2, names: 'Donkey', image: 'donkey.jpg'}, { id: 3, empid: 'FG2043', image: 'penguin.jpg'}];
var im = [{image: ''}];
var ctr=0;
var uid=3;
this.save = function (contact,id) {
ctr=0;
for (i=0;i<items.length;i++) {
if(items[i].id == id)
{
im[0].image= items[i].image;
ctr=100;
break;
}
}
uid = (uid+1);
contact.id = uid;
items.push(contact);
if (ctr==100 ) {
alert("in save putting the image");
items[contact.id].image = im[0].image; //doubt
alert("finished putting image");
}
}
//simply search items list for given id
//and returns the object if found
this.getid = function (name) {
for (i=0;i<items.length;i++) {
if (items[i].names == name) {
return (i+1);
}
}
}
//simply returns the items list
this.list = function () {
return items;
}
});
The problem I am facing is this: Everything works, except one thing. In ContactService, push() function, the line I have commented as //doubt is not getting executed.
The alert before it "in save putting the image" runs, but the alert "finished putting image" doesn't. What is the mistake there??
The problem here is that you're using the id's, which start at 1, to navigate in an array whose indexes start at 0.
To access the most recently pushed element, you should rather do :
items[contact.id - 1].image = im[0].image;
But you actually don't need to access the array : items[contact.id - 1] will return the object that you just pushed, and which is already referenced by variable contact, so you could just do :
contact.image = im[0].image;
I have code that populates then dropdownlist and the javascript variable that gets the last item in the list. Now all I want to do is select that last item as the default .What am I missing ?
<div class="row">
<div>
<select ng-init="lastItem" ng-model="congressFilter" ng-options="cc.congressLongName for cc in ccList"></select>
</div>
<div class="grid-style" data-ng-grid="userGrid">
</div>
ccResource.query(function (data) {
$scope.ccList.length = 0;
angular.forEach(data, function (ccData) {
$scope.ccList.push(ccData);
})
//Set default value for dropdownlist?
$scope.lastItem = $scope.ccList[$scope.ccList.length - 1];
});
You simply need to asign a value to congressFilter in your controller.
$scope.congressFilter = 'someVal';
It depends a little on how your data looks however.
It might help to new developers. need to add default id for display default item in option.
The below code sample we add [ $scope.country.stateid = "4" ] in controller $scope to set the default.
var aap = angular.module("myApp", []);
aap.controller("MyContl", function($scope) {
$scope.country = {};
$scope.country.stateid = "4";
$scope.country.states = [{
id: "1",
name: "UP"
}, {
id: "4",
name: "Delhi"
}];
});
<body ng-app="myApp">
<div ng-controller="MyContl">
<div>
<select ng-model="country.stateid" ng-options="st.id as st.name for st in country.states">
</select>
ID : {{country.stateid}}
</div>
</div>
</body>