AngularJS - Proven way of handling ng-model data - javascript

I have a form, and I'm trying to bind date from it in the angularjs - controller so I can pass it into the djangorestframework view to do more stuff with it.
Now my problem is that I don't understand how to properly bind data from the datetimepicker input filed in the controller, I'll show up my form and small part of the controller, as far as I understand this is that I need to have ng-model on the input field and put a function on the Submit button, and that is clear for me but the part in the controller I don't understand, so how can I properly bind this, can someone please help me, thank you, controller is written in coffee script.
<div class="flex-grid"
ng-controller="FilterContactsListCtrl">
<div class="row">
<div class="cell size-p20 padding10">
<form action="." method="post">{% csrf_token %}
<label for="id_select_date">Select Date: *</label>
<div class="full-size">
<div class="input-control full-size text"
data-role="datepicker" date-format="mmmm d, yyyy">
<input id="id_select_date" ng-model="selectDate"/>
<button class="button"><span class="mif-calendar"></span></button>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="cell size-p20 padding10">
<button class="button primary" ng-click="doAction()">
{% trans "Submit" %}
</button>
</div>
</div>
</div>
Controller:
app = angular.module 'vinclucms.sales'
app.controller 'FilterContactsListCtrl', ['$scope', '$rootScope', 'LeadContact'
($scope, $rootScope, LeadContact) ->
$scope.doAction = ()->
filterLeadContactList()
filterLeadContactList = () ->
$scope.selectDate = null
$scope.doAction = () ->
# Do Action with date form the input field so I can pass it to the restapi view
# this part I don't understand, how to bind this properly
]

the problem is you don't pass any model to your controller. basicly your doAction() method can't do anything!
What I suggest is to change your form to use ng-submit and pass your model to controller.
<form ng-submit="doAction(selectDate)" action="." method="post">{% csrf_token %}
<label for="id_select_date">Select Date: *</label>
<div class="full-size">
<div class="input-control full-size text"
data-role="datepicker" date-format="mmmm d, yyyy">
<input id="id_select_date" ng-model="selectDate"/>
<button class="button"><span class="mif-calendar"></span></button>
</div>
</div>
<button class="button primary" type="submit">
{% trans "Submit" %}
</button>
</form>
As you notice I bring your button inside the form with type=submit.
Later in your controller you can access your data model like this :
$scope.doAction = function(selectDate){
console.log(selectDate); // You have access to your data now
//Do what ever you want
}

Related

Refresh (variable) part of an html page with hotwire (Flask app)

Context
I am building a simple "todo" flask app with an SQLAchemy database.
The tasks are sorted by sections (check the image below to see how it is organized).
Once I implemented all the functionalities I wanted, I ran into the issue which was the whole page
got refreshed each time I triggered a button (add/edit/delete/update_status).
Then I found out hotwire which is amazing to handle this.
This my taskManager.html organization:
<!--New task button-->
<turbo-frame id="data_frame-add">
<button class="btn-add" onclick="openAddForm()">...</button>
<div class="form-popup" id="myForm">
<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST">
<label for="section"><b>Section</b></label>
<input type="text" id="section" name="section" required>
<label for="content"><b>Task</b></label>
<input type="text" id="content" name="content required>
<button type="submit" class="btn">Add</button>
</form>
</div>
</turbo-frame>
<!--Display sections and tasks-->
<div class="flex-row">
{% for section in sections %}
<turbo-frame id="data_frame-{{ section }}">
<div class="flex-column">
<h2>{{ section }}</h2>
{% for task in tasks %}
{% if task.section == section %}
<p>{{ task }}</p>
<button class="satus"></button>
<button class="edit"></button>
<div class="form-popup" id="form-{{ task.id }}">...</div>
<button class="delete"></button>
<div class="form-popup" id="form-{{ task.id }}">...</div>
{% endif %}
{% endfor %}
</div>
</turbo-frame>
{% endfor %}
</div>
Using a turbo frame with id data_frame-{{ section }} (one for each section) allowed to refresh only the concerned section when hitting status, edit and delete buttons (for example, hitting delete button of task 2 of Section 2 will only refresh the turbo frame data_frame-Section 2). However as the New task button is out of theses turbo frames, It works differently and this is a different challenge...
Issue
When adding a new task, I would like the section of the task (entered here <input type="text" id="section" name="section"...>) to be saved in a variable which will be used to target a specific <turbo-frame id="data_frame-{{ section }}"> and refresh it without refreshing the whole page.
At the moment as the New task button is wrapped with <turbo-frame id="data_frame-add"> it is self contained (meaning if I'm adding a task 5 to Section 1 only the turbo frame with id data_frame-add is refreshed not the data_frame-Section 1 so I need to manually refresh the page to see changes)
What I tried
I added data-turbo-frame to the form:
<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST" data-turbo-frame="data_frame-Section 1">
in order to be able to refresh the "data_frame-Section 1" when I add a New task in section Section 1, and it works! But I would like to make this data-turbo-frame="data_frame-<section>" with <section> a variable that get the value of <input type="text" id="section" name="section"...>
To achieve this I removed data-turbo-frame="data_frame-Section 1" in the form:
<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST">
and added a Javascript part:
var sectionValVar = document.getElementById("section").value;
const sectionValPref = "data_frame-";
let sectionVal = sectionValPref + sectionValVar;
$(".AddTaskForm").attr("data-turbo-frame", sectionVal);
sectionVal is supposed to get the variable value "data_frame-<section>" and last line add "data-turbo-frame" = "data_frame-<section>" to the <form name="AddTaskForm"...>
But this doesn't work. I'm not sure if this even possible to make as it looks tricky...
But if someone has any hint or fix for this It would be amazing !
Thank you !
Other ressources
This is my add_task route in my python flask app:
#app.route('/add', methods=['GET', 'POST'])
def add_task():
content = request.form.get('content')
section = request.form.get('section')
task = Task(content=content, section=section)
db.session.add(task)
db.session.commit()
return redirect(url_for('taskManager'))
This looks like it would work but I don't see an event listener to set the form data-turbo-frame attribute whenever the input value changes.
You need to update the attribute either before the form submits or whenever the input gets updated.
this is how you could do it with jquery
$("#section").change(function() {
let sectionAndPrefix = "data_frame-" + $("#section").val()
$(".AddTaskForm").attr("data-turbo-frame", sectionAndPrefix);
})
in vanilla javascript
const sectionInput = document.querySelector("#section")
sectionInput.addEventListener("input", function() {
const taskForm = document.querySelector(".AddTaskForm")
const sectionAndPrefix = "data_frame-" + sectionInput.value
taskForm.setAttribute("data-turbo-frame", sectionAndPrefix)
})

Why When I click on submit the parameters of the form disappear?

In my google app script, i have an html page with a form whose action attribute is created dynamically based on the input values (with javascript) and i create the action url with parameters. After that i insert some input value , the action url is correctly created (i inspected code), but when i click on submit button , the action url open but without parameters.
i tried with method get, post but i have the some result, new url is opened but without parameter, only with "?" character. But if i do the same procedure with a link and href attribute, it works fine. I noticed that parameter not arrive to doget function
include file index.html and app.gs
<form id="myForm" action="#">
<div class="form-row">
<div class="col">
<input type="text" class="form-control" placeholder="Numero" id="numero">
</div>
<div class="col">
<div class="form-group">
<select id="tipologia" class="form-control">
<option selected>Tipologia supporto</option>
<option >concorsi</option>
</select>
</div>
</div>
</div>
<button type="submit" class="btn-get-started scrollto">Richiedi assistenza Form</button><!--NOT WORK-->
</form>
Richiedi assistenza<!--WORK-->
<script>
document.getElementById("tipologia").addEventListener("change", redirect);
function redirect(){
var nome =document.getElementById("nome").value;
var numero =document.getElementById("numero").value;
var opzioni = document.getElementById("tipologia");
var selezionato = opzioni.options[opzioni.selectedIndex].value;
document.getElementById("btn").href="https://script.google.com/macros/s/AKfycbxia-_rMYlvVjrlyGGd7zRcb1CD5hSYe6W-mLldzxY__8I2b3Q/exec?supporto="+selezionato+"&controllo="+numero+"&nome="+nome;
document.getElementById("myForm").action ="https://script.google.com/macros/s/AKfycbxia-_rMYlvVjrlyGGd7zRcb1CD5hSYe6W-mLldzxY__8I2b3Q/exec?supporto="+selezionato+"&controllo="+numero+"&nome="+nome;
}
</script>
function doGet(e) {
Logger.log(e.parameter.supporto);
var supporto = e.parameter.supporto;
var numero= e.parameter.controllo;
var nome= e.parameter.nome;
}
i don't understand why the parameters disappear
When you submit a form with method="GET" (the default), the URL specified in the action is used as a base.
The query string (which you added using JavaScript) is removed from it and it is replaced with one constructed with the data from the successful form controls.
Since none of your form controls have a name, none are successful, so there is no data. This gives you a blank query string.
Don't try to set the action with JavaScript (which you are doing incorrectly: The DOM action property expects a plain URL, not an HTML encoded one).
Just set the base URL in the action attribute and give the form controls names.
<form id="myForm" action="https://script.google.com/macros/s/AKfycbxia-_rMYlvVjrlyGGd7zRcb1CD5hSYe6W-mLldzxY__8I2b3Q/exec">
<div class="form-row">
<div class="col">
<input type="text" class="form-control" name="controllo" placeholder="Numero" id="numero">
</div>
<div class="col">
<div class="form-group">
<select id="tipologia" name="supporto" class="form-control">
<option selected>Tipologia supporto</option>
<option>concorsi</option>
</select>
</div>
</div>
</div>
<button type="submit" class="btn-get-started scrollto">Richiedi assistenza Form</button>
</form>
NB, your code includes var nome =document.getElementById("nome").value;
but there is no matching id in your HTML. This would cause your JS to error.

Modify ng-model from view

I´m pretty newbie on AngularJS, how can I update the value of my ng-model from my view?
Right now I have form and I´m updating app_key input, but when I call my module and I check the $scope.variable, the value has not change.
Here my code
<div>
<br>
<div class="field_row">
<label for="app_key">AppKey:</label>
<input id="app_key" type="text" ng-model="appKey"> --> this is the input that I´m changing the value
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" ng-click="selectUser()">
Select
</button>
</div>
And in my module I have the method selectUser
$scope.selectUser = function () {
$scope.setAppKey($scope.appKey); --> Here the appKey it´s not modified
};
Solution
I dont know why, but to make it works I need to pass the ng-model to the controller function
<input id="app_key" type="text" ng-model="appKey" ng-change="changeAppKey(appKey)">
I made this working JSFiddle using your code. It seems to work just fine. Are you sure your setAppKey function is correct ?
JS
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.selectUser = function () {
console.log($scope.appKey);
};
}
HTML
<div ng-controller="MyCtrl">
<div>
<div class="field_row">
<label for="app_key">AppKey:</label>
<input id="app_key" type="text" ng-model="appKey">
</div>
</div>
{{appKey}}
<div class="modal-footer">
<button type="button" class="btn btn-info" ng-click="selectUser()">
Select
</button>
</div>
</div>

Incorporate directive's functionality into Angular form validation

I have two questions that are related:
First: I have the following directive, who's purpose is to validate whether an input[type=file] is valid or not, however I have no idea how it does it least of all, what the actual code means, here it is:
angular.module('sccateringApp')
.directive('fileModel', ['$parse', function($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
Like I said, I have no idea what the above code actually does, the explanation I got from the forum where I copied that was that it validated an input type file. Is this correct? (So far I haven't been able to verify if it works or not since it doesn't work with the code I'm using at the moment to validate my forms).
Second: Having the form below, using angular form validation it doesn't allow the submit button to be clicked until the actual inputs inside the form match the validation rules (enter a name for the category, and the description should have a max length of 144 characters). I included the directive into the file input, however the actual ng-model for the form ignores the required in the input type file and just verifies the rules are met for the first two inputs.
Here is my form:
<form method="post" role="form" name="newCategoryForm" ng-submit="submitForm()" enctype="multipart/form-data" novalidate>
<div class="row">
<div class="row">
<div class="col s12">
<div input-field>
<input type="text" name="cat-name" id="cat-name" ng-class="{ 'ng-invalid' : newCategoryForm.catname.$invalid && !newCategoryForm.catname.$pristine }"
ng-model="catname" required>
<label>Nombre</label>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
<div input-field>
<textarea class="materialize-textarea" name="cat-description" id="cat-description" length="144"
ng-model="catdescription" ng-maxlength="144" required></textarea>
<label>Descripción</label>
</div>
</div>
</div>
<div class="row">
<div class="col s12">
<h6>Imagen de Fondo</h6>
<div class="file-field input-field">
<div class="btn pink darken-2 waves-effect waves-light">
<span>Archivo</span>
<input type="file" name="cat-bgimg" id="cat-bgimg"
file-model="variable" required>
</div>
<div class="file-path-wrapper">
<input class="file-path" type="text">
</div>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-large pink darken-2 waves-effect waves-light center-button" ng-disabled="newCategoryForm.$invalid">Crear Categoría</button>
</form>
The first two inputs get validated correctly, the third one (file input) doesn't and I don't really know why since the directive got included on the input (I know natively, ngModel doesn't validate file inputs).
Any ideas or suggestions of how can I fix this? I'm really new to Angular, and all the tutorials are pretty much useless. I come from 5 years of experience working on jQuery, and the transition to Angular hasn't been easy at all.
The directive posted above is used to make the submit get the data found in the <input type="file"></input>.
Also, a variable should be initialized in the controller so that the values found inside the form are copied to said variable, then this variable needs to be sent as a parameter inside the ng-submit="submitForm().
Example:
angular.module('sccateringApp')
.controller('newSubcategoryController', function (httpcalls, $scope) {
...
$scope.subcategory = [];
...
$scope.submitForm = function(subcategory){
...
$scope.request.insertSubcategory(subcategory);
}
});
Each ng-model inside the form would be:
<input type="text" ng-model="category.name">
So that the category variable found in the controller acquires that value.

$location.path does not change the route after form submit

I have seen many issues regarding the $location.path in angular js on stackoverflow but none of them would solve the issue I have.
I have defined below routes in my app.js:
angular.module(
'hotPosts',
[ 'hotPosts.filters', 'hotPosts.services',
'hotPosts.directives',
'hotPosts.controllers', 'ngRoute', 'ngSanitize' ]).config(
[ '$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider.when('/hot-posts', {
templateUrl : 'gptarin/partials/hot-posts.jsp',
controller : 'ListPostsCtrl'
});
$routeProvider.when('/list-users', {
templateUrl : 'gptarin/partials/list-users.jsp',
controller : 'ListUserCtrl'
});
$routeProvider.when('/edit-user/:userId', {
templateUrl : 'gptarin/partials/edit-user.jsp',
controller : 'EditUserCtrl'
});
$routeProvider.when('/create-user', {
templateUrl : 'gptarin/partials/edit-user.jsp',
controller : 'EditUserCtrl'
});
$routeProvider.otherwise({
redirectTo : '/hot-posts'
});
$locationProvider.html5Mode(false);
} ]);
The list-users.jsp partial will show a list of users where I can select a record to update. When I click ong update button the ngRoute successfully routes the app to edit-user.jsp partial. However, when I click the Save button in that page, it does not change the route to "/list-users", even though I used the $location.path('/list-users') in Controller EditUserCtrl. It redirects me to "[app_url]/?".
Here is my controllers:
app.controller('EditUserCtrl', [
'$scope',
'$routeParams',
'UserListFactory',
'UserFactory',
'$location',
function($scope, $routeParams, UserListFactory, UserFactory,
$location) {
// callback for ng-click 'updateUser':
// force it to refresh in the client
$scope.saveUser = function() {
if (angular.isUndefinedOrNull($scope.user)) {
$scope.user = {};
UserListFactory.create($scope.user, function() {
$location.path('/list-users');
});
} else if (angular.isUndefinedOrNull($scope.user.id)) {
UserListFactory.create($scope.user, function() {
$location.path('/list-users');
});
} else {
UserFactory.update($scope.user, function() {
$location.path('/list-users');
});
}
};
// callback for ng-click 'cancel':
$scope.cancel = function() {
$location.path('/list-users');
};
if ($routeParams.userId !== undefined) {
$scope.user = UserFactory.show({
userId : $routeParams.userId
});
}
} ]);
The update function (saveUser) uses a service which makes Restful requests to the backend server via ngResource. The service works fine (all tests are passed).
I have enclosed the $location.path in the success callback function when calling the resource actions.
I have tried catching "$locationChangeSuccess" and "$locationChangeError" and saw that in this case a $locationChangeError is thrown but I do not know which promise object is rejected causing this error.
Any help is highly appreciated.
Edit:
Here is the edit-user.jsp partial
<div class="container-fluid">
<div class="panel panel-primary">
<div class="panel-heading">
<h2 class="panel-title label">Edit User</h2>
</div>
<div class="panel-body">
<form role="form" action="#" class="form-horizontal" ng-submit="saveUser()">
<div class="form-group col-sm-11">
<div class="form-group">
<label for="accountId" class="col-sm-1 control-label">G+
Id: </label>
<div class="col-sm-4">
<input type="text" id="accountId" class="form-control"
ng-model="user.gpId"></input>
</div>
<label for="accountName" class="col-sm-2 control-label">Name:
</label>
<div class="col-sm-5">
<input type="text" id="accountName" class="form-control"
ng-model="user.name"></input>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<input type="checkbox" id="showPosts" class="form-control"
ng-model="user.listPosts">ShowPosts</input>
</div>
</div>
</div>
<div class="form-group col-sm-1">
<div class="row">
<div class="col-sm-offset-1 col-sm-12 pull-right">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
<div class="row">
<div class="col-sm-offset-1 col-sm-12 pull-right">
<button type="button" class="btn btn-primary" ng-click="cancel()">Cancel</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
Well After a few days of trying everything and not finding any help over internet I sort of fixed this issue.
I decided to share it with whoever reads this post so that it does not take several days of them as well.
When I traced my app more carefully I figured out that my cancel button worked just fine and $location.path was successfully sending me back to the /list-users page.
Further investigations showed that the difference between my Cancel and Save buttons was that the Cancel button uses the ng-click whereas I defined the type of my Save button to be "submit".
I then changed my html code so that instead of providing the function call saveUser() in ng-submit of the form, I used an ng-click for the Save button and changed its type to "button".
Here is the working version of my html partial. I did not need to change anything in js files.
<form role="form" action="#" class="form-horizontal">
<!-- ng-submit="saveUser()"> -->
<div class="form-group col-sm-11">
<div class="form-group">
<label for="accountId" class="col-sm-1 control-label">G+ Id: </label>
<div class="col-sm-4">
<input type="text" id="accountId" class="form-control" ng-model="user.gpId"></input>
</div>
<label for="accountName" class="col-sm-2 control-label">Name: </label>
<div class="col-sm-5">
<input type="text" id="accountName" class="form-control" ng-model="user.name"></input>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<label>
<input type="checkbox" id="showPosts" ng-model="user.listPosts"> Show Posts
</label>
</div>
<div class="col-sm-12">
<button type="button" class="btn btn-primary" ng-click="saveUser()">Save</button>
<button type="button" class="btn btn-primary" ng-click="cancel()">Cancel</button>
</div>
</div>
</div>
</form>
I still still do not now the mechanics of form submit in angular and why it causes one of the promise objects that the $location.path expects to fail (and hence causing an error in routing).
Any clarifying comment in this regard is much appreciated.
Well after more than a year of experience with Angular, I now know what was the problem in the first instance.
Reading Angular's documentation for ng-submit I found out this:
Additionally it prevents the default action (which for form means sending the request to the server and reloading the current page), but only if the form does not contain action, data-action, or x-action attributes.
Looking at the code it is evident that I mistakenly added an action attribute when I was defining the form element:
<form role="form" action="#" class="form-horizontal" ng-submit="saveUser()">
Removing it will fix the problem:
<form role="form" class="form-horizontal" ng-submit="saveUser()">
you can add $event.preventDefault(); before saveUser(); it will works.

Categories