Reuse HTML but bind different model - javascript

I'm using AngularJS in my website. One page on my site is quite large, as in, it has a lot of HTML elements. Imagine something like this:
<div id="bronze">
<input ng-model="league.bronze.name">
<input ng-model="league.bronze.title">
<input ng-model="league.bronze.description">
... about 15 more inputs ...
<input ng-model="league.bronze.color1">
<input ng-model="league.bronze.color2">
</div>
<div id="silver">
<input ng-model="league.silver.name">
<input ng-model="league.silver.title">
<input ng-model="league.silver.description">
... about 15 more inputs ...
<input ng-model="league.silver.color1">
<input ng-model="league.silver.color2">
</div>
I also have gold and platinum. A site admin must fill in all these settings. This is not a problem for him. But it is for me as a developer.
Everytime I add one or more elements to the "bronze" section then I also have to do the same for every other section. The only difference is the ng-model value.
Isn't there a smarter way to do this? Because right now I'm kinda repeating my self everytime an element needs to be added...

In your controller do:
$scope.types = ['gold', 'silver', 'bronze'];
$scope.properties = ['name', 'title', 'description'];
And in your html do this:
<div ng-repeat="myType in types" id="{{myType}}">
<input ng-repeat="myProperty in properties" ng-model="league[myType][myProperty]">
</div>
EXAMPLE

Check out this quick example of using ng-repeat for input fields
plunker
in your case it will be something like:
<div ng-repeat="league in leagues>
<input ng-model="league.bronze.name">
<input ng-model="league.title">
<input ng-model="league.description">
... about 15 more inputs ...
<input ng-model="league.color1">
<input ng-model="league.color2">
</div>

Related

JavaScript change value and data from value in a span

I need help with this. I need to make the value in the other place change all the time while user session is active. How can I get the value from a span and make other value in a data change?
Look at there!
1 <div class="pt-uea-container">
2 <span class="pt-uea-currency pt-uea-currency-before"> € </span>
3 <input type="text" class="pt-field pt-uea-custom-amount" autocomplete="off" name="pt_items[1][amount]" id="pt_uea_custom_amount_1" value="199" placeholder="" data-parsley-errors-container="#pt_uea_custom_amount_errors_1">
4 <input type="hidden" class="pt-field pt-uea-custom-amount-formatted" name="pt_items[1][amount]" value="199" data-pt-price="199">
5 <input type="hidden" name="pt_items[1][label]" value="Amount:">
6 <input type="hidden" name="pt_items[1][tax_percentage]" value="0">
7 <input type="hidden" name="pt_items[1][type]" value="open">
8 <div id="pt_uea_custom_amount_errors_1"></div>
9 <span class="form-price-value">85</span>
10 </div>
The value in row 9 needs to constantly change values in row 3 and 4 on the same session. Don't mind the value in row 6.
Let me know how I can get this done. Or maybe a different approach?
Greetings!
========
So this is what I got for now from you guys:
jQuery(document).ready(function($) {
var checkViewport = setInterval(function() {
var spanVal = $('.form-price-value').text();
$('#pt_uea_custom_amount_1').val(spanVal);
$('#pt_uea_custom_amount_formatted_1').val(spanVal);
$('#pt_uea_custom_amount_formatted_1').attr('data-pt-price', spanVal);
}, 1000);
});
This code works, but it only affects my needs when I put my mouse in pt-field pt-uea-custom-amount and add a space in it. Then it does apply to the page source. But this is not correct. The source needs to get changed too without touching that class or a space or something!
You can easily do this with the help of jQuery.
With the help of jQuery I would do like this.
Understanding what input field needs to be tracked for changes. I will give all this field a class (track-me).
In the document ready, I will look for changes for that tracked field.
On change of that field I will get the value and put in other input fields (class copy-to - or you can do whatever you like).
See an example below,
HTML
<form>
<div class="">
<input type="text" class="track-me" value=""/>
</div>
<div class="">
<input type="text" class="copy-to" value=""/>
</div>
<div class="">
<input type="text" class="copy-to" value=""/>
</div>
<div class="">
<input type="text" class="copy-to" value=""/>
</div>
<div class="">
<div class="">Please type anything in the first input box</div>
</div>
</form>
jQuery
$(document).ready(function(){
$('.track-me').change(function (){
$('.copy-to').val($(this).val())
});
});
I made comments in the above jQuery code so you can understand. Also, I have made a fiddle so you can play and have a look. In this fiddle, I am using Bootstrap4 just for the purpose of styling, you don't have to worry about that.
Link to fiddle
https://jsfiddle.net/anjanasilva/r21u4fmh/21/
I hope this helps. Feel free to ask me any questions if you have. Cheers.
This is not an ideal solution. I'm not sure there is a verified way of listening for when the innerHTML of a span element changes. This sort of stuff is usually based on user interaction, and the value of the span will be modified by your page. The best solution would be to use the same method that updates the span element to update the values of you hidden input fields.
However, I've placed an interval that will run every second, that takes the text value of the span element and gives it to the values of the 2 input fields:
function start() {
setInterval(function() {
document.getElementById("pt_uea_custom_amount_1").value = document.getElementById("price_value").innerHTML;
document.getElementById("pt_uea_custom_amount_2").value = document.getElementById("price_value").innerHTML;
}, 1000);
}
window.onload = start();
<div class="pt-uea-container">
<span class="pt-uea-currency pt-uea-currency-before"> € </span>
<input type="text" class="pt-field pt-uea-custom-amount" autocomplete="off" name="pt_items[1][amount]" id="pt_uea_custom_amount_1" value="199" placeholder="" data-parsley-errors-container="#pt_uea_custom_amount_errors_1">
<input type="hidden" class="pt-field pt-uea-custom-amount-formatted" name="pt_items[1][amount]" value="199" data-pt-price="199" id="pt_uea_custom_amount_2">
<input type="hidden" name="pt_items[1][label]" value="Amount:">
<input type="hidden" name="pt_items[1][tax_percentage]" value="0">
<input type="hidden" name="pt_items[1][type]" value="open">
<div id="pt_uea_custom_amount_errors_1"></div>
<span id="price_value" class="form-price-value">85</span>
</div>
MutationObserver should work here..
const formValuePrice = document.querySelector( '.form-price-value' );
const inputText = document.querySelector( 'input[type="text"]' );
// timer to change values
window.setInterval( () => {
formValuePrice.textContent = Math.round( Math.random() * 100 );
}, 1000 );
// mutation observer
const observer = new MutationObserver( ( mutationsList ) => {
inputText.value = formValuePrice.textContent;
} );
observer.observe( formValuePrice, { childList: true } );
https://codepen.io/anon/pen/LgWXrz?editors=1111
try this, simple using jquery, you can check in inspect element for value attribute data-pt-price
Update: you can using jquery event .on() like change, click, keyup or else to Attach an event handler function for one or more events to the selected elements,
you can read the doc here.
here the updated code
$(function() {
var spanVal = $('#price_value').text();
$('#pt_uea_custom_amount_1').val(spanVal);
$('#pt_uea_custom_amount_formatted_1').val(spanVal);
$('#pt_uea_custom_amount_formatted_1').attr('data-pt-price', spanVal);
$('#pt_uea_custom_amount_1').on('change click keyup', function() {
$('#pt_uea_custom_amount_formatted_1').val($(this).val());
$('#price_value').text($(this).val());
$('#pt_uea_custom_amount_formatted_1').attr('data-pt-price', $(this).val());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="pt-uea-container">
<span class="pt-uea-currency pt-uea-currency-before"> € </span>
<input type="text" class="pt-field pt-uea-custom-amount" autocomplete="off" name="pt_items[1][amount]" id="pt_uea_custom_amount_1" value="199" placeholder="" data-parsley-errors-container="#pt_uea_custom_amount_errors_1">
<input type="hidden" class="pt-field pt-uea-custom-amount-formatted" name="pt_items[1][amount]" value="199" data-pt-price="199" id="pt_uea_custom_amount_2">
<input type="hidden" name="pt_items[1][label]" value="Amount:">
<input type="hidden" name="pt_items[1][tax_percentage]" value="0">
<input type="hidden" name="pt_items[1][type]" value="open">
<div id="pt_uea_custom_amount_errors_1"></div>
<span id="price_value" class="form-price-value">85</span>
</div>

What's the cleanest way to implement a dynamic HTML form?

I want to make a HTML form where the user can edit a set of elements at once. The user has to be able to remove an element, edit element and add new elements. I'm using PHP (Laravel) as a backend and jQuery for the dynamic form.
My initial idea was do basically this:
<form id="bars">
#foreach($foo as $bar)
<input type="text" name="name[]" value="{{$bar->name}}" required>
<input type="color" name="color[]" value="{{$bar->color}}" required>
<input type="checkbox" name="completed[]"{{$bar->completed ? ' checked' : ''}}>
<span class="deleteRow"></span>
#endforeach
</form>
<div id="template" style="display:none;">
<input type="text" name="name[]" required>
<input type="color" name="color[]" required>
<input type="checkbox" name="completed[]"{{$bar->completed ? ' checked' : ''}}>
<span class="deleteRow"></span>
</div>
<script>
$(document).ready(function() {
// Remove rows
$('form').on('click', '.deleteRow', function() {
$(this).closest('tr').remove();
});
// Clone new rows from template
$('#addRow').click(function() {
$('#template tr')
.clone()
.appendTo('#bars');
});
});
</script>
This is the Blade template from which I've removed all irrelevant code and styling. This approach uses the following concepts:
[] in the input names so that the POSTed data will be placed into an array. In processing I end up with three arrays name, color, and completed which will contain the POSTed data.
Existing elements are rendered server-side in the form by the Blade template
New rows are cloned from a 'form template' and added to the form when the user clicks the "new row" button.
This is quite simple, and works in most cases (I've used this before), but it does not work in this case because of a small thing: the checkboxes. Unchecked checkboxes will not be POSTed which means that in the completed[] array will be smaller in size than the other arrays and I have no way of checking which elements have the checkbox checked.
Now I could modify my JS in such a way that it keeps track of the indexes and explicitly inserts the index in every input name (so name[0], name[1], etc) but that approach is complicated by the fact that the form must be pre-filled with data and does not start out empty.
I can, instead of filling the data through the Blade template, let the JS handle that too (through a JSON API) but that also gets complicated fast because the JS now has to 'parse' the form template and fill in all the values.
What's the best practise to accomplish this in a clean way?
What I ended up with is the following:
<form id="bars">
#foreach($foo as $i => $bar)
<input type="hidden" name="id[{{$i}}]" value="{{$status->id}}">
<input type="text" class="form-control" name="name[{{$i}}]" value="{{$status->name}}" required>
<input type="color" name="color[{{$i}}]" value="{{$bar->color}}" required>
<input type="checkbox" name="completed[{{$i}}]"{{$bar->completed ? ' checked' : ''}}>
<span class="deleteRow"></span>
#endforeach
</form>
<div id="template" style="display:none;">
<input type="text" name="name[$i]" required>
<input type="color" name="color[$i]" required>
<input type="checkbox" name="completed[$i]"{{$bar->completed ? ' checked' : ''}}>
<span class="deleteRow"></span>
</div>
<script>
$(document).ready(function() {
// Remove rows
$('form').on('click', '.deleteRow', function() {
$(this).closest('tr').remove();
});
// Clone new rows from template
$('#addRow').click(function() {
$('#template tr')
.clone()
.html(function(i, oldHTML) {
return oldHTML.replace(/\$i/g, $('#bars tr').length);
})
.appendTo('#bars');
});
});
</script>
This is mostly what #MagnusEriksson advised me to do (so thanks!) but solved in a slightly cleaner way by not having to keep a counter but simply using the number of rows inside the form as the index for the cloned rows.

Check if form input fields have not changed

I have a form, let's call it myForm similar to this:
<form name="myForm" class="vertical grid-block shrink" ng-init="initSearch()" ng-submit="doSearch(myForm.$valid)" novalidate>
And three input fields: one with keeps details, second which is a starting date and third which is a ending date.
The fields look like this:
<input id="details" type="text" ng-model="myObject.details" placeholder="DETAILS_PLACEHOLDER">
<input id="from" class="uppercase" type="text" name="from" placeholder="{{datePlaceHolder}}" ng-model="myObject.from" required/>
<input id="until" class="uppercase" type="text" name="until" placeholder="{{datePlaceHolder}}" ng-model="myObject.until"/>
And I want to display a button based on the form fields. If the form fields have not changed the button should be hidden.
I tried using $dirty but the problem is $dirty remains true even if the user types in details and then deletes the text.
Anyone has any solution for this?
Also the solution must work even if I come back to the form from another page which probably has another controller.
Any help is appreciated. Thank you
This should fix that. It checks for $pristine, ie untouched.
<button ng-show="myForm.$pristine"></button>
Take a look at sample below, basically you need $viewValue for displaying/hiding submit button.
var app = angular.module('app', []);
app.controller('ctrl', function($scope) {
$scope.myObject = {};
$scope.datePlaceHolder = 'from date';
$scope.datePlaceHolder2 = 'until date';
$scope.doSearch = function() {
alert('submit');
}
$scope.initSearch = function() {
console.log('init search called');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<p>Need to fill FROM and UNTIL values to submit</p>
<form name="myForm" ng-init="initSearch()" ng-submit="doSearch(myForm.$valid)" novalidate>
<input id="details" type="text" ng-model="myObject.details" placeholder="DETAILS_PLACEHOLDER">
<input id="from" type="text" name="from" placeholder="{{datePlaceHolder}}" ng-model="myObject.from" required/>
<input id="until" type="text" name="until" placeholder="{{datePlaceHolder2}}" ng-model="myObject.until" />
<button type="submit" ng-show="myForm.from.$viewValue && myForm.until.$viewValue">Submit</button>
</form>
</div>
You can add more conditions to
ng-show="myForm.from.$viewValue && myForm.until.$viewValue"
Notice that if you delete value from input, submit button disappears

How to dynamically create multiple form input fields with incremented ng-models?

After reading this article, I understand how to dynamically add a form field using ng-repeat.
I am wondering how can multiple form elements be dynamically created with incrementing ng-model values.
For example, the following would be created from a button click.
<input ng-model="vm.foo.bar1.first">
<input ng-model="vm.foo.bar1.second">
<input ng-model="vm.foo.bar1.third">
<input ng-model="vm.foo.bar1.fourth">
<input ng-model="vm.foo.bar2.first">
<input ng-model="vm.foo.bar2.second">
<input ng-model="vm.foo.bar2.third">
<input ng-model="vm.foo.bar2.fourth">
<input ng-model="vm.foo.bar3.first">
<input ng-model="vm.foo.bar3.second">
<input ng-model="vm.foo.bar3.third">
<input ng-model="vm.foo.bar3.fourth">
How can this be done?
I would suggest to restructure your ViewModel to make vm.foo.bar an array. Then this would be trivial:
<div ng-repeat="item in barItems">
<input ng-model="vm.foo.bar[$index].first">
<input ng-model="vm.foo.bar[$index].second">
<input ng-model="vm.foo.bar[$index].third">
<input ng-model="vm.foo.bar[$index].fourth">
</div>
Or, if you insist, then also
<div ng-repeat="item in barItems" ng-init="outerIdx = $index">
<input ng-repeat='p in ["first", "second", "third", "fourth"]'
ng-model="vm.foo.bar[outerIdx][p]">
</div>
(I'm assuming here, that unlike with first, second, etc..., the number of bars is not known - hence an array is a better option)
EDIT:
If you really want, you could also make vm.foo an object that holds properties bar1, bar2, etc...:
<div ng-repeat="item in [1, 2, 3, 4]">
<input ng-model="vm.foo['bar' + item].first">
<input ng-model="vm.foo['bar' + item].second">
<input ng-model="vm.foo['bar' + item].third">
<input ng-model="vm.foo['bar' + item].fourth">
</div>
but don't forget to first create vm.foo object in the controller:
this.foo = {};
When I have to do this I use the $index to control the names of things. Although I've never tried this exact code, this should work.
<input ng-model='vm.foo.bar3[$index]'></input>
$index comes along whenever you do ng-repeat and is just the index of the list item. So that should end up making ng-models that are vm.foo.bar3.0 to whatever.
To my point of view, you should create arrays of models in your controller.
$scope.vm.foo = [{
bar1: [{
first: '',
second: '',
...
},
bar2: ...
],
}]
And then in your view iterate on your tab :
<div ng-repeat="elem in foo">
<div ng-repeat="input in elem">
<input ng-model="input">
</div>
</div>
Hope it will help you !

Requesting specific values from an html input via Javascript

I'm trying to find a way to be able to do the following. I want to be able to get certain things from a form. In this case, I only want the "value" field and NOT the "name" field.
<div class="searchbox_team" style="margin: 0 0 10px 0; z-index: 50;">
<script type="text/javascript">
function customSearch()
{
var x = document.customSearch;
x.replace("customSearch=", "");
return x;
}
</script>
<form name="leSearch" action="/search/node/" onsubmit="return customSearch()" id="search-block-form" class="search-form">
<input type="text" name="customSearch" value="" id="edit-search-block-form-1" class="searchbox_input" title="Enter the terms you wish to search for." />
<input type="submit"/>
</form>
</div>
I have tried using the following in my function.
var x = document.customSearch.value;" but that is not working.
Can anyone shed some light on this?
It sounds like you want the value of the input for customSearch. If so then just use the following
var value = document.getElementById('edit-search-block-form-1').value;
Your input tag already has an id value hence the most efficient and simplest way to search for it is using getElementById.
hmm, so to get things from the form, you'll want to specifiy like so:
document.forms.leSearch.elements["customSearch"].value;
EDIT:
try adding a hidden field that stores the value onclick and then get that from the post or get array in your action file.. I think onsubmit call is to blame
<form name="leSearch" action="/search/node/" onclick="document.getElementById('myhiddenfield').value = customSearch()" id="search-block-form" class="search-form" method="post">
<input type="text" name="customSearch" value="" id="edit-search-block-form-1" class="searchbox_input" title="Enter the terms you wish to search for." />
<input type="hidden" value="" id="myhiddenfield" />
<input type="submit"/>
</form>
EDIT 2:
I think I figured it out.. the url was appending the field names because it was defaulting to "get" method mode.. set the action=/node/search/" and method="post"
<form method="post" action="/search/node/" onsubmit="this.action = '/search/node/' + document.getElementById('edit-search-block-form-1').value;">

Categories

Resources