This is probably a very basic question so bear with me I come from a jquery background and I'm a bit confused on how to structure code in Vue.
I have a laravel project set up with vue.js so one app.js file.
Let's say I have a set of directives in that app.js for registering a user :
const app = new Vue({
el: '#app',
data: {
errors: [],
email:null,
first_name:null,
last_name:null,
...
},
methods:{
register: function (e) {
//Do stuff
}
});
Now what if I now want to write code for another page, let's say a crud application to list the users where should I write the Vue code ?
I guess I could add to my app.js file but I will soon end up with a huge app.js file and I do not want to expose methods in pages that do not require them.
What is the correct way of doing this, I guess there must be some kind of routing going on somewhere ?
In Vue, you can make use of components for implementing different pages ad even inside a page you can then make child components.
Each component consists of the template i.e., HTML code, script, and its respective style.
Making use of components makes the code more maintainable, reusable, and understandable.
You can check this link for understanding more about the components: https://v2.vuejs.org/v2/guide/components.html
For routing, there is an official router for Vuejs which is Vue router. You can find more on the router on https://router.vuejs.org/.
Related
All throughout the Vue 3's documentation, they do the following when showing example code in a component:
Vue.createApp({})
I've never had to do this though, I simply use
<script>
export default {
name: 'example-component-xd'
}
</script>
What am I missing here? I just started learning Vue 3, and I haven't learnt any other versions of Vue before. This question is more just a curiosity I have, as it doesn't impact my ability to understand the documentation, but I thought that it may have something to do with the history of Vue, ES6 or best practices etc. Here is another example:
In Vue 3, Vue.createApp({}) replaces new Vue({}) from Vue 2. This line of code is typically in your entry file, where you mount the app (not in your component files).
The code in the <script> tag you showed is for single file components, and you can continue using that style. Never would you use Vue.createApp() or new Vue() in those single file components.
You can checkout an example Vue 3 app that shows Vue.createApp() in src/main.js, while the single file components in src/components use the Options API that you're also using.
So I am trying to use Vue & Django with django-webpack-loader (not DRF), but I'm just getting stuck whenever I try to actually feed Vue data. I am just trying to convert a static Django template to a (.vue) Vue template but I'm not sure how Vue is meant to handle that data?
I'm under the impression that you do something along the lines of this in the script tags:
new Vue ({
data: function () {
game: {{game}}
}
});
And then reference this with {{game.title}} etc in the main html of the Vue template, using Django {% verbatim %} tags so they don't interfere right?
But this just doesn't do anything, Django doesn't give Vue anything and I just get error 'game:' is defined but never used and error 'game' is not defined. I'm just not sure where to go from here and I just need some sort of help...
If I understand correctly that you're trying to have Django directly serve *.vue Single File Components (SFC), then I don't know of anyway that could work. These files will need to be separate from Django so that Vue/webpack can build them into JS.
But there is another way to integrate Django Templates with Vue SFCs. I (humbly) recommend reading my series of articles starting with Vue + Django — Best of Both Frontends. This will walk you through all the steps to do so, freely mixing Django Templates and Vue SFC wherever/whenever you deem appropriate.
There's a companion repo with a ready made Django+Vue starter app.
Otherwise, if I've misunderstood your intention, please clarify so we can better answer.
The problem is that I have lots of HTML templates that include < script > tags with AngularJS code in them. My current project is using Angular 8, and one critical part of it consists basically in usign all the AngularJS templates that I have inside this new project doing as little work as possible (due to the fact that in the future other users will have to be able to add new templates to the project).
I have to be able to add this HTML AngularJS templates to my current project. For that I am using ngUpgrade, but it has been quite misleading and I have not been able o achieve this.
Here is one of the templates, simplified as much as possible for this question:
HTML
<body>
<script>
angular.module('myApp', [])
.controller('myController', function ($scope, $timeout) {
$scope.local = 'es';
BigFunction($scope, $timeout);
});
var texts = {
title: {
en: 'A simple title',
};
// other constanst go here
function BigFunction(scope, timeout) {
scope.texts = texts;
scope.$watch('content', function(end, ini){
// some logic when 'content' changes
}, true);
// more logic
}
<script>
<div>
<!-- Lots of tags using ng-bind and other AngularJS tools -->
</div>
</body>
I have to integrate it touching it as little as possible. I don't know if I should separate the logic of the script from the HTML, or if I could import it as it is right now and work. Take into account that I have no previous experience working with AngularJS, I am an Angular 2+ developer.
If I have to separate the logic from the template, where do I put the angular.module? And the constants?
I would suggest you using Web Components
Basically you would have to wrap the AngularJS application as a Web Component and then import it to your Angular 8 app. You would end up with something like this
<my-angularjs-app></my-angularjs-app>
It could be imported in any component in your Angular 8 application.
There is no official solution on how to do it, but there are good examples there. I would suggest you following this one:
UPGRADING WITH WEB COMPONENTS: FROM ANGULARJS TO ANGULAR
Once you are there scroll down until you see
Exposing AngularJS Components As Custom Elements
After you manage to bundle up the AngularJS application as a web component, having the whole application in one script (main.js), you would have to copy it to your Angular 8 app.
In your Angular 8 app, create folder assets/web-components/my-angularjs-app
Copy the file you generated from AngularJS (main.js) to a folder you just created
Import the file in angular.json - Add this to scripts: assets/web-components/my-angularjs-app/main.js
Your AngularJS app is now imported but there is one more thing we have to do before you can use it in your components. You have to let Angular know that there will be some selectors which Angular might not be aware of. To do this, go to your app.module.ts and add schemas: [CUSTOM_ELEMENTS_SCHEMA]
You can now use your AngularJS app anywhere you prefer by using the selector you declared (This part is in the link I posted). <my-angularjs-app></my-angularjs-app>
I recommend the ng-book, it has a full chapter about hybrid apps. Note the UpgradeAdapter in the book is deprecated. But many concepts are still valid.
NgUpgrade is the official package to do what you want.
The first thing you must do is to put the script in a proper file. And not in the HTML. Then you can do serious coding.
You must follow the NgUpgrade doc to make the 2 apps run.
https://angular.io/guide/upgrade#bootstrapping-hybrid-applications
you need one bootstrap for AngularJS:
angular.bootstrap(document.body, ['heroApp'], { strictDi: true });
And one bootstrap for Angular 8:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule);
You will notice the NgModule has a reference line towards AngularJs.
The code they give is for JIT compilation.
A production build in AOT compilation will look like this:
import { enableProdMode } from '#angular/core';
import { platformBrowser } from '#angular/platform-browser';
const { AppModuleNgFactory } = require('../aot/src/app/AppModule.ngfactory');
enableProdMode();
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
--
You must know a directive in AngularJS corresponds to what is meant by component in Angular 8.
You have 2 options.
inject Angular 8 components in ANgular JS directives
inject Angular JS directives in ANgular 8 components.
I recommend to upgrade ANgularJs Components inside Angular 8 components. Like that in the future you converge towards moving to Angular 8 only.
It will require some work to copy each AngularJS component in an Angular 8 component.
To reduce work, run Angular JS as it is. And put Angular 8 directives outside of the templates of AngularJS.
<div id="angularjs-root"></div>
<div my-unaltereted-angular8-component></div>
It may be I am wrong and you cannot put them deparately. But it is worth a try.
Myself, I inject DOM elements dynamically from Angular8 to a JQuery application. But you cannot do that in AngularJS since it has lifecycle for all nodes. HOwever, with some carefulness, removing change detection rendering for the node, a huge tree of nodes from Angular 8 could be injected in the template of an ANgularJs template, and this wihtout downgrading.
This is what I use.
addComponentToElement({ component, element }: { component: any, element: any }) {
// Create a component reference from the component
const componentRef = this.componentFactoryResolver
.resolveComponentFactory(component)
.create(this.injector);
// Attach component to the appRef so that it's inside the ng component tree
this.appRef.attachView(componentRef.hostView);
// Get DOM element from component
const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
// Append DOM element to the body
element.appendChild(domElem);
// detect changes
componentRef.changeDetectorRef.detectChanges();
}
The AppModule will need to add each component inside entryComponents:
entryComponents: [ InjectedCOmponent1m InjectedCOmponent2]
I keep getting this same error. My code is shown here:
https://codesandbox.io/s/88o66kkyz9
For those who need an idea of what it does, it gets lists of IPs from a websocket in the main Vue.js. I want to create a component to layout information about the IP so I can just iterate through that in the app.vue render template I know this is basic, but I'm really new to vue. but god, so much better than angular messiness to me.
I'm pretty new to Vue and I'm trying to create my first app with it (I have worked through a whole tutorial though so it's not like I'm completely new to it). I come from a Java/Grails background so this new "frontend oriented webapps" is still pretty confusing to me. I'm using Vue 2 with webpack.
The problem I'm having is running methods when the app initializes which creates data in the App.vue component (which I'm assuming is the root component, is that correct?) and then accessing this data in child components. So specifically what I'm trying to do is on in the 'created' life cycle hook I want to check if the user is logged in or not and then I want to update my navbar accordingly (show a login button if not, else show the user's name, for example).
I haven't even quite figured out how exactly I'm gonna determine if the user is logged in yet coz so far I've only been trying to create dummy data in the App.vue component and then accessing it in child components. Everywhere that I've researched says that I should use an event bus but (I think) that's not gonna work for me (correct me if I'm wrong) coz the only examples I can find is all $emit functions being called on user events (like button clicks) and I want it to have the data globally accessible (and mutate-able) everywhere all the time.
So I'll try to show you some code of what I had in mind:
App.vue:
...stuff...
data() {
return {
authToken = '',
userdetails = {},
loggedIn = false
}
},
created: function() {
// check browser storage for authToken
if(authToken) {
// call rest service (through Vue Resource) to Grails backend that I set up
// beforehand and set this.userdetails to the data that gets returned
if(this.userdetails) {
this.loggedIn = true;
}
}
}
...stuff...
Home.vue:
<template>
<div class="home">
<nav-bar></nav-bar>
</div>
</template>
...stuff
NavBar.vue:
<template>
<div class="navBar">
<div v-if="loggedIn">Hi {{ userdetails.name }}</div>
<div v-else>Please log in before continuing.</div>
</div>
</template>
Please excuse if any of that code has any mistakes in it, it's just to show more or less what I'm trying to do and I made most of it up right now. So the main question: How do I go about getting the v-if="loggedIn" and {{ userdetails.name }} part to work (coz obviously the way it's set up now that won't work, right?). And then besides that, any general advice on "global variables" and data flow in Vue js will be appreciated coz I believe that my server-side app mentality might not work in front-end javascript apps.
To get Data from parent component you can use this.$parent.userdetails in the child component.
But a much better way is to use props like this
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-with-Props
Lite pattern, not for complex sharing tasks
You can access global app component everywhere (routes, components, nested components) with the predisposed field:
this.$root
Notes for overengineers: It is officially suggested as "lite" approach in vue guide: https://v2.vuejs.org/v2/guide/state-management.html.
Obviously, it is not a architectural patternized solution, but ok for just sharing a variable or two.
In these case this lean approach avoids developer to import a full sharing framework like vuex, heaving development, know-how requirement and page download, just for such a trivial sharing task.
If you dislike lite approaches, even when appropriated and officially suggested, maybe is not how to share the problem, but which framework to use.
For instance, Angular is probably best fitted for heavy engineered approach, and I not mean one is better than the other, I mean different instruments suite best for different task and approaches, I too use it when need to realize a heavier and more complex solution, in these case even with vuex I could not handle all the components interaction and realtime data sharing.
Following methods are not recommended by other developers or vue itself.
this.$root;
or
this.$parent.userdetails
In my opinion the best way to share any data across components is using vuex state management.
Here is the official documentation link: https://vuex.vuejs.org/#what-is-a-state-management-pattern