I am writing a form, in that I required dynamic elements which will come from a json script.
here is a sample.
<div ng-repeat='element in elements'>
<input type="button" ng-click="{{element.clickedAction}}" />
</div>
But this ng-click is not updating as in json script. How to proceed?
Thanks in advance.
<div ng-repeat='element in elements'>
<input type="button" ng-click="element.clickedAction()" />
</div>
Presuming that clickedAction is a function on your element in the elements array.
$scope.elements = [
{
clickedAction: function () { /* do something */ }
},
{
clickedAction: function () { /* do something */ }
},
{
clickedAction: function () { /* do something */ }
}
];
This is an unusual scenario though. More often people will be doing something like this:
$scope.doSomething = function(item) {
item.name += '!';
};
$scope.items = [ { name: 'test' }, { name: 'foo' }, { name: 'bar' } ];
<div ng-repeat="item in items">
<button ng-click="doSomething(item)">Click Me</button>
</div>
Related
Currently i am using buefy autocomplete.But there are a few issues with it.
DepartmentDetail.vue
<template slot-scope="props">
<div class="container is-fluid">
<b-loading :is-full-page="true" :active.sync="this.isLoading"></b-loading>
<b-field label="Business Unit">
<b-autocomplete
:data="dataBusinessUnit"
placeholder="select a business unit..."
field="businessUnit"
:loading="isFetching"
:value="this.objectData.businessUnit"
#typing="getAsyncDataBusinessUnit"
#select="(option) => {updateValue(option.id,'businessUnit')}"
>
<template slot-scope="props">
<div class="container">
<p>
<b>ID:</b>
{{props.option.id}}
</p>
<p>
<b>Description:</b>
{{props.option.description}}
</p>
</div>
</template>
<template slot="empty">No results found</template>
</b-autocomplete>
</b-field>
</div>
</template>
Function that fetches the results based on user input-
getAsyncDataBusinessUnit: debounce(function(name) {
// console.log('getAsyncDataBusinessUnit you typed'+name);
if (!name.length) {
this.dataBusinessUnit = [];
return;
}
this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/${name}`)
.then(response => {
this.dataBusinessUnit = [];
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500),
So instead of using the buefy's b-autocomplete i want to create and use my own autocomplete component.
So i went ahead and created my own autocomplete component and called it from the DepartmentDetail vue page like this-
DepartmentDetail.vue
<b-field label="Custom Business Unit ">
<AutoComplete :method="getAsyncDataBusinessUnit" title='businessUnit' :autocompleteData="dataBusinessUnit" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
AutoComplete.vue
<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="this.objectData[this.title]" #input="getAsyncDataBusinessUnit"/>
<ul v-show="isFetching" >
<li v-for="(dataBusinessUnit, i) in dataBusinessUnit" :key="i" #click="setResult(dataBusinessUnit)" >
<!-- {{ autocompleteData }} -->
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>Description:</b>
{{dataBusinessUnit.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>First Name:</b>
{{dataBusinessUnit.firstName}}
</p>
<p>
<b>Last Name:</b>
{{dataBusinessUnit.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import schemaData from '../store/schema';
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "AutoComplete";
var passedview;
export default {
name: "AutoComplete",
props: {
method: {
type: Function
},
title: String,
viewname:String,
autocompleteData: {
type: Array,
required: true
}
},
data() {
return {
// results: [],
dataBusinessUnit: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
viewData() {
return this.$store.getters.getViewData('DepartmentDetail')
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
},
methods: {
setResult(result) {
this.updateValue(result.id,this.title);
// localStorage.setItem(this.title,result.id );
this.isFetching = false;
},
updateValue(newValue, fieldName) {
var val;
var schema = schemaData[this.viewData.schema];
if(typeof schema!=='undefined' && schema['properties'][fieldName]['type'] == 'date'){
val = this.formatDate(newValue);
} else {
val = newValue;
}
this.$store.dispatch('updateDataObjectField', {
key: this.viewData.api_id,
field: fieldName,
value: val
});
},
getAsyncDataBusinessUnit: debounce(function(name) {
console.log('getAsyncDataBusinessUnit you typed'+name.target.value);
if (!name.target.value.length) {
this.dataBusinessUnit = [];
this.isFetching = false;
return;
}
// this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/${name.target.value}`)
.then(response => {
this.dataBusinessUnit = [];
if (!response.length)
{
console.log('inside if')
this.isFetching = false;
}
else{
console.log('inside else')
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
this.isFetching = true;
}
console.log('length of dataBusinessUnit is '+(this.dataBusinessUnit).length)
console.log('contents of dataBusinessUnit array '+JSON.stringify(this.dataBusinessUnit))
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
// this.isFetching = true;
});
}, 500),
},
components: {
}
};
</script>
The issue iam facing is whenever i start to type anything into the Custom Business Unit input field, then immediately after 1-2 seconds the value gets reset(with the value coming from the store getters ).This however does not happen if i remove the line this.dataBusinessUnit = []; in the getAsyncDataBusinessUnit function . Why is this happening? I even tried using :input instead of v-model for the input field , but i am still facing the same issue.And also second issue is when i click an existing record under DepartmentDetail page, the value that should be set for the custom business unit input field coming from the store getters (this.objectData.businessUnit) is not showing up sometimes? please help
The basic logic of an autocomplete is not really hard:
Vue.component('Autocomplete', {
props: ['list'],
data() {
return {
input: null
}
},
template: `<div><input v-model="input" #input="handleInput"><div class="bordered" v-if="input"><ul><li v-for="(item, i) in list" :key="i" #click="setInput(item)">{{ item }}</li></ul></div></div>`,
methods: {
handleInput(e) {
this.$emit('input', e.target.value)
},
setInput(value) {
this.input = value
this.$emit('input', value)
}
}
})
new Vue({
el: "#app",
computed: {
filteredList() {
if (this.filterInput) {
return this.list.filter(e => e.toLowerCase().indexOf(this.filterInput.toLowerCase()) !== -1)
} else {
return this.list
}
}
},
data: {
filterInput: null,
list: [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Seventh"
]
},
methods: {
handleInput(e) {
this.filterInput = e
}
}
})
.bordered {
border: 1px solid black;
display: block;
}
ul li {
cursor: pointer;
}
ul li:hover {
background: rgba(0, 0, 0, 0.3)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<autocomplete :list="filteredList" #input="handleInput" />
</div>
I wouldn't handle the data in the presentational component (autocomplete is a presentational component in the structure I created), but in its parent-container. That way the autocomplete just gets a list as a prop and displays it; every action is $emitted to the parent, who does the data-handling.
It's easier to control displayed data this way - even with an async data source.
I have to call a POST function, and need to compress the data in web form into an object in advance. I use ng-repeat to display all data input, and init this value for input, but I have no idea how to use ng-model to save the data.
I can resolve this case by setting an dynamic id for input, and using JQuery to get data, but it's not a good solution. And I want use AngularJS model only.
Please give me an advice. Below is my code:
Click here to see the screenshot of UI form
html file
<!-- edit-user-page-view -->
<div spectrum-error-container
data-scope-id="$id"
class="col-sm-12"></div>
<div class="email-preference-block">
<form name="editUserForm"
id="editUserForm"
role="form"
class="prj-form prj-create-user-form">
<fieldset id="fieldsetAccountInformation">
<legend>{{ 'user.userPage.form.fieldsets.accountInformation' | translate }}</legend>
<div class="row language-preference-block">
<div class="form-group">
<div class="col-md-12 col-sm-12">
<label data-translate="user.userPage.form.preferredLanguage"></label>
</div>
<div class="col-md-12 col-sm-12">
<label data-ng-repeat="option in preferencesData.languageOptionList"
class="radio-inline">
<input type="radio"
ng-model="emailNotificationModel.selectedLanguageValue"
value="{{ option.value }}"> {{ option.label | translate }}
</label>
</div>
</div>
</div>
<div class="row user-preference-block">
<table data-ng-repeat="preferenceItem in preferencesData.userNotificationPreferencesList"
class="table table-bordered table-striped user-preference-table">
<thead>
<tr>
<th>{{ preferenceItem.label | translate }}</th>
<th>{{ 'organisation.mediaPreference.selected' | translate }}?</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="mediaOption in preferenceItem.mediaOptionList">
<td class="first-column">
{{ mediaOption.label | translate}}
</td>
<td class="second-column">
<input type="checkbox"
id="inputStatus1-{{ mediaOption.id }}"
value="{{ mediaOption.id }}"
ng-checked="mediaOption.userChoice"
data-ng-click="clickHandler.checkValue(mediaOption.id)">
</td>
</tr>
</tbody>
</table>
</div>
<div class="prj-form-actions"
data-spectrum-style-affix
data-threshold="150"
data-css-class="sdx-sticky-container">
<button id="selectAllButton"
class="btn btn-default"
type="button"
data-ng-click="clickHandler.selectAll()"
data-translate="global.buttons.selectAll">
</button>
<button id="deselectAll"
class="btn btn-default"
type="button"
data-ng-click="clickHandler.deselectAll()"
data-translate="global.buttons.deselectAll">
</button>
<button id="saveBtn"
class="btn btn-default"
type="button"
data-ng-click="clickHandler.saveButton()"
data-translate="global.buttons.save">
</button>
<button id="cancelBtn"
class="btn btn-default"
type="button"
data-ng-click="clickHandler.deselectAll()"
data-translate="global.buttons.cancel">
</button>
</div>
</fieldset>
</form>
</div>
<div class="clearfix"></div>
<!-- / edit-my-account-page-view -->
controller file:
angular.module(
'EditUserPreferencesPageControllerModule',
[]
).controller(
'EditUserPreferencesPageController',
[
'$scope',
'$location',
'$routeParams',
'$filter',
'$window',
'$modal',
'PageTitleModel',
'SpectrumPageHeaderModel',
'AccountModel',
'UserModel',
'OrganisationService',
'RolesModel',
'MediaPreferencesModel',
'UserService',
'EmailNotificationService',
'EmailNotificationModel',
'SpectrumErrorModel',
'SpectrumHATEOASHelper',
function ($scope,
$location,
$routeParams,
$filter,
$window,
$modal,
PageTitleModel,
SpectrumPageHeaderModel,
AccountModel,
UserModel,
OrganisationService,
RolesModel,
MediaPreferencesModel,
UserService,
EmailNotificationService,
EmailNotificationModel,
SpectrumErrorModel) {
var impersonateUserCode = AccountModel.account.impersonatedUserCode,
userCode = impersonateUserCode ? impersonateUserCode : $routeParams.userCode;
$scope.mediaPreferencesModel = MediaPreferencesModel;
$scope.userModel = UserModel;
$scope.emailNotificationModel = EmailNotificationModel;
$scope.rolesModel = RolesModel;
$scope.statusList = [];
$scope.relationshipNotificationList = [];
$scope.auditNotificationList = [];
$scope.testListMediaValue = [];
$scope.preferencesData = {};
$scope.pleaseSelect = $filter('translate')('global.placeholders.pleaseSelect');
function initialise() {
PageTitleModel.setTitle('user.pageTitles.emailNotification');
SpectrumPageHeaderModel.setTitle('user.pageTitles.emailNotification');
SpectrumErrorModel.clearErrorsForScope($scope.$id);
generateOptions();
loadUserData();
}
function generateOptions() {
for (var status in MediaPreferencesModel.STATUS) {
if (MediaPreferencesModel.STATUS[status].domainType === 'Relationship') {
$scope.relationshipNotificationList.push(MediaPreferencesModel.STATUS[status]);
} else if (MediaPreferencesModel.STATUS[status].domainType === 'Audit') {
$scope.auditNotificationList.push(MediaPreferencesModel.STATUS[status]);
}
}
}
function loadUserData() {
UserService.getOne(userCode).then(
function successHandler(successResponse) {
UserModel.populateFromJSON(successResponse.data);
getNotificationPreferences();
},
function errorHandler(errorResponse) {
SpectrumErrorModel.handleErrorResponse($scope.$id, errorResponse);
}
);
}
function getNotificationPreferences() {
EmailNotificationService.getNotificationPreferences(UserModel.userCode).then(
function successHandler(successResponse) {
$scope.preferencesData = successResponse.data;
EmailNotificationModel.populateData($scope.preferencesData);
},
function errorHandler(errorResponse) {
SpectrumErrorModel.handleErrorResponse($scope.$id, errorResponse);
}
);
}
function saveData() {
var a = $scope.testListMediaValue;
EmailNotificationService.saveNotificationPreferences(UserModel.userCode, EmailNotificationModel.getJsonForSavePreferences()).then(
function successHandler(successResponse) {
console.log(successResponse);
},
function errorHandler(errorResponse) {
SpectrumErrorModel.handleErrorResponse($scope.$id, errorResponse);
}
);
}
$scope.clickHandler = {
saveButton: function () {
saveData();
},
backButton: function () {
$location.path(viewUserPath());
},
selectAll: function () {
angular.forEach(MediaPreferencesModel.relationshipStatus, function (itm) {
itm.userChoice = true;
});
},
deselectAll: function () {
angular.forEach(MediaPreferencesModel.relationshipStatus, function (itm) {
itm.userChoice = false;
});
},
checkValue: function (isChecked) {
console.log(isChecked);
}
};
function viewUserPath() {
return '/user/view/' + userCode;
}
$scope.canShow = {
emailPreferences: function () {
var preferenceFields = MediaPreferencesModel.preferenceFields;
return (preferenceFields.length > 0);
},
isRelationshipNotification: function (reference) {
return reference.domainType === 'Relationship';
},
isAuditNotification: function (reference) {
return reference.domainType === 'Audit';
}
};
initialise();
}
]
);
model
angular.module(
'EmailNotificationModelModule',
[]
).factory(
'EmailNotificationModel', [
function () {
return {
selectedLanguageValue: '',
userNotificationPreferencesList: [],
getJsonForSavePreferences: function () {
var json = {};
json.selectedLanguageValue = this.selectedLanguageValue;
json.userNotificationPreferencesList = this.userNotificationPreferencesList;
return json;
},
populateData: function (preferencesData) {
this.selectedLanguageValue = preferencesData.selectedLanguage.value;
}
};
}
]
);
JSON parse when I call get function to generate form
{
selectedLanguage: {
label: 'Spain',
value: 'sp'
},
languageOptionList: [
{
label: 'English',
value: 'en'
},
{
label: 'Chinese',
value: 'cn'
},
{
label: 'Spain',
value: 'sp'
},
{
label: 'French',
value: 'fr'
},
{
label: 'Portuguese',
value: 'po'
},
{
label: 'Japanese',
value: 'jp'
}
],
userNotificationPreferencesList: [
{
label: 'organisation.mediaPreference.tradingRelationships',
domainType: 'tradingRelationShip',
mediaOptionList: [
{
id: 'ACCEPTED',
label: 'organisation.relationshipStatuses.accepted',
order: 1,
userChoice: true
},
{
id: 'INITIATED',
label: 'organisation.relationshipStatuses.initiated',
order: 2,
userChoice: false
},
{
id: 'REJECTED',
label: 'organisation.relationshipStatuses.rejected',
order: 3,
userChoice: true
}
]
},
{
label: 'organisation.mediaPreference.auditNotifications',
domainType: 'auditNotification',
mediaOptionList: [
{
id: 'AUDIT_CREATED',
label: 'organisation.auditStatuses.created',
order: 4,
userChoice: true
},
{
id: 'AUDIT_ACCEPTED',
label: 'organisation.auditStatuses.accepted',
order: 5,
userChoice: false
}
]
}
]
};
The correct way to use NgModel with input[checkbox] can be found here.
For your implementation, change this
<input type="checkbox"
id="inputStatus1-{{ mediaOption.id }}"
value="{{ mediaOption.id }}"
ng-checked="mediaOption.userChoice"
data-ng-click="clickHandler.checkValue(mediaOption.id)">
To this
<input type="checkbox"
ng-model="mediaOption.userChoice"
ng-change="clickHandler.checkValue(mediaOption.id)">
I'm using a vue component which is two way bound with an input field.
I want to append a +- and a % sign to this value solely in the input field view. I don't want to change the actual value as this will cause troubles with the component.
Here is what I am looking for:
Here is what I have:
Using this code:
<form class="form-container">
<label for="changePercent" class="move-percent-label">Move Market</label>
<input class="move-percent" id="changePercent" v-model="value.value" type="number">
<span class="middle-line"></span>
<vue-slider v-bind="value" v-model="value.value"></vue-slider>
<div class="control-buttons">
<button #click="" class="primary-button">Apply</button>
<button #click.prevent="value.value = 0;" class="tertiary-button">Reset</button>
</div>
</form>
------------------UPDATE-------------------
As per answer below using a computed property.
Good:
Not Good
So I need this to work both ways
To have another value always formated a computed property:
new Vue({
el: '#app',
data: {
value: {value: 0},
// ..
},
computed: {
readableValue() {
return (this.value.value => 0 ? "+" : "-" ) + this.value.value + "%";
}
}
})
Creating an editor for the slider and showing formatted
To get what you want we will have to do a litte trick with two inputs. Because you want the user to edit in a <input type="number"> but want to also show +15% which can't be shown in a <input type="number"> (because + and % aren't numbers). So you would have to do some showing/hiding, as below:
new Vue( {
el: '#app',
data () {
return {
editing: false,
value: {value: 0},
}
},
methods: {
enableEditing() {
this.editing = true;
Vue.nextTick(() => { setTimeout(() => this.$refs.editor.focus(), 100) });
},
disableEditing() {
this.editing = false;
}
},
computed: {
readableValue() {
return (this.value.value > 0 ? "+" : "" ) + this.value.value + "%";
}
},
components: {
'vueSlider': window[ 'vue-slider-component' ],
}
})
/* styles just for testing */
#app { margin: 10px; }
#app input:nth-child(1) { background-color: yellow }
#app input:nth-child(2) { background-color: green }
<div id="app">
<input :value="readableValue" type="text" v-show="!editing" #focus="enableEditing">
<input v-model.number="value.value" type="number" ref="editor" v-show="editing" #blur="disableEditing">
<br><br><br>
<vue-slider v-model="value.value" min="-20" max="20">></vue-slider>
</div>
<script src="https://unpkg.com/vue#2.5.13/dist/vue.min.js"></script>
<script src="https://nightcatsama.github.io/vue-slider-component/dist/index.js"></script>
I have a vue app and a component. The app simply takes input and changes a name displayed below, and when someone changes the name, the previous name is saved in an array. I have a custom component to display the different list items. However, the component list items do not render immediately. Instead, the component otems render as soon as I type a letter into the input. What gives? Why would this not render the list items immediately?
(function(){
var app = new Vue({
el: '#app',
components: ['name-list-item'],
data: {
input: '',
person: undefined,
previousNames: ['Ian Smith', 'Adam Smith', 'Felicia Jones']
},
computed: {
politePerson: function(){
if(!this.person) {
return 'Input name here';
}
return "Hello! To The Venerable " + this.person +", Esq."
}
},
methods: {
saveInput: function(event){
event.preventDefault();
if(this.person && this.previousNames.indexOf(this.person) === -1) {
this.previousNames.push(this.person);
}
this.setPerson(this.input);
this.clearInput();
},
returnKey: function(key) {
return (key + 1) + ". ";
},
clearInput: function() {
this.input = '';
},
setPerson: function(person) {
this.person = person;
}
}
});
Vue.component('name-list-item', {
props: ['theKey', 'theValue'],
template: '<span>{{theKey}} {{theValue}}</span>'
});
})()
And here is my HTML.
<div id="app">
<div class="panel-one">
<span>Enter your name:</span>
<form v-on:submit="saveInput">
<input v-model="input"/>
<button #click="saveInput">Save</button>
</form>
<h1>{{politePerson}}</h1>
</div>
<div class="panel-two">
<h3>Previous Names</h3>
<div>
<div v-for="person, key in previousNames" #click='setPerson(person)'><name-list-item v-bind:the-key="key" v-bind:the-value="person" /></div>
</div>
</div>
</div>
You are not defining your component until after you have instantiated your Vue, so it doesn't apply the component until the first update.
Im trying to use ionic list to display a set of cars, when the particular car is selected it should display a small description of it and also editable. Here Im unable to display the description of the item selected. Also please let me know how to edit the description and update it.
link here
this is the controller
car.controller('popSelect', ['$scope','$ionicPopup', function ($scope, $ionicPopup) {
$scope.homePage = function () {
window.location = "#/menu.html"
}
$scope.carList = [{ value: "Honda", description: "Its honda" },
{ value: "Toyota", description: "Its Toyota" },
{ value: "BMW", description: "Its BMW" }];
//$scope.carList=['Bmw','Mercedes','Honda'];
$scope.select_item = function (key) {
{{item.decription}}
}
}]);
Changes in Index.html
<body ng-app="myApp" ng-controller="myCtrl">
<h4>Select car</h4>
<ion-list>
<ion-item ng-click="select_item(value)" ng-repeat="(key,value) in carList">
{{value.value}}
</ion-item>
</ion-list><hr>
<div style="text-align:center">
<button class="button3" ng-click="showPopup()">ADD</button><hr>
<button class="button2" ng-click="homePage()">Back</button>
<button class=" button2" ng-click="deleteSelected()">Delete</button>
</div>
<div>{{description}}</div>
<!--{{couponTitle}} {{couponDescribe}} {{validFrom}} {{validTo}} -->
</body>
In your js
var car=angular.module('myApp',[]);
car.controller('myCtrl', ['$scope', function ($scope) {
$scope.homePage = function () {
window.location = "#/menu.html"
}
// $scope.description = '';
$scope.selectedItem = 'select';
$scope.items=[];
$scope.carList = [{ value: "Honda", description: "Its honda" },
{ value: "Toyota", description: "Its Toyota" },
{ value: "BMW", description: "Its BMW" }];
//$scope.carList=['Bmw','Mercedes','Honda'];
$scope.select_item = function (key) {
$scope.description=key.description;
}
}]);
Thanks (Y)