What is this.loading = false? - laravel

I am using Laravel and Vue.
When I was searching on the internet I saw the following code.
<template>
<div>
<h3 class="text-center">Create Movie</h3>
<div class="row">
<div class="col-md-6">
<form #submit.prevent="createMovie">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" v-model="movie.name">
</div>
<div class="form-group">
<label>Director</label>
<input type="text" class="form-control" v-model="movie.director">
</div>
<button type="submit" class="btn btn-primary">Create movie</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
movie: {}
}
},
methods: {
createMovie() {
this.axios
.post('http://localhost:8000/api/movie/create', this.movie)
.then(response => (
this.$router.push({name: 'movie'})
))
.catch(error => console.log(error))
.finally(() => this.loading = false)
}
}
}
I am trying to find out what the last line
.finally(() => this.loading = false)
is doing. I am searching on the internet but I can't find what it does. Also, I tried running the code without the last line however, it did not make any change.
Can someone please tell me what this is doing and when it is useful?

Without seeing the associated Vue template we cannot explain what it is doing exactly, however, we can be fairly confident that the value of loading will be used to show/hide some sort of overlay or activity spinner.
The purpose of the overlay/activity spinner is to provide visual feedback to the user that something is happening. This is useful when loading large amounts of data into your page, or when you perform a long running process (such as uploading a large file for example). So rather than the user seeing nothing on first page load, or clicking a button and wondering if it worked, they are provided with something to let them know that something is happening.
A basic example of what this might look like in the Vue template could be:
// if the value of loading is true, show this
<div v-if="this.loading">Loading, please wait ...</div>
// otherwise show this
<div v-else>Other content</div>
Your example is setting the value of loading to false once a response has been received from your axios request. You would probably want to set the value of loading to true prior to making the request to show an overlay/activity spinner.
Uncaught (in promise) ReferenceError: ture is not defined
You have a typo, it should be true not ture.

Related

alasql and alpine.js: reactivity from updates outside of Alpine

I would like to use Alpine.js while keeping as much application state inside of alasql.js as possible. The problem is that data which is loaded after Alpine.start() does not show up in the x-for section. If there is a way to force a wasteful full-refresh (without running Alpine.start() again) I would be interested to know. I tried the customEvents workaround several times but that did not work.
Is there any other way to make something like this work? Without changing too much of the architecture..? I'm curious / stubborn
await fetch(`${API_domain}?playlist=` + playlist)
.then(response => response.json())
.then(data => {
alasql('ATTACH localStorage DATABASE RuntimeDB')
alasql('SET AUTOCOMMIT ON')
alasql(`DELETE from videos where webpage_url = "${data.webpage_url}"`)
alasql(`INSERT INTO videos SELECT * FROM ?`, [[data]])
app.updateView()
})
<div x-data="{
playlistToAdd: '',
async submit($event) {
await app.fetchPlaylist(this.playlistToAdd)
}
}">
<label for="add_new">Add New Playlist or Channel:</label>
<input type="text" name="add_new" id="add_new" x-model="playlistToAdd"
placeholder="https://www.youtube.com/playlist?list=PL8A83124F1D79BD4F" #keyup.enter="submit" />
<button type="button" #click="submit">Submit</button>
</div>
<div x-data>
<template x-for="(pl, index) in app.view.table" :key="index">
<div>
<details x-html="`<summary><a href='${pl.webpage_url}'>${pl.title}</a> by
<a href='${pl.channel_url}'>${pl.uploader}</a> (watched ${pl.playlist_count-pl.entries.length} of ${pl.playlist_count}; ${(pl.playlist_count-pl.entries.length)/pl.playlist_count*100.0}%)</summary>
<template x-for='(v, index) in pl.entries' :key='index'>
<div><span x-text='v.title'></span></div>
</template>
`"></details>
</div>
</template>
</div>
full code here: https://github.com/chapmanjacobd/lb-lite/tree/main/client
As you noticed app.view.table is not reactive. For this purpose you can use the Alpine.store() which is externally accessible and reactive as well:
Alpine.store('table', [])
updateView: function () {
Alpine.store('table', alasql('select * from videos'))
}
And in the template just use $store.table:
<template x-for="(pl, index) in $store.table" :key="index">

Alpinejs property bound to x-show not defined

I'm building a form in Laravel's Blade syntax, and using AlpineJs for some interactivity stuff like showing and hiding content.
My code is here:
<div class="flex items-center" x-data="destinationBuilder">
<x-label required="true" class="mr-5">Destination:</x-label>
<x-basic-input #change="handleDestinationChange" ::value="destination" placeholder="https://google.com"/>
<x-buttons.primary class="ml-5" #check="validateDestination">Check</x-buttons.primary>
</div>
<div class="mt-4">
<button type="button"
class="p-5 w-full text-sm grid grid-cols-[min-content_min-content_auto_min-content] items-center gap-x-3 font-light text-gray-400 hover:bg-gray-300 rounded-full"
#click.camel="toggleAdvancedOptions">
<i class="lni lni-cog"></i>
<span class="whitespace-nowrap">Advanced options</span>
<div class="h-px w-full bg-gray-400"></div>
<i class="lni lni-chevron-down"></i>
</button>
<div x-show="advanced" x-transition x-cloak>
{{-- <x-links.get-parameter-form/>--}}
</div>
</div>
#push('footer-scripts')
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('destinationBuilder', () => ({
destination: '',
advanced: false,
handleDestinationChange() {
if (this.validateDestination()) {
// emit constructed destination up
}
},
validateDestination() {
// check url is in a legit form (with https)
// basic is just url input
// advanced dropdown with get parameters, fragment, http protocol and port
},
toggleAdvancedOptions() {
this.advanced = !this.advanced;
}
}));
})
</script>
#endpush
I'm using a property named advanced to bind to x-show for another component.
When I look in my browser I get the following message: app.js:434 Uncaught ReferenceError: advanced is not defined
I'm not sure if this is due to a weird collision with blade or if I'm missing something fundamental with Alpinejs. I tried renaming the variable to showAdvanced but that didn't work either. I would expect advanced to be recognised as a property and bind to x-show as expected. What am I doing wrong here?
You have the following HTML structure:
<div x-data="destinationBuilder">
...
</div>
<div>
<div x-show="advanced">
...
</div>
</div>
As you see, the second div is not a child element of the first one where the x-data is defined, so it's outside of the scope of destinationBuilder component. Just create a common div (or similar) element that embeds both divs and apply the component x-data there, so each div will have access to the component's scope.

Ember : if a value is set in controller , perform an Ajax command(written in template),

I have given a rough code to understand what I need to perform,
/app/route1/controller.js
export default Controller.extend({
test: function(id) {
$.ajax({
.....
.....
.....
}).then(() => {
set('message','successfully added');
});
}
});
/app/route1/template.hbs
<div class="ui container">
<div class="ui segment"
<button class="ui button" {{action "test" model.id}}>Submit</button>
</div>
</div>
<div class="ui container">
<div class="ui modal">
<div class="header"
Message
</div>
<div class="content"
{{message}}
</div>
<div class="actions">
<div class="ui ok button">OK</button>
</div>
</div>
</div>
<script type="text/javascript">
$.ajax({
if(message) {
$('.ui.modal').modal('show');
}
})
</script>
If I set a message in controller then, I have to show this message in the MODAL. The Ajax command that I've written is not correct., Please help to solve this issue.
Thanks in advance
First, you must not use <script> tags with ember. This won't work as expected and is in no way supported.
If you have to manually access DOM you should use the didInsertElement of a component.
Are you absolutly sure you want to build your modal yourself? There are many addons providing a nice API for modals. If you can use one of them. If you cant you basically have to write your own modal component.
I will not instruct your in detail how to do this, because this seems not to be your primary question.
Now about your test function. first you seem to try to use it as an action:
{{action "test" model.id}}
however for this you must place the function under the actions hash.
Next this line is wrong:
set('message','successfully added');
you either must use this.set('message','successfully added'); or set(this, 'message','successfully added');
Then you can display your message like this:
{{#if message}}
{{#modal-dialog
onClose=(action (mut isShowingBasic) false)
}}
{{message}}
{{/modal-dialog}}
{{/if}}
And it will work as expected.

How do I use an Angular.JS generated form to upload a file (and other fields) through AJAX to Symfony2?

I'm trying to send a form generated by Angular.js through an AJAX call to a Symfony controller action in order to be saved. The form is generated in an ng-repeat after receiving some JSON data (including an entry ID). The code looks as follows.
<div ng-repeat="job in sc.careers">
<div>
<h2>{[{job.title}]}</h2>
<span ng-if="job.super">{[{job.super}]}</span>
<div>
<h4>Role</h4>
<span ng-bind-html="job.role"></span>
</div>
<div class="col-md-4">
<h4>Required Skills</h4>
<span ng-bind-html="job.skills"></span>
</div>
<div class="col-md-4">
<h4>Media</h4>
Some other content \ media
</div>
</div>
<hr/>
<div class="join-us">
<button ng-click="sc.joinUs[$index] = true" ng-hide="sc.joinUs[$index]">Apply for this position</button>
<div ng-show="sc.joinUs[$index]">
<h4>Join Us</h4>
<span>Some random text</span>
<form>
<input type="hidden" name="job_id" value="{[{job.id}]}" />
<label for="email">E-Mail</label>
<input type="email" id="email" name="email" />
<label for="motivation">Cover Letter & CV link</label>
<textarea id="motivation" name="motivation"></textarea>
<label for="resume">Upload your resume</label>
<input type="file" name="resume">
<button type="submit">Send your application</button>
</form>
</div>
</div>
</div>
* most class names have been redacted to keep the code as relevant to the question as possible
I have not yet placed an ng-click -> submit directive. Also, my Angular is configured for the use of the "{[{" and "}]}" delimiters so as not to interfere with TWIG.
I have searched the internet for possible answers but they all pertain to Symfony generated forms (which include validation tokens). Other answers (such as this one) don't quite describe the Angular side of things or don't describe sending the entire form.
In the end, I'm not exactly sure how to approach this. If it's too complicated I'd even settle for not using AJAX at all and submitting directly to Symfony. (actually, the only reason I want to use AJAX is to make the site feel more "snappy").
For what it's worth I'm using the latest stable versions of PHP, Symfony and Angular.JS at the time of writing.
Update
So i managed to send the data back to the controller by using an Agular JS module that enables the use of ng-model on file inputs and by using FormData like so:
var formObject = new FormData;
formObject.append('email', self.careers[index].application.email);
formObject.append('motivation', self.careers[index].application.motivation);
formObject.append('resume', self.careers[index].application.file);
formObject.append('jobID', self.careers[index].id);
$http.post('/app_dev.php/jobs/apply', formObject, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined } // Allows angular to choose the proper Content-Type
})
The only problem left now is that Symfony does not recognize my form data as being valid. The controller action looks like this (for now).
public function applyAction(Request $request) {
$jobApplication = new JobApplications(); // This is the entity I use to store everything.
$form = $this->createFormBuilder($jobApplication)
->add('jobId')
->add('email')
->add('coverLetter')
->add('file')
->getForm();
$form->handleRequest($request);
$response = array (
'isValid' => $form->isValid(), // false
'isSubmitted' => $form->isSubmitted(), // false < that's why the form is invalid
'isErrors' => $form->getErrorsAsString() // empty array
);
if ($form->isValid()) { // is not valid so the following section is not complete
$em = $this->getDoctrine()->getManager();
$jobApplication->upload();
$em->persist($jobApplication);
$em->flush();
//return $this->redirect($this->generateUrl('idea_presentation_careers'));
}
return new JsonResponse($response);
}

Showing a system error on a form after submitting via AJAX

I'm trying to find the best way in Angular to invalidate a form not due to a specific element, but due to a system-level error AFTER submission via AJAX. For example, you could put in a valid email and password (both good strings), press submit, and find out there is a system error that should trigger a generic error message on the form. Since this isn't tied to anything in the data model, what is the best way I can generically call the form 'invalid'?
<form name="loginForm" class="loginForm" ng-submit="loginSubmit(loginForm)">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email" ng-model="login.email" required>
<span class="error" ng-show="loginSubmit.email.$error">Required!</span><br>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="login.password" minlength="8" required>
</div>
<button type="submit" class="btn btn-default">Log In</button>
</form>
and...
ngModule.controller('LoginController', function($scope, $location) {
$scope['login'] = {};
$scope['loginSubmit'] = function(form) {
var loginPromise = myAsyncLoginFuncYouCanAssumeWorks();
loginPromise.done(function(){
$location.path('/');
});
loginPromise.fail(function() {
//how best to trigger a generic error in the form here?
});
};
});
As you can see, I'd like to trigger some form-wide error state after submission. It really could be as simple as adding an invalid form class to the form, but again, I'd like to know the purest Angular way to do this.
Add a label to your form with your generic error which shows upon a scope variable being true when an error occurs:
<div class="alert alert-danger" role="alert" ng-show="loginError">There was an error with your login details. Please check them and try again</div>
then when your promise fails:
loginPromise.fail(function () {
$scope.loginError = true;
});
maybe also could be nice if you have many system messages to abstract them all out into a separate service so you can inject the systemmessages service into your controller and then simply bind:
<div class="alert alert-danger" role="alert" ng-show="loginError">{{ systemMessages.loginError }}</div>
Alternatively as you use Bootstrap maybe inject the $modal service and show the error message inside a popup.
It is also important to make sure you try to use a bearer token stored in localatorage as oppose to cookies for persistence, so it doesn't get sent to the server on each request.
Anti forgery token would also be very beneficial for SPAs.
Your server could return some sort or error pay load with error key or error code.
{errors:[{key:"invalid.password"}]}
Assign the error response to your scope:
loginPromise.fail(function(response) {
$scope.errors = response.data;
});
Next, add filter to translate error key/code into error messages:
angular.module('mtApp').filter('errorFilter', function() {
return function(e) {
if (e === 'invalid.password') {
return 'Invalid password, please try again.';
}
};
});
Finally, display the appropriated errors as a list:
<div ng-show="errors">
<ul>
<li ng-repeat="e in errors">{{e.key | errorFilter}}</li>
</ul>
</div>
Optionally, you can reuse this same "$scope.erros" object combined to ng-class and control the CSS of each field with error.

Resources