AngularJs - Input text box is not populated by the initial value id - html

I really hope you can help me with this. I am having trouble showing the ID population the textbox when I modify the form.
Let me explain it to you in detail
This is my list:
myList = [{ 'id':100, 'name': 'test mission 1' },
{ 'id':102, 'name': 'test mission 2' },
{ 'id':103, 'name': 'test mission 3' },
{ 'id':104, 'name': 'test mission 4' },]
This is my HTML with a workable typeahead function.
<input type="text"
ng-model="selected"
typeahead="mission as mission.name for mission in myList | filter:{name:$viewValue} | limitTo:8"
name="mission_name"
disabled>
<input type="hidden"
name="mission_id"
ng-model="selected"
value="{{field.value}}">
so whatever name value you type from the textbox naming mission_name, the id of that name will automatically populate the textbox naming mission_id. This is already workable and this is not the real problem. Because the only value that I want to save to the database is the mission_id. Let me show you my controller first.
This is my controller
angular.module('actinbox.web').controller('TypeaheadCtrl_{{ field.id_for_label }}', function($scope) {
$scope.selected = "{{ mission.name }}";
$scope.myList = {{ Mission|safe }};
});
The problem is, when I want to modify this data and i go to the form, I can only see that mission_name is populated by the data, it must be because i put an initial value such as $scope.selected = "{{ mission.name }}". However in mission_id textbox, the initial value is also the same as the value of mission_name maybe bacause of ng-model. What I want to do is to see the mission_id value and not the mission_name value.
I hope my explanation is clear. I really need help to this.

Try chaning this
<input type="hidden"
name="mission_id"
ng-model="selected"
value="{{field.value}}">
to
<input type="hidden"
name="mission_id"
ng-model="selected1"
value="{{field.value}}">
Then in js
$scope.selected = "{{ mission.name }}";
$scope.selected1 = "{{ mission.id }}";
$scope.myList = {{ Mission|safe }};

Related

Datalist input showing options from another Vue component

I'm quite new with Vue, and webdev in general. Am creating a simple hiking pack list as a school project. Have a component 'PackItem' that consists of input fields for brand, model and weight. In the backend I have a database with some items that the user can pick from. When selecting a brand, the model input will show a list of models for that brand, and when a model is chosen the weight input gets filled automatically.
<template>
<div class="inputRow">
<h1> Brand: {{ this.brand }}</h1>
<input
type="text"
list="brands"
placeholder="Brand"
v-model="this.brand"
/>
<datalist id="brands">
<option
v-for="(suggested_brand, index) in getProductBrands"
:key="index"
:value="suggested_brand"/>
</datalist>
<input
type="text"
list="models"
v-model="this.model"
/>
<datalist id="models"> -->
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"/>
</datalist>
<input
class="product-inputs"
type="number"
name="Weight"
placeholder="Weight"
v-model="this.getProductWeight"
#change="this.updateItemOnStore($event.target.value);"/>
</div>
</template>
This is the component. The datalists and input gets their values from computed properties, that talk to vuex.
export default defineComponent({
name: 'PackItem',
props: {
},
data() {
return {
model: '',
brand: '',
weight: 0,
}
},
...
...
...
computed: {
getProductBrands(){
return this.$store.getters.productsBrandList
},
getProductWeight(){
let weight = this.$store.getters.productWeight(this.brand, this.model)
return weight
},
brandModels(){
if(this.brand === ''){
return []
}
console.debug('Item %d getting models for %s', this.uuid, this.brand)
let models = this.$store.getters.brandModels(this.brand)
return models
},
},
}
This all works great for the first PackItem, but when i spawn a second PackItem from the parent, the models list for the new PackItem will be the same as the first PackItem, regardless of choosing a different brand.
Image: One item spawned
Image: Two items spawned
If I change computed property brandModels() to a method, and save the returning array from the store in local data and print it, I can see that the model list looks like it should, but those values are not showing up in the list for the user?
Image: Two items spawned with printouts
I can also see in the logs that I'm getting the correct models for the selected brand.
Item 0 getting models for Hyperlite Mountain Gear
index.js:209 Hyperlite Mountain Gear models: Windrider 3400,Junction 3400
index.js:221 Got weight 907g for product Hyperlite Mountain Gear Windrider 3400
index.js:64 Creating item 1 in category 0
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
index.js:225 Did not find weight for product in database!
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
PackItem.vue:119 Item 1 getting models for ZPacks
index.js:209 ZPacks models: Arc Blast
index.js:225 Did not find weight for product ZPacks Windrider 3400 in database!
So seems to me like I'm fetching all the correct data, and as far as I can see it should be displayed in the browser, but for some reason is not. I have no idea what is going on here...
EDIT:
As suggested by #yoduh I've fetched the whole products table into the component, and do computed values there instead of the supposedly broken computed values from getters in vuex. This has unfortunately not fixed it. Can still see in logs that brandModels() creates a correct list of models, but the datalist still shows the wrong ones.
computed: {
getProductBrands(){
let brands = []
this.dbProducts.forEach(product => {
//var json = JSON.parse(product);
var brand = product.brand;
if(!brands.includes(brand)){
console.debug('Adding brand %s to brands list', brand)
brands.push(brand)
}
} )
console.log('Brands list: %s', brands)
return brands
},
brandModels(){
if(this.brand === '') {return }
let models = []
this.dbProducts.filter(
product => product.brand === this.brand)
.forEach(product => models.push(product.model)
)
console.debug('%s models: %s', this.brand, models)
return models
},
getProductWeight(){
if(this.brand === '' || this.model === ''){ return }
let product = this.dbProducts.find(
product => (
product.brand === this.brand && product.model == this.model
))
if(product){
let weight = product.weightGrams
console.debug('Got weight %dg for product %s %s', weight, this.brand, this.model)
return weight
}
console.debug('Did not find weight for product %s %s in database!', this.brand, this.model)
return 0
},
},
Turns out the actual issue was with the pair of elements <InputText> and <datalist> on every item having the same id and list values. The value for list and id connect the two elements together, but once a second item is added, another pair of <InputText> and <datalist> with the same id and list values is created. Because no two elements should ever share the same id, the connection between inputs and datalists becomes confused and broken.
The solution then is to bind unique values to the id/list attributes. I used the uuid of each item since it's a unique number:
PackItem.vue
<InputText
type="text"
:list="`models${uuid}`"
placeholder="Model"
class="p-inputtext"
v-model="this.model"
/>
<datalist :id="`models${uuid}`">
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"
/>
</datalist>
updated codesandbox

Passing Django variables to forms in templates

I'm building a website and I'm trying to make the user able to change his credentials. I've made a form for this:
newUsername = forms.CharField(
label="Enter your new username here*",
required=True,
widget=forms.TextInput(attrs={
'class': 'userChangeCredentials',
'value': "{{ user.username }}"
}))
newEmail = forms.EmailField(
label="Enter your new e-mail adress here*",
required=True,
widget=forms.EmailInput(attrs={
'class': 'userChangeCredentials',
'value': '{{ user.email }}'
}))
newPassword = forms.CharField(
label="Enter your new password here",
required=False,
widget=forms.PasswordInput(attrs={
'class': 'userChangeCredentials',
'value': ''
}))
passwordConfirmation = forms.CharField(
label="Confirm your existing password here*",
required=True,
max_length=256,
widget=forms.PasswordInput(attrs={
'class': 'userChangeCredentials',
'value': ''
}))
The problem is, that the values in the widget dictionary are passed as raw text and I want them to be variables.
This is the resulting page layout:
Do I need to change something inside of HTML?
My HTML:
<form id="UserForm" action="{% url 'user' name=user.username %}" method="POST">
{% csrf_token %}
{{ form|safe }}
<input class="userChangeCredentials" type="submit"></input>
</form>
I tried to make the text raw like this:
widget=forms.EmailInput(attrs={
'class': 'userChangeCredentials',
'value': r'{{ user.email }}'
})
But it didn't help. I searched for a week and I couldn't find any questions of this nature. I've read the official Django form page, but there is nothing about this exact thing.
In this case, I suggest you use the form for the user credentials only (email and username), then use the built-in PasswordChangeView for updating the user password without even creating a form class for it.
As for the raw values, the best way to get them is to inherit the form from the User model.

Saving and displaying "nested" JSON data from API

I have the following format of JSON data fetched from an API and stored in IntentList
{
"id": 22,
"name": "IntentName",
"fk_app": 3,
"fk_intent": null,
"nlu_models": [],
"sentences": [
{
"text": "text1",
"id": 2308
},
{
"text": "text2",
"id": 2309
},......
So there are these levels : the first having "name" and "sentences", and the second which is inside sentences, having "text".
My goal is to be able to search the API data By text and to display on each row the name and text found inside the sentences related to that text.
Therefore if I search "text" this would appear => IntentName text1 IntentName text2
if I search text1 this would appear => IntentName text1
-----What I have done/tried so far-----
I searched for corresponding text and stored its intent object and was only able to display its intent name
Typescript:
fetchIntentsTxt(event: any) {
if (event.target.value === '') {
return this.searchResultINTxt = [];
}
this.searchResultINTxt = this.IntentList.filter(
i => i.sentences.some(
s => s.text.toLowerCase().includes(event.target.value.toLowerCase())));
}
Html:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (keyup)="fetchIntentsTxt($event)"
class="form-control" id="search0" placeholder="Search for Intents by Text">
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt" >{{intentTxt.sentences.text(this doesn't work obviously)}}<span
class="float-right">{{intentTxt.name}}</span> <br></a>
Any recommendation is appreciated, even if it meant changing the search function. Thanks in advance.
Example: https://stackblitz.com/edit/angular-ivy-pyaq7a
I would just create a new array in the fetchIntentsTxt method and add the data I want to display by the user input/search. After "filtering" all the data I need I set it to the variable thats iterated over in the View/Template.
public fetchIntentsTxt(searchValue: string): void {
const searchResults = [];
for (const entry of this.intentList) {
for (const sentence of entry.sentences) {
if (sentence.text.toLowerCase().includes(searchValue)) {
searchResults.push({
name: entry.name,
text: sentence.text,
});
}
}
}
this.searchResultINTxt = searchResults;
}
View:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (ngModelChange)="fetchIntentsTxt($event)" class="form-control" id="search0" name="search0" placeholder="Search for Intents by Text"/><br />
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt">
{{ intentTxt.text }}<span class="float-right">{{ intentTxt.name }}</span>
<br/>
</a>
Also note here, I used the (ngModelChange) instead of the (keyup) on the search input so I do not need to hassle around with the events and just get the value I need for filtering.

Display label displayed in options as the title of select

How to show option.Brand.Name as the title of the select field without using java script and changing the ng-model?
<select ng-model="detail.BrandId" title="" class="form-control" disabled>
<option ng-repeat="option in mainCtrl.products" ng-selected="option.Id === detail.ProductId" ng-value="option.BrandId">{{option.Brand.Name}}</option>
</select>
AngularJS and select-options
Try using ng-options AngularJS ngOptions directive within select element itself. Then you don't need to add each option element yourself using ng-repeat.
Clarification
The title-attribute belongs to the select-element and will show if you hover over the select. You would like the title to reveal the current selected option? Did I understand you correctly?
How to show option.Brand.Name as the title of the select field
Curious, where this detail.ProductId comes from? Is the brand preselected by product-id (see your code)?
ng-selected="option.Id === detail.ProductId"
Solution space
Your requirements/restrictions are:
without using JavaScript (maybe because you can't change the sources)
without changing the ng-model (because you need there only the BrandId for some database-reasons)
So since the title of the select-element has no access to the options inside, the only way to set it is depending on the current selection, i.e. your detail.BrandId. So the title can only set dynamically (depending on the current selection) by using standard-angularJS means, as:
{{ expression }} expressions
{{ expression | filter }} array-filter
Expected behavior
The only scope-variable changed by selecting is specified within select's ng-model as detail.BrandId. This will be set when user selects an option to its property BrandId. When user selects between options they will be visible with ther BrandName as label. After selection this BrandName (label of the option) should be shown as title of the entire select element.
So we need to get from detail.BrandId (selected ng-model) to related options BrandName (as this should show as title).
Possible Solution
Only way is to use standard angular expressions/filters/array-indexing to get the whole option by the selected detail.BrandId (ng-model)
Then we can lookup the option.BrandName by this equation after selected detail.BrandId === option.BrandId
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope){
$scope.products = [
{Id: 0, name: 'Watson', brandId: 1, brandName:"IBM"},
{Id: 1, name: 'DB2', brandId: 1, brandName:"IBM"},
{Id: 2, name: 'Windows', brandId: 2, brandName: "Microsoft"},
{Id: 3, name: 'Office', brandId: 2, brandName: "Microsoft"}
];
$scope.detail = { ProductId: 3, BrandId: null };
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<!DOCTYPE html>
<html>
<body data-ng-app="app" data-ng-controller="mainCtrl">
<table border="1">
<tr>
<th>Product Id</th><th>Product Name</th><th>Choose Brand</th><th>Brand Id</th>
</tr>
<tr>
<td>{{detail.ProductId}}</td>
<td>{{ (products | filter: {Id: detail.ProductId})[0].name }}</td>
<td>
<select class="form-control"
ng-model="detail.BrandId"
ng-init="detail.BrandId = (products | filter: {Id: detail.ProductId})[0].brandId"
ng-options="o.brandId as ('['+ o.Id +'] '+ o.name +' : '+ o.brandName +' ('+ o.brandId +')') for o in products"
title="{{ (products | filter: {brandId: detail.BrandId})[0].brandName}}"
>
<!-- default option when not preset by detail.ProductId-->
<option value="">-- please choose brand --</option>
</select>
</td>
<td>{{detail.BrandId}}</td>
</tr>
</table>
<hr/>
<p>Product is predefined. So the brand is pre-selected by product. BUT: after brand is selected, the product-details do NOT change!</p>
Selected <strong>detail</strong>:
<pre ng-model="selected">{{detail | json}}</pre>
</body>
</html>
See also
For using ng-options, see also plunkr example.
You can register the selected option object in the ng-repeat parent scope by using as alias-expression provided by ng-repeat.
In your case you just need to do something like that:
<select ng-model="detail.BrandId"
title="{{options | selectedProductFilter : detail.ProductId}}"
class="form-control"
disabled>
<option ng-repeat="option in mainCtrl.products as options"
ng-selected="option.Id === detail.ProductId"
ng-value="option.BrandId">
{{option.Brand.Name}}
</option>
</select>
The options object will be available in your controller closure and you can display the title by using a custom filter.
angular.module("app").filter('selectedProductFilter',
function () {
return function (input, id) {
if (!input) return "";
let occurence = input.filter(function (x) {
return x.Id == id;
});
return occurence.length > 0 ? occurence[0].Brand.Name: "";
}
}
);
you need to do ng-change event in your select and call function in it that change the value of label text to the select value name. something like below
In Html
ng-change="changedValue(detail.BrandId)"
In JS
$scope.changedValue = function(item) {
//change label name here
}
fill ng-model by "option" not "option.BrandId"
then you can set title like this :
mainCtrl.products['ng-model-name'].Brand.Name
Here's how you could achive this:
(function () {
"use strict";
const app = angular.module("app", []);
app.controller("app.AppCtrl", $scope => {
$scope.selectedOption = null;
$scope.optionList = [{_id: 1, label: 'Option 1'}, {_id: 2, label: 'Option 2'}];
});
})();
body {
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app" ng-controller="app.AppCtrl">
<select title="{{selectedOption.label}}" class="form-control" ng-model="selectedOption">
<option ng-repeat="option in optionList" ng-value="option"> {{option.label}}</option>
</select>
</div>
Try using ng-init,
add ng-init to your select tag and put your object index value you want to be selected by default.
e.g.
Your code
<select ng-model="detail.BrandId" title="" class="form-control" disabled>
<option ng-repeat="option in mainCtrl.products" ng-selected="option.Id === detail.ProductId" ng-value="option.BrandId">{{option.Brand.Name}}</option>
</select>
adding following code (Suppose I want index 0 by index):
ng-init="detail.BrandId = option[0].Brand.Name"
It will look like this :
<select ng-model="detail.BrandId" ng-init="detail.BrandId = option[0].Brand.Name" title="" class="form-control" disabled>
<option ng-repeat="option in mainCtrl.products" ng-selected="option.Id === detail.ProductId" ng-value="option.BrandId">{{option.Brand.Name}}</option>
</select>
or Check these thread's
how to use ng-option to set default value of select element
How to set default value in ng-options

MeteorJS: How to get id to load from collection

I'm trying to load an array (with simple text) and trying to load it up on the template whenever it is called. How do I get the ID from that specific item to get the array that I stored in it?
HTML Template:
<template name="commentMarker">
<div id="viewMarker">
<h3 id="markerTitle">{{markerName}}</h3>
<h6 id="markerCategory">{{markerCategory}}</h6>
<br>
<fieldset>
<legend>Description</legend>
<p>{{markerDescription}}</p>
</fieldset>
<form id="commentForm">
<fieldset>
<legend>Comments</legend>
<input type="text" id="markerId" name="idForComment" value={{markerId}}>
<textarea rows="3" cols="19" name="comment" id="commentArea" placeholder="Insert your comment here..."></textarea>
{{#each comments}}
<p id="oneComment">{{this}}</p>
{{/each}}
</fieldset>
<input type="submit" value="Comment" class="commentButton">
<input type="submit" value="Close" class="exitButton">
</form>
</div>
</template>
JS:
Template.commentMarker.helpers({
comments(){
alert(template.find("#markerId").value);
if(commentArray.length===0) return;
else return commentArray;
}});
This is where I insert the comment into the collection's item and it's working fine
Template.commentMarker.events({
'click .commentButton': function(e, template){
e.preventDefault();
var id = template.find("#markerId").value;
var comment = template.find("#commentArea").value;
Points.update(id, { $push: { comments: comment }});
commentArray = Points.findOne(id).comments;
template.find("#commentArea").value = ' ';
}
I tried with commentArray as a global variable which still is. But I'm at loss how I can get the Id from that specific item, I even put it's Id (with hidden display) in the form to actually be able to insert the comment. But it doesn't help me with showing the comments because I cannot seem to get to this field in the Template.helpers ...
Not entirely sure what you are trying to do. It's almost like as if you are displaying the comments right after you updated in to the collection. It looks like you are doing this entirely on local and not a online collection.
However, storing it as a session would work...or reactive var. Might not be the best solution tho. Basically replace commentArray = Points.findOne(id).comments; with:
Session.set('comments', Points.findOne(id).comments)
Then to get it out in helpers:
let commentArray = Session.get('comments')
It's not safe to use it all the time tho for sensitive data. Also try catch the findOne(id).comments because it does produce errors if it happen to not find it.
NOTE: If you are going to use Meteor.Methods, you cannot use Session. You have to return the id and find it in your helpers.