Durandal: Switch views without reloading model - durandal

I am using duraldal v2, and on one of the pages I want to switch views without re-activating the model.
To achieve this I leverage approach from v1 using compose binding with view connected to observable, changed by inline click binding in my navigation links:
<ul class="nav nav-tabs">
<li data-bind="css: {active:activeView() == 'view1'}">View 1</li>
<li data-bind="css: {active:activeView() == 'view2'}">View 2</li>
. . .
</ul>
<div data-bind="compose: {view: activeView, activate: true, area: 'settings', transition: 'entrance', cacheViews: true}"></div>
VM:
define(['knockout'], function (ko) {
return {
activeView: ko.observable()
,setView: function(view) {
this.activeView(view);
}
,activate: function (args) {
var self = this;
return system.defer(function(dfd) {
self.setView('view1');
dfd.resolve(true);
}).promise();
}
. . .
I believe this is an awkward way, and in v2 there should be a more elegant way to do this. As far as understand, I cannot use child router, like in ko samples, because it reloads model every time.
Any ideas?

Looks fine to me.
Is it the inline functions you dislike? You could just do:
<ul class="nav nav-tabs">
<li data-bind="css: {active:activeView() == 'view1'}">View 1</li>
<li data-bind="css: {active:activeView() == 'view2'}">View 2</li>
. . .
</ul>
and:
return {
activeView: ko.observable()
,setView1: function() { this.activeView('view1') },
,setView2: function() { this.activeView('view2') },
...
If this functionality is really common for you, you might consider turning it into a widget which could potentially box up a pattern like this.

Related

Space disabled on concatenation

I have an array which looks like this
skills: [
"HTML5",
"CSS3",
"SCSS",
"Bootstrap",
"JavaScript",
"Vue.js",
"PHP",
"MySQL",
"Symfony"
]
And in my template I'm doing something like
<ul>
<li v-for="(skill,index) of skills" :key="index">{{ skill + ', '}}</li>
</ul>
But what I get is
HTML5,CSS3,SCSS,Bootstrap,JavaScript,Vue.js,PHP,MySQL,Symfony,
How do I able the spaces?
Btw is there a better way to concatenate the elements of my array? I first used join() like that
<ul>
<li v-for="(skill,index) of skills.join(', ')" :key="index">{{ skill }}</li>
</ul>
But not only the spaces are still disabled but it returns every character of my elements, I don't know why
Like
<ul data-v-c226fde6="">
<li data-v-c226fde6="">H</li>
<li data-v-c226fde6="">T</li>
<li data-v-c226fde6="">M</li>
<li data-v-c226fde6="">L</li>
<li data-v-c226fde6="">5</li>
<li data-v-c226fde6="">,</li>
<li data-v-c226fde6=""> </li>
<li data-v-c226fde6="">C</li>
<li data-v-c226fde6="">S</li>
<li data-v-c226fde6="">S</li>
<li data-v-c226fde6="">3</li>
...
EDIT: otherwise what I could just do is
<ul>
<li v-for="(skill,index) of skills" :key="index">{{ skill + ','}}</li>
</ul>
And then adding some padding-right to the li but I don't know if it's good practice + I don't know how I would remove the comma after the last element
Inside html template such as li you have to specify space characters using ;
<ul>
<li v-for="(skill,index) of skills" :key="index">{{ skill + ','}} </li>
</ul>
also checkout https://www.w3schools.com/html/html_entities.asp
to learn more about HTML entities
With that code you will get a list of <li></li> elements with each skill looking like this:
<li>HTML5,</li><li>CSS3,</li><li>...
just add a class or even a style with a margin to the right to add a little space between the elements either:
<ul>
<li v-for="(skill,index) of skills" style="margin-right: 5px" :key="index">{{ skill }}</li>
</ul>
better:
<ul>
<li v-for="(skill,index) of skills" class="class-with-some-margin-right" :key="index">{{ skill }}</li>
</ul>
You can add the ', ' after each item using CSS like so:
var example1 = new Vue({
el: '#example-1',
data: {
skills: ["HTML5", "CSS3", "SCSS", "Bootstrap", "JavaScript",
"Vue.js", "PHP", "MySQL", "Symfony", ]
}
})
/* styling help:
Q: https://stackoverflow.com/questions/1517220/
A: https://stackoverflow.com/a/1517228/7505395 */
#example-1 {
display: inline;
list-style: none;
}
#example-1 li {
display: inline;
}
#example-1 li:after {
content: ", ";
}
#example-1 li:last-child:after {
content: "";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<ul id="example-1">
<li v-for="(sk, idx) in skills" :key="idx">{{ sk }}</li>
</ul>
This answer incorporates it into Vue, for the pure css answer, go here.
As always you can also combine normal text inside Vues {{}} like so : {{ skill + ', ' }} - not sure if thats helpfull though as it applies to all replacement and you end up having a trailing ,

VUEJS V-FOR acting weird

I need a simple v-for to render object properties in a list.
<div
v-if="profileData && profileData.length > 0"
>
<ul
v-for="value in profileData"
:key="value.id"
>
<li>{{value.id}}</li>
</ul>
</div>
In the script:
profileData: {},
created() {
const userId = this.$route.params.userId
const currentUser = this.$store.getters.currentUser
const profileData = this.$store.getters.profileData
console.log('profileData in seeProfile: ', profileData) }
(profileData comes from an api response)
I did exactly the same in two other pages (just rendering different objects) and it worked.
With this code, in the console I get value is undefined.
If I remove :key="value.id" (it becomes red in the editor but it still works), and instead of the list items I type only {{ value }}}, then the object properties get rendered ( but in the ugly format of a js object). How can it be? What am I doing wrong?
Thank you
Your v-if will never show even if profileData has data, because you can't directly check for the length of an Object in javascript.
A few things:
You can't check for the length of an Object, it will return undefined. If you must use an object, then you'd have to check for Object.keys(obj).length.
let obj = {
first: {
name: "first",
meta: "data"
},
second: {
name: "second",
meta: "data"
}
};
console.log("Object.length is: ", obj.length);
console.log("Object.keys().length is: ", Object.keys(obj).length);
You're being redundant, you don't need to check for profileData and its length (and you don't need to > 0), you could simply check for v-if="Object.keys(profileData).length". If the Object has zero entries, then it won't show because if(0) is false.
I'd strongly recommend to work with arrays to iterate with v-for. I'd use computed properties and return an array, and iterate through that. Object reactivity works non-intuitively in JS, so you'll be scratching your head later when you try to find out why stuff isn't updating on your view:
computed: {
listData() {
let list = Object.values(this.profileData);
return list;
}
}
In view:
<div v-if="listData.length"/>
Also, don't use the array's entry index as your :key, because if you have another array with v-for, you'll have duplicated keys in your model. I'd use something like v-for="(item, key) in list" :key="'list-item-' + key"
Put the v-for on the li, not the ul.
<ul>
<li v-for="value in profileData"
:key="value.id">{{value.id}}</li>
</ul>
Also , if your your profileData is an object and not an array, you need to decide if you want to loop through the keys or values.
<ul>
<li v-for="value in Object.values(profileData)"
:key="value.id">{{value.id}}</li>
</ul>
<ul>
<li v-for="value in Object.keys(profileData)"
:key="value.id">{{value.id}}</li>
</ul>
Or use Vue's default behavior.
<ul>
<li v-for="(value,key) in profileData"
:key="value.id">{{value.id}}</li>
</ul>
The api has been changed, so the working code is slightly different from the original one.
Here's the template:
<div
v-if="listData.length"
>
<ul>
<li>Name: <b>{{ profileData.user.first_name }}</b></li>
<li>Surname: <b>{{ profileData.user.last_name }}</b></li>
<li>Username: <b>{{ profileData.user.username }}</b></li>
<li>Car: <b>{{ profileData.if_cars_owned }}</b></li>
<li v-if="profileData.if_cars_owned === true">
Car model: {{ profileData.user_car_type }}
</li>
<li v-if="profileData.if_cars_owned === true">
Car power: {{ profileData.user_car_powered_by }}
</li>
<li>Motorcycle: <b>{{ profileData.if_motorcycle_owned }}</b></li>
<li v-if="profileData.if_motorcycle_owned === true">
Motorcycle model: {{ profileData.user_motorcycle_characteristic }}
</li>
</ul>
</div>
Script:
created(){
const profileData = this.$store.getters.profileData
this.profileData = profileData
console.log('profile data in profilo: ', profileData)
},
I've also updated
<div
v-if="listData.length"
>
and in the script
computed: {
...,
listData() {
let list = Object.values(this.profileData);
return list;
}
},
following the advice of #Adrián S. Basave.
Thanks to anyone who tried to help.
x

Translate Laravel Spark plan features

I was wondering what would be the best way to display my Spark plan features in multiple different languages.
Let's say I have the followings features
Spark::plan('Premium', 'monthly-artist-premium')
->price(10)
->trialDays(14)
->features([
'Online profile', 'Access To More Features',
]);
I thought about doing something like this using Laravel's translation tool and the translation keys
Spark::plan('Premium', 'monthly-premium')
->price(10)
->trialDays(14)
->features([
'base.Online_profile', 'base.Access_to_more_features',
]);
And then when rendering the plans using Vue I would do something like this, but it's not translating.
<li class='pricing-feature' v-for="feature in plan.features">
#lang('#{{ feature }}')
</li>
Any idea how I could implement this to handle multiple languages?
On Laravel Spark 7.0, I've managed to translate the Feature List by:
Appending the translated keys to the language .json files.
On the SparkServiceProvider::booted() method using those keys for the feature list.
On resources/views/vendor/spark/modals/plan-details.blade.php and spark/resources/views/modals/plan-details.blade.php
<!-- Modal Body -->
<div class="modal-body">
<ul class="plan-feature-list p-0 m-0">
<li v-for="feature in detailingPlan.features">
#{{ feature }}
</li>
</ul>
</div>
Change to:
<!-- Modal Body -->
<div class="modal-body">
<ul class="plan-feature-list p-0 m-0">
<li v-for="feature in detailingPlan.features">
#{{ __(feature) }}
</li>
</ul>
</div>
Not the best solution, but here's what I ended up doing:
Spark::freePlan('Basic')
->features([
'free_plan'
]);
Then when showing the plans in register-common.blade.php
I did something like this with the v-if conditions for each different plans
<ul v-if="plan.features[0] === 'free_plan'" class='pricing-feature-list'>
<li class="pricing-feature">
#lang('base.Online_profile')
</li>
</ul>
You can use the HandleInertiaRequests middleware to translate the strings before they are shared in the request.
For more info: Shared data
Sample code:
namespace Application\Http\Middleware;
use Domain\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
/**
* Define the props that are shared by default.
*
* #return array
*/
public function share(Request $request)
{
$this->translatePlans();
...
}
private function translatePlans(): void
{
$sparkConfig = config('spark');
$plans = $sparkConfig['billables']['account']['plans'];
$translatedPlans = [];
foreach ($plans as $plan) {
$plan['short_description'] = __($plan['short_description']);
$translatedPlans[] = $plan;
}
$sparkConfig['billables']['account']['plans'] = $translatedPlans;
app('config')->set('spark', $sparkConfig);
}
}

How can I switch the `selected` style easily in Vue?

How can I switch the selected style easily?
In the template I have a nav:
<ul class="nav">
<li class="nav-item selected" #click="clickLi(0)"><router-link to="/">首页</router-link></li>
<li class="nav-item" #click="clickLi(1)"><router-link to="/data-center">数据中心</router-link></li>
</ul>
in the methods:
clickLi(page_num){
// there I have to clear all the li `selected` style, then add the style to the page_num.
}
in Vue whether there is a better way to realize this effect?
Take a look at Cass and Style Binding Docs
pseudo example below, also added some shorcuts to vue-router to avoid the router-link component, you just can bind the :to to the <li>
<li
v-for="(item, $index) in routes"
#click="selectedIndex = $index"
:class="{'item-selected': $index == selectedIndex}"
:to="item.path">{{ item.name }}</li>
// on component
data() {
return {
selectedIndex: null,
routes: [{path:'/', name: 'home'},{path:'/data-center', name:'data center'}]
}
}
in the data you return a selected_num param:
data() {
return {
selected_num: 0
}
}
in your template:
<ul class="nav">
<li class="nav-item" :class="selected_num===0 ? 'selected' : ''" #click="selected_num=0"><router-link to="/">首页</router-link></li>
<li class="nav-item" :class="selected_num===1 ? 'selected' : ''" #click="selected_num=1"><router-link to="/data-center">数据中心</router-link></li>
</ul>
you even do not need the method.

Is there a better way to get dom index in Dojo?

im used to use jquery .index() and need some help to understand how to works Dojo Nodelist. My code:
<ul id="indexof1">
<li><a id="1">domain_1.net</a></li>
<li><a id="2">domain_2.com</a></li>
<li><a id="3">domain_3.org</a></li>
<li><a id="4">domain_4.net</a></li>
</ul>
require(["dojo/query", "dojo/on", "dojo/NodeList-traverse"], function(query, on){
query("ul a").on("click", function(){
console.log(query(this).parent().parent()[0]); // Returns UL
console.log(query(this)[0]); // Returns the node has been clicked A
alert(query('a', query(this).parent().parent()[0]).indexOf(query(this)[0]));
});
});
https://jsfiddle.net/wwghes79/4/
This should help you with the requirement you have:
require(["dojo/query", "dojo/on", "dojo/NodeList-traverse"], function(query, on){
query("ul a").on("click", function(e){
console.log(query("#indexof1 li a").indexOf(this)); //returns index
});
});