My goal is to write some reusable code to render basic navbars, since it would be a very repetitive task. The following features are my first goal:
Each page should be rendered in a foreach binding
Each page should grab the active state reading current route
Each page should be loaded either async or inline
Here's my first attempt. I want markup to be something like this
<ul data-bind='foreach: pages'>
<li>
<!--
[1]
Here a toggler is needed for active/no-active status,
i.e. a class binding.
-->
<a data-bind='html: caption, click: $data.load'></a>
</li>
</ul>
Each page item should look something like this
function PageItem(id, caption) {
this.id= id;
this.caption = caption;
this.page = pager.page.find(id);
this.load = function() {
// [2]
// Code here to trigger page load,
// i.e. this.page.async(someCallback, this.id);
}
this.active = function() {
// [3]
return this.page.isVisible();
}
}
Usage goal:
function VM() {
var self = this;
self.pages = [];
self.pages.push(new PageItem('dashboard', "<i class='fa-icon-home'></i>"));
self.pages.push(new PageItem('offerJoin', 'Offer'));
}
var vm = new VM();
pager.extendWithPage(vm)
ko.applyBindings(vm);
pager.start('dashboard');
I need help with [1], [2] and [3] topics. Any pointer?
Here is how you can built it. This is only example.
The structure of app is like this which you can customize.
app/
/index.js
/index.html/
/lib/
/pager.js
/require.js
/knockout-3.0.0beta.js
/views/
/test.html
/test1.html
And here is how you can do it.
First index.html
<html>
<head>
<script data-main="index.js" type="text/javascript" src="lib/require.js"></script>
</head>
<body>
<div class="container" style="padding-top: 30px;">
<span id="span" onclick = 'clickme(this)'>I am span</span>
<div data-bind="page: {id: 'start' , title : 'First Page'}">
you are currently viewing the content of first page. <br />
first child<br />
second child<br />
<br />
<div data-bind="page: {id: 'deep', title : 'Second Page',role: 'start', source: 'views/test1.html'}">
you are currently viewing the content of first page inside First Page.
<br />
<a data-bind="page-href :'../second'" >Second Child</a>
</div>
<div data-bind="page: {id: 'second', title : 'Second Page', source: 'views/test.html'}">
you are currently viewing the content of second page inside Second Page.
<br />
<a data-bind="page-href :'../deep'" >First Child</a>
</div>
<br />
<br />
<br />
Go to Structure
</div>
<div data-bind="page: {id: 'structure', title : 'Second Page'}">
you are currently viewing the content of second page.<br />
Go to Start
</div>
</div>
</body>
</html>
Next index.js
requirejs.config({
shim:{
bootstrap:['jquery'],
hashchange:['jquery']
},
paths:{
jquery:'lib/jquery-1.10.2',
knockout:'lib/knockout-3.0.0beta',
pager:'lib/pager'
}
});
requirejs(['jquery','knockout','pager'], function ($, ko,pager) {
function PagerViewModel(){
var self = this;
}
$(function () {
pager.Href.hash = '#!/';
pager.extendWithPage(PagerViewModel.prototype);
ko.applyBindings(new PagerViewModel());
pager.start();
});
});
And the views to load
test.html
<h3>Second Page</h3>
<p>This is a test view loaded by pager.js</p>
<p>The view loads with ajax request when the main page loads</p>
<p>All the pages that need to be loaded are loaded only once with ajax</p>
<p>while navigating the pages are not loaded again</p>
<a data-bind="page-href :'../deep'" href="#">First Child</a>
test1.html
<h3>First Page</h3>
<p>This is yet another page loaded by pager.js</p>
<a data-bind="page-href :'../second'" href="#">Second Child</a>
You can see how i created navigation bar the titles are first child and second child.
You can download the demo here
Related
a button corresponding to a prompt box,each box is different shells;Although implements the desired function, but my code is too complicated, and that there is no simple way. how can I do? This is my code
<--html button-->
button1
button2
...
button100
<--html pop box-->
<div class="note1" style="display:none;">
<img class="title-css" src="note1.png">
<p class="one">note1</p>
</div>
...
<div class="note100" style="display:none;">
<img class="title-css" src="note100.png">
<p class="one">note100</p>
</div>
<--angular js-->
$scope.showRulePop = function(index) {
for(var i=1;i<=8;i++) {
$('.note'+i).hide();
}
$('.note'+index).show();
};
Well first of all, don't use jQuery, unless your in the directive level of angular jQuery have nothing to do there.
First let's get rid of the links part using a simple ng-repeat :
<--html button-->
<div ng-repeat="button in buttons">
{{button.label[i]}}
</div>
// JS in the controller
$scope.buttons = [{
label:'button1'
},{label:'button2'}];
As you can see i declare in the javascript all your buttons and i just loop over it.
Now the "bombox" or whatever it is let's make it a simple template :
<div class="{{currentnote.class}}" ng-if="currentNote">
<img class="title-css" src="{{currentNote.img}}">
<p class="one">{{currentNote.content}}</p>
</div>
// and use ng-repeat for the eight first when there is no button selected
<!-- show 1 to 8 if note current note selected -->
<div ng-repeat="button in buttons1To8" ng-if="!currentNote">
<div class="{{button.note.class}}">
<img class="title-css" src="{{button.note.img}}">
<p class="one">{{button.note.content}}</p>
</div>
</div>
// JS
$scope.buttons = [{
label:'button1'
note:{class:'note1', img:'note1.png', content:'note1'//assuming no HTML or you' ll need something more
}},{label:'button2', note:{...}}, ...];
$scope.showRulePop = function(index){
$scope.currentNote = $scope.buttons[index].note;
}
$scope.buttons1To8 = $scope.buttons.slice(0, 8);//0 to 7 in fact
That's all, no need of jQuery.
I inherited an application and trying to figure out way scripts on the tabs are loading more than once.
It is an basic bootstrap tab feature that loads partial views.
<ul class="nav nav-tabs" id="myTab">
<li class="active"><a data-target="#home" data-toggle="tab">Home</a></li>
<li><a data-target="#reputation" data-toggle="tab">Reputation</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="home">
#Html.Action("SocialConfig", "Admin", new {area = "Admin", accountID = ViewBag.AccountID})
</div>
<div class="tab-pane" id="reputation">
#Html.Action("ReputationConfig", "Admin", new { area = "Admin", accountID = ViewBag.AccountID })
</div>
</div>
There is an JavaScript file included on each partial view. Each script will get loaded as many times there are #Html.Action
So for instance I have 2 #Html.Action so the script on the SocialConfig view will load twice and the script on the ReputationConfig will also load twice.
How can i stop this silly behavior.
If you are using jQuery to add some event handlers then check if the selectors are filtering elements of both partials views. If so you should change your selector in way that it is contextualized to your partial view.
Ex:
Partial view 1
<div>
<button class="button" type="button">Click me</button>
</div>
<script>
$(".button").click(function () { alert("click on button") });
</script>
Partial view 2
<div>
<a class="button">Click me</a>
</div>
<script>
$(".button").click(function () { alert("click on link") });
</script>
A example of contextualized events:
Partial view 1
<div id="my-partial-view-1-container">
<button class="button" type="button">Click me</button>
</div>
<script>
$("#my-partial-view-1-container").find(".button").click(function () { alert("click on button") });
</script>
Partial view 2
<div id="my-partial-view-2-container">
<a class="button">Click me</a>
</div>
<script>
$("#my-partial-view-2-container").find(".button").click(function () { alert("click on link") });
</script>
When working with partialview try to avoid loading script, add it instead in the main view. Why not just add it in the current view?
I have the following markup inside my .cshtml Partial View and it is placed in the Views/Shared folder:
_Modal.cshtml
<div class="loadingOverlay">
<button class="btn loading-button" disabled><i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i></button>
</div>
And also the External JavaScript (different file from the Partial View .cshtml):
Modal.js
var Modal;
Modal = Modal || (function () {
var loadingDiv = $('.loadingOverlay');
return {
Loading: function () {
loadingDiv.css('display', 'block');
$('body').prepend(loadingDiv);
},
LoadingDismiss: function () {
loadingDiv.fadeOut('500');
},
};
})();
And I call from my page like below:
Index.cshtml
<!DOCTYPE html>
<html>
<head>
#Styles.Render("~/Content/font-awesome", "~/Content/css")
</head>
<body>
<div class="container-fluid">
<div class="container">
<div class="row">
<button id="showOverlay" class="btn btn-default"></button>
</div>
</div>
</div>
#Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
$(document).ready(function () {
$('#showOverlay').click(function () {
Modal.Loading();
});
})
</script>
</body>
</html>
My problem is: whenever I clicked the button, nothing happens. But it works when I put the html element not inside _Modal.cshtml (Refer to the below code).
My question is: how can I get the class name in the _Modal.cshtml using JavaScript or JQuery?
I can do this in the Modal.js (Note that it is same as I put that into _Modal.cshtml) but that would be hard to maintain and I would like to put all of the content in the Partial View, and call it when needed by class name, instead of write string that will converted to JavaScript Object:
var loadingDiv = $('<div class="loadingOverlay"><button class="btn loading-button" disabled>'
+ '<i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i></button></div>');
Your answer much appreciated.
Edit:
Question on 31st of May 2016:
What's the difference between #Html.Partial("_Modal") with #Html.Partial("~/Views/Shared/_Modal.cshtml"). is it just to better understand for where the compiler get the partial views from?
Answered on 1st of June 2016 by #Guruprasad Rao:
There is nothing difference between two except the mentioning of partialview name. The one I suggested was providing fully qualified path which helps compiler from where to fetch the partialview. To one you use will search in all the directories under the View folder. So the one I mentioned will be good in the aspect of performance.
Thanks for the #Guruprasad Rao for the answer and the explanation.
Cheers~
You should import the partialview first from server to client browser first and then you can manipulate it with jquery. Otherwise there would be no element with the selector you are trying to find. User, #Html.Partial or #Html.RenderPartial to import it, hide it at initial state and show it on click. Take a below example:
<body>
<div class="container-fluid">
<div class="container">
<div class="row">
<button id="showOverlay" class="btn btn-default"></button>
</div>
</div>
</div>
#Html.Partial("~/Views/Shared/_Modal.cshtml")
<!--I assume that the content inside this will be initially in hidden state-->
#Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
$(document).ready(function() {
$('#showOverlay').click(function() {
Modal.Loading();
});
})
</script>
</body>
Modal.js changes
var Modal;
Modal = Modal || (function () {
var loadingDiv = $('.loadingOverlay');
return {
Loading: function () {
loadingDiv.css('display', 'block');
//$('body').prepend(loadingDiv);
//No need to prepend here since we are importing it through partial method
},
LoadingDismiss: function () {
loadingDiv.fadeOut('500');
},
};
})();
I'm stuck on an older WebForms project and I'd like to know if there's a recommended approach for my scenario.
Goal
I have a feedback form in a modal dialog that I bound up using KnockoutJS.
I would like the feedback form to be available on all pages, via a link in the footer of the site.
I would like to have several other pages using knockout as well with their own individual scripts & bindings, irrespective of the feedback form bindings in the modal.
I have some pages that do not use knockout at all. I would like them not to have to insert code to accomplish this.
I would like to avoid global variables, if possible, in favor of namespaced JavaScript.
In essence, I would like for the viewmodels on the page and the feedback viewmodel not to be aware of each others' existence.
Current Setup
Our footer links are in a Site.master file, and so that's where I've placed the Feedback.js script and the div for the modal which has the bindings. So on the master page, I call ko.applyBindings(vm, referenceToFeedbackDiv), which works fine to wire up the feedback form.
Our individual pages occasionally have a knockout viewmodel, and so they may call ko.applyBindings(vm), since to their knowledge they'd like to apply the vm to their entire page.
Problem
This causes a conflict in knockout because one vm is being applied to the feedback form via the Site.master call, and one vm is being applied to the entire body by the page after it.
Question
How can I enable these two things -- a modal dialog across all pages that uses knockout, and individual knockout pages -- to work in harmony?
Demonstration of the Issue in (the Current) Code
Remember, the issue is that I want to be able to have one feedback VM that applies only to the feedback div across the client site, and I want to have other VMs able to be applied that aren't required to know anything about the feedback vm.
Master Page file (Site.Master) -- Excerpt
This is on every page:
<div class="page">
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
<div class="footer">
© <%=DateTime.Now.Year.ToString() %> Company, Inc. | Home | About |
<!-- begin feedback area -->
<span id="FeedbackArea">
<a data-bind="click: showModal">Feedback</a>
<div id="feedback-modal" title="What's on your mind?">
<div class="btn-group" id="feedbackButtonGroup">
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-warning fa-2x fa-align-center"></i>
<br />
<span>Problem</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-question-circle fa-2x fa-align-center"></i>
<br />
<span>Question</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-lightbulb-o fa-2x fa-align-center"></i>
<br />
<span>Suggestion</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-thumbs-o-up fa-2x fa-align-center"></i>
<br />
<span>Praise</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-info-circle fa-2x fa-align-center"></i>
<br />
<span>General</span>
</button>
</div>
<br />
<br />
<textarea rows="5" placeholder="Enter feedback here" data-bind="value: feedbackText, valueUpdate: 'afterkeydown'"></textarea>
<br />
<br />
<button>Send Feedback</button>
<button data-bind="click: CancelFeedback">Cancel</button>
<h3>Other Information: </h3>
<ul>
<li><strong>Feedback Type:</strong> <span data-bind="text: feedbackType"></span></li>
<li><strong>Current URL:</strong> <span data-bind="text: pageUserIsOn"></span></li>
<li><strong>Current User: </strong><%=hdnLoggedInUsername.Value %></li>
<li><strong>Current Client: </strong>[Not yet captured]</li>
<li><strong>Current Tab: </strong>[Not yet captured]</li>
</ul>
</div>
</span>
<!-- End feedback area -->
</div>
Feedback.JS -- This is also included in every page
...a somewhat-namespaced definition of a FeedbackVM:
var FeedbackNamespace = FeedbackNamespace || {};
..the definition of the namespace itself:
FeedbackNamespace = {
ViewModel: function () {
// etc. etc.
}
};
...and the declaration of a VM variable plus wiring it up on document.ready():
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#FeedbackArea')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
Other Pages without Knockout / JS
Other pages may or may not have any javascript on them at all, let alone knockout. On these pages, the FeedbackVM currently works fine.
Pages with their own Knockout ViewModel
These pages would have their own namespaced JS file with their own document.ready() event, that creates a vm of say invoiceUploaderVM = new InvoiceUploader.ViewModel(), and then calls ko.applyBindings(invoiceUploaderVM).
This is where we run into trouble.
Update: One potential Approach and a little trouble
In the Site.master page, I wrapped my entire footer in a "stopBindings: true" div:
<div data-bind="stopBindings: true">
<div class="footer" id="footerDiv">
<!-- Feedback Viewmodel stuff in here -->
</div>
</div>
I've defined stopBindings as:
ko.bindingHandlers.stopBindings = {
init: function () {
return { controlsDescendantBindings: true };
}
};
My Feedback.js file, loaded on every page as part of a global JS file, has:
var FeedbackNamespace = FeedbackNamespace || {};
FeedbackNamespace = {
// defines viewmodel, etc. etc.
};
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#footerDiv')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
This approach works perfectly well -- as long as there are no other viewmodels being bound. On the pages that inherit from my master page, I might have something like:
$(document).ready(function () {
'use strict';
vm = new invoiceUploader.ViewModel();
ko.applyBindings(vm);
});
I would expect that this:
Sets up the feedback viewmodel applied to the div, stopping other viewmodels
Sets up the invoiceUploader viewmodel and applies it to the body (which is then stopped by the stopBindings div)
However, instead I get an error upon loading the child page along the lines of:
Commenting the line to apply the feedback bindings makes this work just fine again.
What am I doing wrong?
I think I would put the view model for your modal in a global object and do whatever you need to do with it aside from applying the bindings in a shared script:
window.feedbackModal = {
foo: ko.observable("Whatever you need to do here"),
bar: ko.observable("assuming it can be done the same on every page")
};
Then in the Site.master
<div class="feedback-modal" data-bind="with: feedbackModal">
<p data-bind="text: foo"></p>
<p data-bind="text: bar"></p>
</div>
And in every individual page's script:
function ViewModel() {
this.individualProperty = ko.observable(true);
this.specificAction = function() { /* do something specific to this page */ };
this.feedbackModal = window.feedbackModal;
}
ko.applyBindings(new ViewModel());
So window.feedbackModal could be undefined and it won't cause you problems, but if you ko.applyBindings, you have to have a feedbackModal property exposed in the view model or you'll get errors applying those bindings.
Of course, there are more clever ways you could implement this basic idea in order to fit your patterns the best, but the big point is, as you know, you can't apply bindings twice, so you need to defer that task to your most specific code and expose your reusable code to to it.
Here is another strategy for separation of common modules from page dependant modules:
// An example of a module that runs on everypage
var modalDialog = function(){
this.name = "dialog1";
this.title = ko.observable("My Modal Title");
this.content = ko.observable("My Modal content is also something");
}
// An example of a module that runs on everypage
var modalDialog2 = function(){
this.name = "dialog2";
this.title = ko.observable("My Modal Title 2");
this.content = ko.observable("My Modal content is also something 2");
}
// Either generate it automatically or by hand
// to represent which modules are common
var commonModules = [modalDialog, modalDialog2];
// An example of a module only for this page
var pageModule = function(){
this.pageFunction = function(){
alert("Called page function");
}
}
// Composition is the final object you will actually bind to the page
var composition = {
pageMod: new pageModule()
}
// Let's add the common modules to the composition
ko.utils.arrayForEach(commonModules, function(item){
var module = new item();
composition[module.name] = module;
});
// Bind the composition
ko.applyBindings(composition);
example HTML for this would be:
<div class="modalDialog">
<h2 data-bind="text: dialog1.title"><h2>
<h2 data-bind="text: dialog1.content"><h2>
</div>
<div class="modalDialog">
<h2 data-bind="text: dialog2.title"><h2>
<h2 data-bind="text: dialog2.content"><h2>
</div>
<div id="content">
<h2>Welcome to page</h2>
<div id="somePageStuff">
Click me
</div>
</div>
Link to the jsfille for this
You can set this up by using a technique to not have scope your bindings to a specific area in your page.
Check out: How to stop knockout.js bindings evaluating on child elements
Example:
http://jsfiddle.net/anAgent/RfM2R/
HTML
<div id="Main">
<label data-bind="text: ViewModel.Name">default</label>
<div data-bind="stopBindings: true">
<div id="ChildBinding">
<label data-bind="text: AnotherViewModel.Name">default</label>
</div>
</div>
</div>
JavaScript
$(function () {
ko.bindingHandlers.stopBindings = {
init: function () {
return {
controlsDescendantBindings: true
};
}
};
var data = {
ViewModel: {
Name: "Testing"
}
};
var data2 = {
AnotherViewModel: {
Name: "More Testing"
}
};
ko.applyBindings(data, $("#Main")[0]);
ko.applyBindings(data2, $("#MyModalHtml")[0]);
});
Hello there i am following "lets learn ember" by José Mota in tuts premium. I am stuck in a very strange situation.
I am at 9th video in the series where i am learning to create list view items and inserting that view in application template. I exactly did what author did in the video, however the result doesn't show up in the screen. But in console the obejct is successfully added.
I am creating a flash message view, whenever user add certain message the flash message should display at the top of the screen with fade in effect.
I have basically an alert view that handles fadein and out of the message, App.flashController controller which in turn extended from App.FlashController, which extends Ember.ArrayController, and App.FlashListView view.
Here is my code
flash.js controller
App.FlashController = Ember.ArrayController.extend();
App.flashController = App.FlashController.create({content: Ember.A()});
FlashListView code:
App.FlashListView = Ember.CollectionView.extend({
itemViewClass: "App.AlertView"
, contentBindings: "App.flashController"
});
Alert view code
App.AlertView = Ember.View.extend({
templateName: "_alert"
, tagName: "div"
, classNameBindings: ["defaultClass", "kind"]
, defaultClass: "alert-box"
, kind: null
, click: function(){
this.$().fadeOut(300, function(){ this.remove();});
}
, didInsertElement: function(){
this.$().hide().fadeIn(300);
}
});
here is the order of loading of the files
<script src="js/app.js"></script>
<script src="js/route/route.js"></script>
<script src="js/controllers/bookmarks_controller.js"></script>
<script src="js/controllers/bookmark_controller.js"></script>
<script src="js/views/alert.js"></script>
<script src="js/views/flash_list.js"></script>
<script src="js/controllers/flash.js"></script>
And this is where the flash message should be displayed
<script type="text/x-handlebars" data-template-name="application">
<div>
<nav class="top-bar" data-options="is_hover:true">
<ul class="title-area">
<li class="name">
<h1>My Ember Site </h1>
</li>
</ul>
<section class="top-bar-section">
<ul class="right">
<li class="name">{{#linkTo "index"}}Home{{/linkTo}}</li>
<li>{{#linkTo "about"}}About{{/linkTo}}</li>
<li>{{#linkTo "about.team"}}About Team{{/linkTo}}</li>
<li>{{#linkTo "about.kshitiz"}}About Kshitiz{{/linkTo}}</li>
<li>{{#linkTo "bookmarks"}}Bookmarks{{/linkTo}}</li>
</ul>
</section>
</nav>
</div>
<div class="container">
<div class="row">
{{outlet}}
{{view App.FlashListView}}
</div>
</div>
</script>
_alert template code:
<script type="text/x-handlebars" data-template-name="_alert">
{{view.content.message}}
</script>
in browser when I write this command
App.flashController.pushObject(Ember.Object.create({message:"Hello!"}))
a class was returned
Class {message: "Hello!", constructor: function, toString: function,
reopen: function, init: function…}
But it doesnt show up in screen as in tutorial.
I have no idea what went wrong and i am completely new to this ember.js world.
There is a possible typo in App.FlashListView, should be singular contentBinding not plural contentBindings
App.FlashListView = Ember.CollectionView.extend({
itemViewClass: "App.AlertView"
, contentBinding: "App.flashController"
});