How to add a boot message before initializing a Vue component? - javascript

I have a Rails application. In the main layer I wrap the HTML code of the controllers in #app:
body
#app
= yield
Vue components in the HTML (Slim) Rails application code I describe as follows:
news-list limit="24"
news-show news-id="528"
Vue in the project is globally initialized as follows:
document.addEventListener('DOMContentLoaded', (event) => {
if (document.getElementById('app')) {
new Vue({
el: '#app',
// ...
})
The problem is that sometimes there is a delay before Vue completes the render.
That is, it looks like this:
Go to the Rails application page. We see an empty page.
Render the Vue component. There is still no news list. General information only.
API request takes place. Rendered list of news. Done.
I want to do on the side of a Rails application between 1 and 2 points that something similar to the spinner.
That is, I go to the Rails application page. Wherever there is a Vue component, I see a spinner. The spinner disappears as soon as the component finishes the render.
But I do not know how to do it...
I hope for your help. And sorry for my english.

Showing spinner before vue component is initialized can be done by rendering spinner inside element that vue is being initialized on.
<div id="app">
<!-- This content will be replaced, once the Vue component is rendered -->
</div>
Spinner after component is loaded can be done using v-if
<template>
<div>
<spinner v-if='!news' />
<list-of-news v-else :news='news' />
</div>
</template>

Related

Reload/reinit renderless vue component after morph dom node

I'm building at the moment a frontend editor.
When I'm logged in, elements are getting rendered by server (laravel) and are enclosed in a renderless vue component:
<vue-element>
<template v-slot="{ editing }">
<div class="element">
// other HTML
</div>
</template>
</vue-element>
The problem is, when I'm editing this element, css classes and other stuff are getting changed and I have to swap (morph dom) the element with a new element from the server. But when I do that, the element disappears and vue has lost track of the component.
How can I reinit the component or are there other solutions to my problem?

Include component from parent app in component contained in node_modules directory

I am working on Vue app that incorporates Vue Bootstrap Calendar, and I would like to be able to override the content of the day cell (handled by the Day.vue component) to add my own custom content inside. My thought was initially to modify the Day component to include <slot></slot> tags and pass in the custom content that way.
The problem has to do with accessing the Day component. To include the calendar in your app, you include the Calendar.vue component, which includes Week.vue, which in turn includes Day.vue. As I understand slots, I have to have the child component (Day.vue in this case) included in the component where I'm passing the data, which means it would need to be included in my own component.
If this is not possible, my other thought is to perhaps modify the library by adding another configuration prop (something like dayCustomContent) to the Calendar.vue that indicates that the Day cell content is custom content, pass that in to Calendar.vue, and then down to Day.vue, and then in the Day.vue template, have a v-if conditional based on this prop that either displays the custom content or the default cell content, something like:
<template>
<div class="day-cell" v-if="dayCustomContent">
...my custom content here...
</div>
<div class="day-cell" v-else>
...default events from my.events goes here...
</div>
</template>
I would probably then need to define a custom component to render whatever custom content I want to display, and somehow include that component within Day.vue.
So to sum up, my questions are these:
1) Is there a way to do what I need with slots?
2) For my second option, am I going down the right path? I'm open to suggestions.
UPDATE: I was able to get this done by adding a boolean customDayContent prop in Calendar.vue like so and passing it down to Week.vue and then to Day.vue:
<template>
...
<div class="dates" ref="dates">
<Week
v-for="(week, index) in Weeks"
:firstDay="firstDay"
:key="week + index"
:week="week"
:canAddEvent="canAddEvent"
:canDeleteEvent="canDeleteEvent"
:customDayContent="customDayContent"
:displayWeekNumber="displayWeekNumber"
#eventAdded="eventAdded"
#eventDeleted="eventDeleted"
></Week>
</div>
...
</template>
<script>
export default {
...
props: {
...
customDayContent: {
type: Boolean,
default: false
}
},
}
</script>
and then in Day.vue, do like I had suggested with v-if:
<template>
<div class="day-cell" v-if="customDayContent">
<custom-day></custom-day>
</div>
<div
class="day-cell"
:class="{'today' : day.isToday, 'current-month' : day.isCurrentMonth, 'weekend': day.isWeekEnd, 'selected-day':isDaySelected}"
#click="showDayOptions"
v-else
>
... existing code goes here...
</div>
</template>
The last part is referencing the CustomDay.vue component referenced in my v-if block. I want the user to be able to define the content of their own custom CustomDay.vue template in their own parent app. However, I'm having trouble trying to figure out how to do that. Following the pattern of including components already in this component, I added this in the components section of Day.vue:
CustomDay: require("../../../../src/Components/CustomDay.vue").default
? require("../../../../src/Components/CustomDay.vue").default
: require("../../../../src/Components/CustomDay.vue")
However, no matter what I try along these lines, I get an error that the relative module was not found. On top of that, I need to add it to the componentsarray only if customDayContent is true. What is the best way to do that? In a watcher or computer property, perhaps? Or another way?

Loading screen inside router outlet before lazy loaded module is initialized Angular2

I'm trying to display loading screen inside router outlet till data is received.
here is my code inside index.html
<root>
<div class="app-content text-center">
<div class="app-content-body" >
<div class="loader_div" >
<div class="loader"></div>
</div>
</div>
</div>
</root>
which works as expected but as I try to execute same code inside It remains Inside the screen even after the screen Is loaded.
One possible solution is that I can subscribe in router event and use instanceof NavigationStart and NavigationEnd.and use ngIf to remove the DOM from routeroutlet.
Is there any other easier way to solve that?
Edit1: After subscribing in router event I realized it wasn't wise decision. Because in case more than one nested router-outlet It'll show refresh screen in whole page instead of the part where the child router-outlet is located.
When I inspect DOM, <ng-component> doesn't replace <router-outlet> instead it's loaded after finishing tag.(</router-outlet>)
Is it a bug of Angular2?

Two different layouts in aurelia app

I'd like to use two separate layouts for my aurelia app. Difference between them is that one doesn't have a sidebar. Currently I'm using one layout file defined as below:
<template>
<div class="container">
<router-view></router-view>
</div>
</template>
If an active route needs this sidebar to appear I'm just putting it into its view.
What I'd like to achieve is to add another layout that would have this sidebar by default:
<template>
<require from="../common/elements/sidemenu/sidemenu"></require>
<div class="container">
<sidemenu></sidemenu>
<router-view></router-view>
</div>
</template>
So the question is - how to do this? Is it even possible with an aurelia app to have multiple layouts (or master pages, however you call those)?
Use aurelia.setRoot()
You can manually set up your application by specifying a script with configure instructions in your index.html. Typically, this is set to main.
index.html
<body aurelia-app="main">
In this script you can specify a root view model using aurelia.setRoot('root'). If no argument is provided, the convention is to use 'app'.
main.js
aurelia.start().then(() => aurelia.setRoot());
However, you can inject the aurelia object anywhere in your application, and call the setRoot function at any time to load a different root view model.
home.js
#inject(aurelia)
export class HomeViewModel {
constructor(aurelia) {
this.aurelia = aurelia;
}
doStuff() {
this.aurelia.setRoot('withSidebar');
}
}
One common use case for this is having a login page, and I've created a complete template for this use case that you can review, clone, or fork here: http://davismj.me/portfolio/sentry/

How to insert and nest a component within another in ractive.js when the components are in two separate files?

I've been learning ractive.js for few weeks and I am trying to build a feature within a larger webpage that lists out three items. Each list has it's own title and description. Each list is a component (the list-component) with its own styles and javascript (each component is a separate html). I also have a component (description-component) that is responsible for writing out the title and description and it is in a separate html file. The problem I am having is 'importing' or bringing in that description-component into the list-component so the output in the main index html file is this:
<description title="Name">
<list-items/>
</description>
So far I looked into the yield directive and tried some examples but that was a simple example using two components on the same document. I am not sure if that is the correct way. This is what is in the description-component html file:
<p>{{title}}</p>
I tried using ractive-load.js and load up the description-component html for every list-component file like in this example on their github:
Ractive.load({
Foo: 'my-components/foo.html',
Bar: 'my-components/bar.html'
}).then( function ( components ) {
var foo = new components.Foo({
el: 'body',
data: { ... }
});
var bar = new components.Bar({
el: 'body',
data: { ... }
});
}).catch( handleError );
and that seemed like an overkill and thought there must be a better way. How would I go about approaching this?
The loading of Ractive components is not directly related to handling nested components and passing yielded content to components.
Ractive.load works by fetching one or more html files that it then resolves to Components. From your code samples, it seems you've got that part working. For larger apps, I usually use a build tool plugin to pre-bundle all my Ractive components so they're deliverable in one file or even rolled into my main bundle js.
However the components are made available, they need to be registered on either the consuming component (or a view parent) or globally:
Ractive.load({
Foo: 'my-components/foo.html',
Bar: 'my-components/bar.html'
}).then( function ( components ) {
Ractive.components.foo = components.Foo;
Ractive.components.bar = components.Bar;
// now create your actual top-level view instance(s)
}).catch( handleError );
In component architectures, you create trees or bushes of components. I usually only have one top level app component, but it certainly is feasible to create multiple trees that start at different places in the DOM.
For simplicity, continuing on the above example, let's create a generic ractive instance that uses the two components Foo and Bar we registered (notice we use the property name we assigned to Ractive.components):
const ractive = new Ractive({
el: document.body,
sayHello() {
alert('hello from main Ractive instance');
},
template: `
<h1>my kewl app</h1>
<foo>
<h3 on-click="sayHello()">hello world</h3>
<bar bizz="{{buzz}}"></bar>
</foo>
`
});
In this case we're passing some content (html and our bar component) to the foo component by including it as the <foo> element content.
How this content is used depends on the foo template. There are two choices:
<div>
<h2>foo component template</h2>
{{>content}}
<p>some more stuff</p>
</div>
In this example, we're using the built-in partial "content" to tell the template to put provided content in the {{>content}} slot. In this case the provided content is passed like a partial, and any directives will be applied against the foo component. So in this example, clicking on the h3 header will try and run foo.sayHello(). And when passing the bizz data to the bar component, Ractive will start looking in the foo component for buzz.
Often, this isn't what you want. What you would rather have happen is for the parent to own the directives. So instead the foo template would look like:
<div>
<h2>foo component template</h2>
{{yield}}
<p>some more stuff</p>
</div>
And now when h3 is clicked, it calls the main ractive.sayHello() method as the content was passed to be rendered in the DOM by the the foo component, but it was still owned by the passing instance. Likewise Ractive will start look for buzz in the main instance, not foo.
With yield you can also name multiple partials to be passed:
<!-- "foo" template: -->
<div>
<header>{{yield header}}</header>
<section>
<div>something here</div>
<div>{{yield message}}</div>
</section>
</div>
<!-- using "foo": -->
<div>
<foo>
{{#partial header}}
<h2>This is the header to use</h2>
{{/partial}}
{{#partial message}}
<p>This is the message to use, with a bar component to boot</p>
<bar></bar>
{{/partial}}
</foo>
</div>

Categories

Resources