Passing multiple vars in vue,js - laravel

I'm building a chat app, and in the cat vue component I have:
<template>
<div class="chat-app">
<Conversation :contact="selectedContact" :messages="messages" #new="saveNewMessage"/>
<ContactsList :contacts="contacts" #selected="startConversationWith"/>
</div>
then in the script part, in the methods array, I have:
saveNewMessage(message) {
this.messages.push(message);
},
Basically, there is an attribute in contact that I want to bring into the message to save it in message - the conversation_id. Is there a way to get contact into saveNewMessage()? I tried saveNewMessage(message, contact), but that didn't work. Thanks in advance!

You have access to all the data properties in the your component methods:
saveNewMessage(message) {
// access the selected contact
this.selectedContact
...
this.messages.push(message);
},

Related

Mixing Alpine.js with 'static' serverside markup, while getting the benefits of binding, etc

I'm new to Alpine and struggling to wrap my head around how to make a scenario like this work:
Let's say I have a serverside built page, that contains some buttons, that represent newsletters, the user can sign up to.
The user might have signed up to some, and we need to indicate that as well, by adding a css-class, .i.e is-signed-up.
The initial serverside markup could be something like this:
<button id='newsletter-1' class='newsletter-signup'>Newsletter 1</button>
<div>some content here...</div>
<button id='newsletter-2' class='newsletter-signup'>Newsletter 2</button>
<div>more content here...</div>
<button id='newsletter-3' class='newsletter-signup'>Newsletter 3</button>
<div>and here...</div>
<button id='newsletter-4' class='newsletter-signup'>Newsletter 4</button>
(When all has loaded, the <button>'s should later allow the user to subscribe or unsubscribe to a newsletter directly, by clicking on one of the buttons, which should toggle the is-signed-up css-class accordingly.)
Anyway, then I fetch some json from an endpoint, that could look like this:
{"newsletters":[
{"newsletter":"newsletter-1"},
{"newsletter":"newsletter-2"},
{"newsletter":"newsletter-4"}
]}
I guess it could look something like this also:
{"newsletters":["newsletter-1", "newsletter-2", "newsletter-4"]}
Or some other structure, but the situation would be, that the user have signed up to newsletter 1, 2 and 4, but not newsletter 3, and we don't know that, until we get the JSON from the endpoint.
(But maybe the first variation is easier to map to a model, I guess...)
Anyway, I would like to do three things:
Make Alpine get the relation between the model and the dom elements with the specific newsletter id (i.e. 'newsletter-2') - even if that exact id doesn't exist in the model.
If the user has signed up to a newsletter, add the is-signed-up css-class to the corresponding <button> to show its status to the user.
Bind to each newsletter-button, so all of them – not just the ones, the user has signed up to – listens for a 'click' and update the model accordingly.
I have a notion, that I might need to 'prepare' each newsletter-button beforehand with some Alpine-attributes, like 'x-model='newsletter-2', but I'm still unsure how to bind them together when Alpine has initialising, and I have the data from the endpoint,
How do I go about something like this?
Many thanks in advance! 😊
So our basic task here is to add/remove a specific item to/from a list on a button click. Here I defined two component: the newsletter component using Alpine.data() creates the data (subs array), provides the toggling method (toggle_subscription(which)) and the checking method (is_subscribed(which)) that we can use to set the correct CSS class to a button. It also handles the data fetching in the init() method that executes automatically after the component is initialized. I have also created a save method that we can use to send the subscription list back to the backend.
The second component, subButton with Alpine.bind() is just to make the HTML code more compact and readable. (We can put each attribute from this directly to the buttons.) So on click event it calls the toggle_subscription with the current newsletter's key as the argument to add/remove it. Additionally it binds the bg-red CSS class to the button if the current newsletter is in the list. For that we use the is_subscribed method defined in our main component.
.bg-red {
background-color: Tomato;
}
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js" defer></script>
<div x-data="newsletter">
<button x-bind="subButton('newsletter-1')">Newsletter 1</button>
<button x-bind="subButton('newsletter-2')">Newsletter 2</button>
<button x-bind="subButton('newsletter-3')">Newsletter 3</button>
<button x-bind="subButton('newsletter-4')">Newsletter 4</button>
<div>
<button #click="save">Save</button>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletter', () => ({
subs: [],
init() {
// Fetch list of subscribed newsletters from backend
this.subs = ['newsletter-1', 'newsletter-2', 'newsletter-4']
},
toggle_subscription(which) {
if (this.subs.includes(which)) {
this.subs = this.subs.filter(item => item !== which)
}
else {
this.subs.push(which)
}
},
is_subscribed(which) {
return this.subs.includes(which)
},
save() {
// Send this.sub to the backend to save active state.
}
}))
Alpine.bind('subButton', (key) => ({
'#click'() {
this.toggle_subscription(key)
},
':class'() {
return this.is_subscribed(key) && 'bg-red'
}
}))
})
</script>

Upload multiple files, picking them one by one, with BootstrapVue b-form-file

I'm uploading files with b-form-file in BootstrapVue, setting multiple to true works perfectly for multiple files, BUT each time I choose a file it removes any previously added ones. And the files will often be spread across multiple folders, so I need to be able to pick a file from one folder, choose it, pick another file from another folder, and so on.
Here's the HTML:
<b-form ref="form" novalidate action="upload" method="post" enctype="multipart/form-data">
<b-form-file name="file_upload[]" :multiple="true" :file-name-formatter="formatAssetUpload" no-drop placeholder="Click to choose"></b-form-file>
</b-form>
I've tried adding
ref="fileUpload"
to the b-form-file tag and then in the formatAssetUpload function just setting the value, but that doesn't work. There's a setFiles function in there but it doesn't seem to affect anything. I tried catching the form on submit and manually adding the files to formdata but that's not working either, whatever I try there's always only the last file/files that were picked coming through on the backend.
Any ideas?
Thanks for any help! :)
If you like to remember the file objects which have been selected by the user you an use this javascript:
new Vue({
el: '#app',
data() {
return {
files: [],
filesAccumulated: []
}
},
methods: {
onChange(event) {
this.files.forEach(thisFile => {
this.filesAccumulated.push(thisFile)
})
},
onReset() {
this.filesAccumulated = []
}
}
})
Together whit this vue template
<div id="app">
<b-form-file
v-model="files"
multiple plain
v-on:input="onChange">
</b-form-file>
<div>
<ul>
<li v-for="thisFile in filesAccumulated">
{{ thisFile.name }}
</li>
</ul>
</div>
<b-button v-on:click="onReset">
Reset
</b-button>
</div>
The b-form-file vue component is emitting an input event when the user performs the selection. The file objects can be picked up
from the variable bound with the v-model directive. See documentation
Look at this fiddle https://jsfiddle.net/1xsyb4dq/2/ to see the code in action.
General comments
Let me make some comments on your code example and b-form-file component in general: With bootstrap-vue and <b-form-file> it is not the idea to use an enclosing form tag to submit forms. Instead you use vue's v-model feature to obtain the file objects from vue's data object.
I have created a more comprehensive example here https://jsfiddle.net/to3q7ags/ , but I will explain step by step:
The value of the v-model attribute is the name of a vue data property:
<b-form-file v-model="files" multiple plain></b-form-file>
After the user has clicked 'Browse' in the file input field, has selected the files and pressed ok, the vue data property will hold an array of file objects:
new Vue({
el: '#app',
data() {
return {
files: []
// initially empty, but will be populated after file selection
}
}
})
From the b-form file documentation:
When no files are selected, an empty array will be returned. When a
file or files are selected the return value will be an array of
JavaScript File object instances.
After files are selected by the user you can send the files to your server on a button click or change event.
<b-button v-on:click="onSubmit">
Submit
</b-button>
the corresponding vue method looks like this
methods: {
onSubmit() {
// Process each file
this.files.forEach(thisFile => {
console.log("Submitting " + thisFile.name)
// add submit logic here
}
}
To submit the files you need to manually create a http post request which is using the multipart/form-data encoding. That can be done by using FormData class.
const formBody = new FormData()
formBody.set("formParameter", thisFile)
The FormData.set() method is accepting file blobs as arguments. Then you can use XMLHttpRequest.send() or fetch() to send the post request:
fetch(
"https://yourserver/post",{
method: 'POST',
body: formBody})
.then(response => console.log(response))
Now all selected files are posted to the server. The user can repeat the process with the same form, select new set of files and again press the post button.
You can also send the form automatically without the use of a button by listening to the 'input' event. Drag and drop also works.

What is the best way to use v-model without using it in the main app.js file?

I was wondering i have this code inside my blade file:
<input type="text" class="form-control" :model="productname" id="name" name="name" aria-describedby="emailHelp" placeholder="Title" value="{{ old('name') }}" required>
and i want to create an 2 way data binding, so when user submitted 3 or more character to get an green background of this input field, but when they are less to get a red background.
I tried to do this with vue-component, but it seems for whatever reason the vue-component does not have access to the main #app id which is assign to the main app blade template in order all vue code to have access to the html code and change it. I have created an vue-component called Upload inside ressources/assests/js/components/Upload.js, but when i write this code:
<template>
</template>
<script>
export default {
data() {
return {
productname: '',
};
},
mounted() {
console.log('Upload component', this.message);
}
}
</script>
and added of course to app.js like that Vue.component('upload-component', require('./components/Upload.vue'));
and run - npm run dev to compile the code i am getting this error:
[Vue warn]: Property or method "productname" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
Whatever when i write this code inside the main app.js file (resources/assests/js/app.js):
const app = new Vue({
el: '#app',
data() {
return {
productname: '',
};
},
});
everything is working fine and i dont get any errors, but i dont want that, as imagine for my website/app i have to have for example 100 v-models and i dont want to put them all inside the main app.js file. Is there other way to do this?
Can i still somehow make it work inside the vue component, so the component can see the v-model and not giving me this error?
If not, can i make it work and use vue inside the blade file something like that?
new Vue({
el: '#form',
data: {
productname: ''
}
});
but this code its not working. Thanks!
It's a bit tricky to make out what you are doing exactly, but your error is related to having no data property 'message', you have only a property called 'productname'.
If you want your component to communicate up to its parents or siblings you should read into emitting events in vue using
this.$emit

Better Way to use Laravel old and Vue.js

Im working using vue.js 2.0 and Laravel 5.4 I would like to know if exits a better way to send data Controllers -> views without lost the value, overwrite by the v-model
Because after charge vue.js this take the value in data that is define in this way
data: {
ciudad:'',
}
If I try to do something like that
<input class="form-control" id="ciudad" name="ciudad" type="text" v-model="documento.ciudad" value="{{ $documento->ciudad }}" >
I lost the value sending by the controller
Vue really expects you to init data from the view model which then updates the view, however, you want to use data in the view to update the underlying model data.
I think the easiest approach to this is to write a directive that inits the value from the HTML, so you don't need to directly access the DOM:
Vue.directive('init', {
bind: function(el, binding, vnode) {
vnode.context[binding.arg] = binding.value;
}
});
It's a little abstract so it's worth looking at Directive Hook Arguments section of the docs.
This can then be used like:
<input v-model="ciudad" v-init:ciudad="'{{ old('ciudad') }}'">
Here's the JSFiddle: https://jsfiddle.net/v5djagta/
The easy way to do this for me was in this way:
Laravel Controller
$documento = ReduJornada::where("id_documento",$id)->first();
return view('documentos.redujornada')->with(compact('documento'));
Laravel View
<input class="form-control" id="ciudad" v-model="documento.ciudad" value="{{ old('ciudad', isset($documento->ciudad) ? $documento->ciudad : null) }}" >
Vue.js
data: {
ibanIsInvalid : false,
documento: {
ciudad: $('#ciudad').val(),
}
In this way I can use the same view to create and edit an object, using the same form, even use laravel validation without lost the data after refresh.
If you know a better way to do that please tell me... Thanks
You have to define your value first, for example :
$oldValue = $request->old('value');
and then pass it to vue component, define props and use it in v-model
<script>
export default {
props: {
$oldValue : String,
}
};
</script>
<template>
<input class="form-control" id="ciudad" name="ciudad" type="text" v-model="oldValue">
</template>

Ember-Validation Issue w/ Ember CLI & Ember Data

I'm attempting to implement form validation on a new Contact in my app using the ember-validations library. I'm currently using Ember Data with fixtures, and I've opted to place the validations in the model like the example in this video. I've been grappling with this for days now, and still can't seem to figure out why the validations aren't working. I'm not getting any indication that errors are even firing.
//app/models/contact.js
import DS from "ember-data";
import EmberValidations from 'ember-validations';
//define the Contact model
var Contact = DS.Model.extend(EmberValidations, {
firstName: DS.attr('string'),
lastName: DS.attr('string'),
});
//Create Contact fixtures
Contact.reopenClass({
FIXTURES: [...]
});
Contact.reopen({
validations: {
firstName: {
presence: true,
length: { minimum: 2 }
},
lastName: {
presence: true
}
}
});
export default Contact;
I'm new to Ember, and have been advised to put the following logic in routes instead of the controller. I haven't seen any examples of this being done with ember-validations, so I'm unsure if that's my issue regarding validations.
app/routes/contacts/new.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('contact');
},
actions: {
createContact: function() {
var contact = this.get('currentModel');
this.transitionTo('contacts');
contact.save();
alert(contact.errors);
},
cancelContact: function() {
var contact = this.get('currentModel');
contact.destroyRecord();
this.transitionTo('contacts');
}
}
});
My other suspicion is that I may not be handling the errors in html correctly?
//app/templates/contacts/new.hbs
{{#link-to 'contacts' class="btn btn-primary"}}Contacts{{/link-to}}
<form>
<label>First Name:</label>
{{input type="text" value=model.firstName}}<br>
<span class="error"></span>
<label>Last Name:</label>
{{input type="text" value=model.lastName}}<br>
<span class="error"></span>
</form>
<button {{action "createContact"}} class="btn btn-primary">Submit</button>
<button {{action "cancelContact"}} class="btn btn-warning">Cancel</button>
<br>
Here is my controller
//app/controllers/contacts.js
import Ember from "ember";
export default Ember.Controller.extend({
});
I'm enjoying Ember, but this issue is stonewalling me greatly. Any help would be much appreciated.
I'm going through this exact thing and have some advice. First I would say validate where you need to ask something if it is valid. You might need to do this on the component if it's a form, you might need to do this on a model if you want to ensure it's valid before saving, or maybe on a route if there are properties there that you're wanting to check.
In any case whatever method you pick, I would highly recommend using the ember-cp-validations addon. For what it's worth, Stephen Penner (ember.js core team) has contributed to the addon, too. It's all ready for Ember CLI as well.
The setup is actually very similar to what you're doing, but instead of reopening the class, you would define with it a mixin (example from their docs). To create the mixin that's used they have a factory called buildValidations. The nice thing is that you can use this on any Ember object.
Once you've defined your validation and mixed it into the create of your object, ie: Ember.Object.create(Validations, {}); where Validations is the mixin you've created just above (like in the docs). You are all set.
Within scope of that object you now have a validations property on the object, so something like:
var Validations = buildValidations({
greeting: validator('presence', true)
});
export default Ember.Object.create(Validations, {
greeting: 'Hello World',
actions: {
announce: function() {
alert('The current validation is: ' + this.get('validations.isValid'));
// per property validation is at:
alert('The individual validation is: ' + this.get('validations.attrs.greeting.isValid'));
}
}
})
Handlebars:
Looks like the form is {{ validations.isValid }}.
You can also <a {{action announce}}>announce the validation</a>.
Also, take a look at all the docs, there are is even more properties and use cases that this addon takes care of, including handlebars helpers, ajax/async resolution of validation, and some validators to use. If you don't find the one you're after make a function validator. Using the function validator all over, easy, make it re-usable with ember generate validator unique-username.
Hope this gets you off in the right start. This is a relatively new project but has decent support and responses on the issues has been quick.
Should also mention that because these validations are based on computed properties they work in an "Ember way" that should work great, including your models.

Resources