I'm pretty new to Vue.js and while I've figured most of the problems I'm hitting out, I can't get my head around this one.
I'm displaying a list of posts based on API output, and want to have a comments box for each post. Posting to the API is working fine and it adds it without a problem, but because I'm using the same v-model for each input in the loop any text entered is replicated throughout all other matching inputs due to the binding.
<div class="row" v-if="">
<div class="col-md-11">
<input type="text" class="form-control" value="" title=""
placeholder="Add comments here.." v-model="note.note">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-default" style="margin-left: -1.5rem" v-on:click="addNote(task.id)">Add Comment</button>
</div>
</div>
JS:
addNote: function (id) {
if (this.note.note) {
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('value');
this.$http.post('api/note/create/' + id, this.note).then(function (response) {
this.fetchTaskList();
});
}
}
Screenshot:
Is there a different directive I should be using? Or is there another way around this?
You can do this by using index in v-for and then binding each box to the notes comment like so:
<!-- use index, so we know where we are in the loop -->
<div v-for="(note, index) in notes">
{{ note.note }}
<!-- Bind to the comment at the given index -->
<input v-model="notes[index].comment" />
</div>
Now you just need to set that up in data:
data: {
notes: [
{note: 'note 1', comment: ''},
{note: 'note 2', comment: ''},
{note: 'note 3', comment: ''}
]
}
Here's the JSFiddle: https://jsfiddle.net/efxzmq9s/
Related
I am trying to build a block editor type form in Angular. I want this form to be just one array of dynamic fields. I have the following code so far but the button to add different fields always adds all fields instead of the one it should. How can I add only that specific field to the array? What is the best approach to develop this kind of form?
Form TS
storyForm = this.story.group({
blocks: this.story.array([]),
});
get blocks() {
return this.storyForm.get('blocks') as FormArray;
}
Buttons to add different fields to the above array
addHeading() {
this.blocks.push(this.story.group({
heading: [''],
}));
}
addParagraph() {
this.blocks.push(this.story.group({
paragraph: [''],
}));
}
HTML Template
<div class="form-group" formArrayName="blocks">
<div *ngFor="let piece of blocks.controls; let i=index">
<textarea class="form-control" rows="8" placeholder="heading" formControlName="heading" ></textarea>
<textarea class="form-control" rows="8" placeholder="paragraph" formControlName="paragraph" ></textarea>
</div>
</div>
<button type="button" class="btn btn-secondary" (click)="addParagraph()">Add Paragraph</button>
<button type="button" class="btn btn-secondary" (click)="addHeading()">Add Heading</button>
I have my chat and I dont want people to send empty message so I would like that my input become required. Thanks for your help.
I tried to put "required='required'" in the input line, I also tried veeValidate but it broke my chat when I use it, I also tried to put "Required = true" in Props and data but without a good result
This is ChatForm.vue
<template>
<div class="input-group" >
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Ecrire..." v-model="newMessage" #keyup.enter="sendMessage">
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendMessage">
✓
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
newMessage: '',
}
},
methods: {
sendMessage() {
this.$emit('messagesent', {
user: this.user,
message: this.newMessage
});
setTimeout(function() {
const messages = document.getElementById('mess_cont');
messages.scrollTop = messages.scrollHeight;
}, 200);
this.newMessage = '';
}
}
}
</script>
And this is my form in the app.blade.php
<div id="app" class="container-chat">
<div class="row">
<div class="col-md-12 col-md-offset-2">
<div class="col-md-12 col-md-offset-2">
<div class="panel-body panel-content" id="mess_cont">
<chat-messages id="mess" :messages="messages" :currentuserid="{{Auth::user()->id}}"></chat-messages>
</div>
<div class="panel-footer">
<chat-form
v-on:messagesent="addMessage"
:user="{{ Auth::user() }}"
></chat-form>
</div>
</div>
</div>
</div>
</div>
Try to change your ChatForm.vue like this:
<template>
<form #submit.prevent="sendMessage">
<div class="input-group" >
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Ecrire..." v-model="newMessage" required>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" type="submit" id="btn-chat">
✓
</button>
</span>
</div>
</template>
You are not treating the input in the correct way, the input which is required needs to be inside a form and the required keyword will prevent the form submission if the input field is empty.
There are a few things I would do differently.
1/ Wrap your chat form in a tag, and execute the sendMessage() method on submit. This will give your users a nicer experience, as they can just to submit the message.
2/ Convert the button into a submit button so it triggers the form.submit event.
3/ You can easily disable the button by checking whether newMessage has contents. I don't think you need vee validate or anything else to achieve this; for something as simple as a chat form, your user doesn't need much more feedback than seeing a disabled button to realise (s)he needs to write something first.
4/ in the addMessage method you can just check the contents of newMessage and not do anything when it's empty. This is perfectly fine because you already hinted the user by disabling the button too.
I think this is a subtle way where you guide your user, but don't overdo it.
Please add name attributes to all of your form elements. Some of the element in my form had name attribute and some didn't. Element which had name attributes worked correctly but the one's which didn't had name failed.
I am working on a web app that displays a prompt based on which field the user has in focus or not. I have a few different scripts of text that I want to display at these different points.
What I would like to do is display the scope data in those strings when they are displayed. Right now everything gets rendered as text. Here is what I am doing. In the view. (Ignore my poor use of ng-init I will change this to a controller.)
<div><p>Full Name</p><input placeholder="Name" name="personal.name" ng-model="personal.name" ng-model-options="{ updateOn: 'default blur' }" class="medium" type="text"/></div>
<div><p>Address</p><input class="long" type="text" ng-model="personal.address" ng-model-options="{ updateOn: 'default blur' }" placeholder="Address"/></div>
<div><p>Phone</p><input placeholder="Phone" ng-model="personal.phone" type="phone" ng-model-options="{ updateOn: 'default blur' }" only-digits/></div>
<div ng-init="script()">
<div ng-repeat="scripts in script">
<div ng-show="{{scripts.element}}" ng-hide="true" class="alert-bar">
<div class="wrapper">
<div ng-bind="scripts.script"></div>
</div>
</div>
</div>
</div>
and the controller again ignore the data in this I will store this later.
$scope.script = function(){
$scope.script = [
{
'element': "personal.name",
'script': '{{personal.name}} welcome to SOCU\'s loan application.'
},
{
'element': "personal.address",
'script': '{{personal.name}}, please enter your full current address.'
},
{
'element': "personal.phone",
'script': '{{personal.name}}, please enter your current phone number, with the area code.'
}
];
}
Everything works except I cannot get the name to show up. I know that I shouldn't use the double curly brackets here. I've also tried getting the data directly from the $scope but I don't see any personal data there.
You can't do bindings in your controller as they are dedicated to view only.
Since you iterate the script array you can just access the elements of object by,
{{scripts.element + scripts.script}}
Handlebar syntax should work for div element. Have an array structure as given below,
$scope.script = [{
element: 'Person name',
script: 'Your script'
}];
In your div,
<div ng-init="script()">
<div ng-repeat="scripts in script">
<div ng-show="{{scripts.element}}" ng-hide="true" class="alert-bar">
<div class="wrapper">
<div>
{{ scripts.element + " , " + scripts.script }}
</div>
</div>
</div>
</div>
</div>
<div class="field row">
<div class="span2">{{field.field_title|underscoreless}}:</div>
<div class="span4">
<div ng-repeat="option in field.field_options" class="row-fluid">
<label>
<input type="checkbox" value="{{option.option_value}}" name="{{field.field_title}}[]" id="{{field.field_title}}" ng-required="field.field_required" ng-model="field.field_value" ng-disabled="field.field_disabled"/>
<span ng-bind="option.option_title"></span>
</label>
</div>
<span class="required-error" ng-show="field.field_required && !field.field_value">* required</span>
</div>
</div>
This is my anugular js code.. I want to add checkboxes dynamically and it added well. but the problem is when i select one checkbox other one also gets checked.. I dont know what to do ? Any help or suggestion?
In your code you have
ng-model="field.field_value"
means one model for all checkboxes. You have to make that model dynamic too.
Soluton -
ng-model="field[field_value]"
Like already pointed out, you are binding each checkbox to the same model, which results in checking all at once when checking one (click updates model => this triggers updating the view again, checking all boxes).
Try binding the option values to an array like this:
ng-model="field.value[option.value]"
The whole template code:
<div class="field row">
<div class="span2">{{field.title}}:</div>
<div class="span4">
<div ng-repeat="option in field.options" class="row-fluid">
<label>
<input type="checkbox" value="{{option.value}}" name="{{field.title}}[]" id="{{field.title}}" ng-required="field.required" ng-model="field.value[option.value]" ng-disabled="field.disabled"/>
<span ng-bind="option.title"></span>
</label>
</div>
<span class="required-error" ng-show="field.required && !field.value">* required</span>
</div>
<pre>{{field.value}}</pre>
</div>
I also changed the property names without the redundancies (field.field_value => field.value and so on). In the controller:
$scope.field = {
title:'Test Field',
options:[
{
value: 'A',
title: 'Option A'
},
{
value: 'B',
title:'Option B'
},
{
value: 'C',
title: 'Option C'
}]
}
field.value then has values like
{"A":false,"B":false,"C":true}
Have a look at this plnkr: https://plnkr.co/edit/4qiUUyYCPsmvkaOEdHTk?p=preview
It's because all checkboxes get the same id when you do id="{{field.field_title}}". Change it to something like option.id instead.
And you're also binding them all to ng-model="field.field_value", it should be ng-model="option.value" (or what ever property is correct for your data in option)
trying to achieve the following behaviour with angular & x-editable:
I have an Add User form with 1 required input: "Name".
When adding a User there are 2 options:
1. If the User exists in the $scope.names I use typeahead to display the option list -> OK
2. If the User is new (not in $scope.names) then I should have a second required input "Email" and I should only display it when typeahead finds no match for my "Name" Input
And this is where I'm stuck... I don't really know how to apply the following "raw" condition:
if user is not in users -> show Email Input
Any help would be much appreciated!
Here is the
JSfiddle!
<div ng-app="app">
<div ng-controller="InvestorsController">
<ul>
<li ng-repeat="investor in investors">
<span href="#" editable-text="investor.name" e-placeholder="Name" e-name="name" e-form="investorsForm" e-typeahead="name for name in names | filter:$viewValue | limitTo:8" e-required>
{{ investor.name || 'empty' }}
</span>
<form editable-form name="investorsForm" ng-show="investorsForm.$visible" class="form-buttons form-inline" shown="inserted == investor">
<button type="submit" ng-disabled="investorsForm.$waiting" class="btn btn-primary sx-btn-ok">OK</button>
<button type="button" ng-disabled="investorsForm.$waiting" ng-click="investorsForm.$cancel()" class="btn btn-default">X</button>
</form>
<div class="buttons" ng-show="!investorsForm.$visible">
<button class="btn btn-primary" ng-click="investorsForm.$show()">OK</button>
<button class="btn btn-danger" ng-click="removeInvestor($index)">X</button>
</div>
</li>
<button class="" ng-click="addInvestor()">Add Investor</button>
</ul>
</div>
</div>
I would add something along the lines of: ng-hide="shouldHide()" in the span that is the email input.
Now, the shouldHide function should always grab the value from the name input and check it against the names list and return true if found and false if not found.
Hope this helps, didn't have the time to mock it up in a fiddle sorry.
[Edit] You find the value of the name input in the $scope.investor.name as far as I can figure, I never worked with x-editable.
As said previously, you could try to get the input value and check if exists in list, if the value isn't in the list the optional input will be shown.
$scope.shouldHide = function(index) {
//get the typeahead input value:
var nameInput = document.getElementsByName('name')[0]
var nameValue = null
if (nameInput){
nameValue=document.getElementsByName('name')[0].value;
}
if (nameValue){
//check if typeahead input value is in list
$scope.names = ['User 1', 'User 2', 'User 3', 'User 4', 'User 5', 'User 6'];
return $scope.names.indexOf(nameValue) >=0
}
return true;
};
I fork your fiddle and added the explained behaviour here: http://jsfiddle.net/nachoorme/RZK9u/1/