How can I show different Views in their corresponding tabs? - asp.net-mvc-4

It doesn't matter which tab I click on, it doesn't show anything. I want to show the View in its corresponding tab. I don't know what to put between <div id="tab...">**HERE**</div> to show the View (see complete code bellow)
The View looks like this:
<ul id="tabs">
<li>#Html.ActionLink("Random stuff", "TabbedIndex?claimed=false", "Stuff", null, new { name = "tab1" }) </li>
<li>#Html.ActionLink("Special stuff", "TabbedIndex?claimed=true", "Stuff", null, new { name = "tab2" }) </li>
</ul>
<div id="content">
<div id="tab1"></div>
<div id="tab2"></div>
</div>
This is my script:
<script>
$(document).ready(function () {
$("#content").find("[id^='tab']").hide(); // Hide all content
$("#tabs li:first").attr("id", "current"); // Activate the first tab
$("#content #tab1").fadeIn(); // Show first tab's content
$('#tabs a').click(function (e)
{
e.preventDefault();
if ($(this).closest("li").attr("id") == "current") { //detection for current tab
return;
}
else {
$("#content").find("[id^='tab']").hide(); // Hide all content
$("#tabs li").attr("id", ""); //Reset id's
$(this).parent().attr("id", "current"); // Activate this
$('#' + $(this).attr('name')).fadeIn(); // Show content for the current tab
}
});
});
</script>

Assuming both tabs are hidden at load time, you can try this:
<script>
$('#tabs a').click(function (e)
{
e.preventDefault();
var name = $(this).attr("name"), tab = $("#" + name);
if (tab.is(":visible")) {
//if the tab is visible, do nothing
return;
}
else {
// show the selected tab and hide all the siblings/neighbours tabs
tab.show().siblings().hide();
}
});
</script>

This is the solution, I didn't change anything in the original code and script:
<div id="content">
<div id="tab1">#{ Html.RenderAction("TabbedIndex", "Stuff", new { claimed = false }); }</div>
<div id="tab2">#{ Html.RenderAction("TabbedIndex", "Stuff", new { claimed = true }); }</div>
</div>

Related

vue component compilation issue

I'm pretty new to Vue.js so bear with me. I'm working on a project where I created two new vue components, one is a tab/toggle element, the other is a cookie banner. However, when both are added to the page the cookie banner does not compile. The HTML is rendered but it still contains all the vue syntax in its uncompiled form. Does anyone see where the conflict is occurring between these two components? I don't see any errors in the console so I'm at a loss on how to begin debugging.
Component 1:
(function () {
var _instance = new Vue({
el: "#multiTrackSwiper",
data: {
tabs: {}
},
methods: {
checkActiveTab: function (index) {
if (this.tabs['active']) {
return this.tabs['active'] === index;
} else {
return index === "0";
}
},
handlerActiveTab: function (index) {
Vue.set(this.tabs, 'active', index);
}
}
});
})();
#using Sitecore.Feature.Media.Models.Components
#model List<ITrackWithCarousel>
#if (Model != null && Model.Count > 0)
{
if (Model.Count == 1)
{
<div class="c-product-details__track">
#Html.Partial("TrackWithCarousel", Model[0])
</div>
}
else
{
var index = 0;
<div id="multiTrackSwiper" class="multi-track-swiper" vue-instance v-cloak>
<ul class="nav nav-tabs">
#foreach (var track in Model)
{
<li class="nav-item">
<button id="tab_#track.Name.Replace(" ","_")" data-bs-toggle="tab" class="nav-link"
v-bind:class="{ 'active':checkActiveTab('#index') }"
v-on:click="handlerActiveTab('#index')">
#track.DisplayName
</button>
</li>
index++;
}
</ul>
#{ index = 0; }
#foreach (var track in Model)
{
<div class="c-product-details__track c-product-details__multitrack" aria-labelledby="tab_#track.Name.Replace(" ","_")"
v-bind:class="{ 'active':checkActiveTab('#index') }">
#Html.Partial("TrackWithCarousel", track)
</div>
index++;
}
</div>
}
}
Component 2:
(function () {
var _instance = new Vue({
el: "#cookie-banner",
data: {
cookieSaved: null
},
methods: {
saveSessionCookie: function () {
var expiry = (new Date(Date.now() + 600 * 1000)).toUTCString(); // 3 days 259200
document.cookie = "cookie-banner-closed=true; expires=" + expiry + ";path=/;"
this.cookieSaved = true;
}
},
mounted: function () {
if (document.cookie.includes('cookie-banner-closed')) {
this.cookieSaved = true;
} else {
this.cookieSaved = null;
}
}
});
})();
<div id="cookie-banner" vue-instance v-cloak>
<div class="cookie-disclaimer" v-if="!cookieSaved">
<div id="cookie-notice">
<div class="cookie-inner-module h-spacing">
This website uses cookies. We do this to better understand how visitors use our site and to offer you a more personal experience. We share information about your use of our site with social media and analytics partners in accordance with our Privacy Notice</a>.
<i class="fas fa-times" v-on:click="saveSessionCookie"></i>
</div>
</div>
</div>
</div>
I've tried switching both vue components into vue instances instead but that doesn't resolve the issue.
The HTML is rendered but it still contains all the vue syntax in its uncompiled form.
I don't think that you are using Vue format/syntax. So it will render what you are typed inside html.

How to wait until data is loaded for showing this v-if condition correctly in Vue.js?

It shows the no items found before the data is getting loaded., code looks like this:
<div class="columns" v-if="!products.length">
<p>No items found</p>
</div>
<div class="columns" v-if="products.length">
</div>
async getProducts() {
let res = await api.products.getProducts({
products: products
})
},
Currently, it shows that no items found until the data is loading. How can I show that message only when data is loaded and it's empty?
You can have a variable this.loading = false and toggle after loaded.
data() {
return { loaded = false }
},
methods: {
async getProducts() {
let res = await api.products.getProducts({
products: products
});
this.loaded = true;
},
}
In template.
<div class="columns" v-if="loaded && !products.length">
<p>No items found</p>
</div>
<div class="columns" v-if="loaded && products.length"></div>

How does vuejs react to component data updated asynchronously

I am very new with vuejs and recently started to try to replace some old jquery code that I have and make it reactive with vuejs. The thing is I have a component that gets information from a nodejs server via socket.io asynchronously.
When I get the data and update my component's data I see the changes when I console log it but it does not change the DOM the way I want it to do.
What is the proper way to grab data asynchronously and use it inside a component? I post some parts of my code so you can see it. I will appreciate any advice you can give me. Thanks in advance!
Vue.component('chat', {
data() {
return {
chat: null,
commands: [],
chatOpened: false,
}
},
props: [
'io',
'messages',
'channels',
'connectChat',
'roomChat',
'user',
'userId',
'soundRoute',
],
methods: {
openChat() {
this.chatOpened = true;
},
closeChat() {
this.chatOpened = false;
},
},
created() {
this.chat = this.$io.connect(this.connectChat);
this.commands.push('clear');
let self = this;
$.each(this.channels, function(index, value) {
self.chat.emit('join', {room: index, user: self.user, userId: self.userId}, function(err, cb) {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
self.channels[index].loaded = true;
}
//some more code
}
});
});
console.log(this.channels);
},
template: `
<div>
<div id="container-chat-open-button" #click="openChat" :class="{hide : chatOpened}">
<div>+90</div>
<i class="fas fa-comment-alt"></i>
</div>
<div id="container-chat" class="chat__container" :class="{open : chatOpened}">
<div id="container-chat-close-button" #click="closeChat">
<span>
<div>
<i class="fas fa-comment-alt"></i>
#{{ messages.chat_lobby_icon_title }}
</div>
<i class="icon-arrowdown"></i>
</span>
</div>
<div id="alert-chat" class="chat__container-notifications animated flash"></div>
<div class="row">
<ul>
<li v-for="channel in channels" v-show="channel.loaded === true">Channel loaded</li>
</ul>
</div>
</div>
</div>
`
});
I would expect to see the list of channels with messsages but instead I don't see the list even thought I see my channels with the loaded attribute set to true (by default they all have this attribute set to false).
My guess is that it's this part that is not working as expected.
if (messages.length > 0) {
self.channels[index].loaded = true;
}
The reactive way of doing this is by setting the full object again.
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
EDIT 1:
this.channels.forEach((channel) => {
this.chat.emit('join', {room: index, user: self.user, userId: self.userId}, (err, cb) => {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
}
//some more code
}
});
})
You'll need to add support for the rest-spread-operator using babel.

Add event listeners in VueJS 2

I am trying to add event listeners to my viewmodel once VueJS is loaded. Adding event listeners works if I do not use VueJS, so I know the code is correct but they never attach in VueJS.
<div id="app">
<div name="pageContent" id="preview">
<section class="row">
<div class="columns medium-12">
<h1>This is the top content</h1>
<p>ashcbaubvdiuavduabd</p>
</div>
</section>
<section class="row">
<div class="columns medium-6">
<h1>This is left content</h1>
<p>ashcbaubvdiuavduabd</p>
</div>
<div class="columns medium-6">
<h1>This is the right content</h1>
<p>ashcbaubvdiuavduabd</p>
</div>
</section>
</div>
</div>
<script type="text/javascript">
let editorContainer = document.getElementById('preview')
let controls = document.getElementById('defaultControls')
let cmsEditor = new CmsEditor(editorContainer, controls)
var app = new Vue({
el: '#app',
data: {
editor: cmsEditor
},
mounted: function () {
// wire up our listeners
console.log('mounted')
document.oncontextmenu = function () { return false }
let rows = this.editor.EditorContainer.getElementsByTagName('section')
for (var i = 0; i < rows.length; i++) {
console.log('section ' + i + ' : ' + rows[i].innerHTML)
rows[i].addEventListener('mouseover', function () {
console.log('mouse over event')
this.editor.SetActiveRow(this)
})
rows[i].addEventListener('dblclick', function () {
this.editor.DisplayContextMenu(this)
})
}
},
methods: {
save: function () {
console.log('save')
this.editor.Save()
},
undo: function () {
console.log('undo')
this.editor.Undo()
}
}
})
</script>
Looks like you are creating the editor on elements that will be removed from the DOM. Vue uses the content of #app as it's template, compiles the template into a render function, then replaces the DOM with the results of the render function. Given that editor is created on DOM elements that are gone now, I expect the code would fail.
You probably want to move the creation of the editor into mounted, then set up your event listeners.
FWIW, I also think you have the this issue mentioned by the commenters.
I think it should be something like this:
mounted: function() {
let editorContainer = document.getElementById('preview');
let controls = document.getElementById('defaultControls');
this.editor = new CmsEditor(editorContainer, controls);
// wire up our listeners
console.log('mounted')
document.oncontextmenu = function () { return false; };
let rows = this.editor.EditorContainer.getElementsByTagName("section");
for (var i = 0; i < rows.length; i++) {
console.log("section " + i + " : " + rows[i].innerHTML);
rows[i].addEventListener('mouseover', () => {
console.log('mouse over event');
this.editor.SetActiveRow(this);
});
rows[i].addEventListener('dblclick', () => {
this.editor.DisplayContextMenu(this);
});
}
},

localStorage and updateView + windows 8

I have some items and I mark them as favorite by pressing a button, here is the code:
function AddToFavorites() {
//called when a shop is added as as a favorite one.
//first we check if already is favorite
var favoritesArray = getStoreArray();
var alreadyExists = exists();
if (!alreadyExists) {
favoritesArray.push(itemHolder);
var storage = window.localStorage;
storage.shopsFavorites = JSON.stringify(favoritesArray);
}
}
function exists() {
var alreadyExists = false;
var favoritesArray = getStoreArray();
for (var key in favoritesArray) {
if (favoritesArray[key].title == itemHolder.title) {
//already exists
alreadyExists = true;
}
}
return alreadyExists;
}
function getStoreArray() {
//restores our favorites array if any or creates one
var storage = window.localStorage;
var favoritesArray = storage.shopsFavorites;
if (favoritesArray == null || favoritesArray == "") {
//if first time
favoritesArray = new Array();
} else {
//if there are already favorites
favoritesArray = JSON.parse(favoritesArray);
}
return favoritesArray;
}
And I have a favorites.html to present those as a list.
The problem I have is that the list doesn't update automaticly every time I add or remove items.
Here is my code for that:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Αγαπημένα</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
<link href="favoritesDetails.css" rel="stylesheet" />
<script src="favoritesDetails.js"></script>
</head>
<body>
<div class="favoritesDetails fragment">
<header aria-label="Header content" role="banner">
<button class="win-backbutton" aria-label="Back" disabled type="button"></button>
<h1 class="titlearea win-type-ellipsis">
<span class="pagetitle">Αγαπημένα</span>
</h1>
</header>
<section aria-label="Main content" role="main">
<div id="mediumListIconTextTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
<div class="mediumListIconTextItem">
<img src="#" class="mediumListIconTextItem-Image" data-win-bind="src: picture" />
<div class="mediumListIconTextItem-Detail">
<h4 data-win-bind="innerText: title"></h4>
<h6 data-win-bind="innerText: text"></h6>
</div>
</div>
</div>
<div id="basicListView" data-win-control="WinJS.UI.ListView"
data-win-options="{itemDataSource : DataExample.itemList.dataSource,
itemTemplate: select('#mediumListIconTextTemplate')}">
</div>
</section>
</div>
</body>
</html>
And here is the JavaScript code:
// For an introduction to the Page Control template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232511
var dataArray = [], shopsArray = [];
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
var ui = WinJS.UI;
shopsArray = getStoreArray();
if (shopsArray) {
for (var key in shopsArray) {
var group = { title: shopsArray[key].title, text: shopsArray[key].subtitle, picture: shopsArray[key].backgroundImage, description: shopsArray[key].description, phoneNumbers: shopsArray[key].content };
dataArray.push(group);
}
var dataList = new WinJS.Binding.List(dataArray);
// Create a namespace to make the data publicly
// accessible.
var publicMembers =
{
itemList: dataList
};
WinJS.Namespace.define("DataExample", publicMembers);
}
WinJS.UI.Pages.define("/pages/favoritesDetails/favoritesDetails.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
},
unload: function () {
},
updateLayout: function (element, viewState, lastViewState) {
}
});
})();
function getStoreArray() {
//restores our favorites array if any or creates one
var storage = window.localStorage;
var favoritesArray = storage.shopsFavorites;
if (favoritesArray == null || favoritesArray == "") {
//if first time
favoritesArray = new Array();
} else {
//if there are already favorites
favoritesArray = JSON.parse(favoritesArray);
}
return favoritesArray;
}
So how can I update the favorites HTML page when new favorites are stored/removed in the localDB? can i add event listeners there?
Is the code that stores favorites a part of the same app?
If so, I would consider adding the favorite to the underlying WinJS.Binding.list that you're using to bind to the ListView, and then store the updated list info in the DB, rather than trying to react to changes in the DB from the ListView.
Have a look at the following sample, which shows how to update a ListView dynamically:
http://code.msdn.microsoft.com/windowsapps/ListView-custom-data-4dcfb128/sourcecode?fileId=50893&pathId=1976562066
Hope that helps!