Loading user control dynamically in two directions - javascript

I am developing a commenting like platform where user can post comments and other users can reply on that comments. But these comments can be replied in two directions (Reply & Expand). The idea is that user can reply and user can also expand that discussion. Please see image.
I have developed a control which will be added dynamically whenever user will Reply or Expand a comment.
If user will reply on control a new control will be added under that message (comments) and if user Expand that message (comments) then a control with expanded reply (message) will be added on right side of that messages (comments). More than one user can Expand a message and users can also reply on expanded message.
I am not sure that what should be the place holder or container in which these control should be added.
Should I use Server Table control and create TableRows and TableCells
dynamically?
What container should I use so page don't get too heavy?
Should I create divs and adjust its position on runtime and add control in each
div?
Other question on which I am not clear are:
More than one user can expand a message, in this case how to handle
UI. Hide/Show controls or any other solution.
Should I show horizontal scrollbar on Expansions or is there other
solutions?
Any help/suggestion?
Thanks.

you can do this in different ways, i am not going to give you code cause i want you to try and learn. So you can do all this with JQuery, you don't need Asp.net controllers, cause sometimes they are complicated to work with.
So 1º step:
You can create a simple table with 2 Columns and N rows depending on the number of messages you have (try filter some, Example: you have 100, show only 10 and have a "next page") or use some plugin table that hold bigdata for you.
2º Step:
when you reply a message create an <tr> in you table to place the "reply of first message" (note: you need to create a structure to handle you navigation on the table easily with ID's)
3º Step:
in your second column have like a <div> prepared to receive and show the expand message when user click "expand" and clean it always when a new message is expanded.
That should work for you, there are others ways but try to think in something simple
Other Questions:
1º - I didn't understand the question, but if your site will run on a server, user1 and user2, ... userN use the same page but render in different places, you just need to reload data to let everyone known that someone comment or reply or whatever in a message.
2º - UI its your choice, see what is best for you ;)
enjoy ;)

I think other answer is to complicated. For me it's seems like you need
<div class="rowContainer">
<div style="float:right">
<uc:YourControl>
</div>
<div>
for row of your expandable controls.
When user will quick on expand button page will add another one of YourControls to div.rowContainer with parent another div with float right:
<div class="rowContainer">
<div style="float:right">
<uc:YourControl>
</div>
<div style="float:right">
<uc:YourControl>
</div>
<div>
When user will add another row of messages add another row container with div.rowContainer:
<div class="allMessagesContainer">
<div class="rowContainer">
<div style="float:right">
<uc:YourControl>
</div>
<div>
<div style="clear:both">
<div class="rowContainer">
<div style="float:right">
<uc:YourControl>
</div>
<div>
</div>
clear:both is important since is breaking float to next line.
I think wolud be goo to create 3 controls:
1. <uc:YourControl> with single message
2. Control from div.rowContainer to easy append messages to the left, just add another one to server div with float:right inside same control.
3. Control for all messages to easy adding messages in next row. Just add another row control to container.
Also floating of controls will not force user to scroll to the left infinitely. It's not nice and they hate that.
And just one more: asp.net is not really for this kind of sites. Things get quickly complicated. What if button in one of nested controls should do something on Page?
Should you add method to Page and cast it on control? ((MyPage)Page).DoSomeWork()
What if type of Page change? Create interface? Maybe delegate instead. Where put him? In the Page? Control? Create event and bubble it's through all parents? Some context class?
And not mention with necessity of reinitialization of dynamic controls tree every-single-time post back is send.

1 MSG
2 MSG
Response
...etc...
... you can do like this :)

When it comes to CSS, simple is better.
My suggestion, if you must have it this way in terms of layout:
<div class="row">
<div class="message"><FirstMessage /></div>
<div class="message"><ExpandedFirstMessage /></div>
</div>
<div class="row">
<div class="message"><Reply /></div>
...etc...
</div>
You create a div for each "row" or expanded responses, using a div with class "row.". Then, each message has its own div with class "message", using the following CSS:
.message {
width: 200px; // or whatever size you want here
height: 100px; // or whatever size you want here
display: inline-block; // This is the important part!
...etc // your extra padding, margin, whatnot.
}
Inline-block allows you to avoid floats and whatnot, which should make things a lot simpler. (As a bonus, people with RTL reading order will have this work automatically for them.)
Now, here's your problem.
From the image you've described, it looks as though anyone can reply to any message, or expand any message; this means, logically, that the grid layout you've described might not work well. (For instance, what if someone replies to Expanded First Message, and someone else expands Reply to First Message? You would have to Message usercontrols that would be vying for the same space. How would that work?) I would suggest implementing a different design that would accommodate your usage flow, unless you have something more specific planned.
I hope this answer helps you. Good luck.

Related

How to capture clicked element using $(this) inside of a vue(js) instance

I am reworking an old app of mine and I am having issues with dom manipulation and basic selections within a vue instance.
Essentially I have information in a database that I load in via ajax.
Each record in the db has 2 sections. The header tab(title, time, date etc) and the body of the record(notes, ideas, etc)
When loaded, the header shows normally to the user but if they want to see what that note contains, they have to click on the header for the bottom to appear.
consider the following html:
<vuejs for loop>
<div v-bind:id='item._id' class="tabW" v-on:click="blueTabClick" >
<div class="blueTabMainColor">
<!-- header stuff here -->
</div>
<div class="notesOpenedW">
<!-- interior informaton here, HIDDEN BY CSS -->
</div>
</div>
<vuejs for loop ender>
This HTML is essentially inside a Vue for/loop directive, and generates however many "tabs(tabW)" as needed based on how much info I have in the DB
All I want the user to do is to be able to click whichever tab(tabW) they want information on, and for the notes show underneath(notesOpenedW).
I stripped my entire app and js and tried to keep it as simple a test as possible and even with the below, I still can't get anything.
here is my JS(JQ):
$(document).ready(function(evt){
$(".blueTabMainColor").click(function(){
$(this).next(".notesOpenedW").fadeToggle();
});
});
With this basic code, when I put it inside a Vue instance, via:
methods: {
blueTabClick: function (evt) {
evt.preventDefault();
$(".blueTabMainColor").click(function(){
//alert("you clicked me");
$(this).next(".notesOpenedW").fadeToggle();
});
}
}
It doesn't work, but if I take it out of the Vue instance, it works just fine.
how can I get this to work? or am I going about it the wrong way?
Vue will not cohabit happily with JQuery. You're $(this) will not work because you're not even in the document at that point, you're in pure js, virtual DOM, another universe. Then, if it did, the event listener you call may not exist. You will need to fundamentally transition this code to Vue if you want it to work, I fear.
You can achieve this by setting a ref on "notesOpenedW".
https://v2.vuejs.org/v2/api/#ref
I would strongly recommend to wrap this behaviour in a dedicated component
That would have the following content :
<div class="tabW" v-on:click="blueTabClick" >
<div class="blueTabMainColor">
<!-- header stuff here -->
</div>
<div class="notesOpenedW" ref="notesToggleDiv">
<!-- interior informaton here, HIDDEN BY CSS -->
</div>
</div>
And the method :
methods: {
blueTabClick: function () {
$(this.$refs.notesToggleDiv).fadeToggle();
}
}
Be aware that when using Vue, manipulating directly the dom is usually a bad idea.
As i showed you, it is possible to use jQuery with Vue if you absolutely need it (or cannot afford to rework more deeply your application).
Edit : Just found this article that i think would help you a lot :
https://www.smashingmagazine.com/2018/02/jquery-vue-javascript/?utm_campaign=Revue%20newsletter&utm_medium=Newsletter&utm_source=Vue.js%20Developers

Using Javascript to show and hide a message

I've set up a small script to show and hide a div..
$('.message-head').click(function () {
$('.message-preview').toggle('slow');
});
Works perfectly as it should do. My problem is that I have multiple instances of the html markup on the page, which is inside a foreach loop..
<div class="two-three-col message-head">
<h4>#message.Subject</h4>
<div class="message-preview">
#Html.Raw(#message.Body)
</div>
</div>
This is basically for a messaging system that has been chopped and changed a lot and has been left to me to fix; not being the best at javascript I'm quite stuck. So how can I modify the js so that if I click on say message 1 then only message 1 will show/hide on click and the rest will stay inactive.
Thanks in advance
You can use the this keyword to refer to the element which raised the event. From there you can traverse the DOM to find the related .message-preview element. Try this:
$('.message-head').click(function () {
$(this).find('.message-preview').toggle('slow');
});

Checking to see if element is visible

I have a list of apps that are listed on a non angular page. The list of apps that are available depends on what subscription level was paid for. If the subscription level does not have an app purchased the app is still listed however there is an overlay over the app. (please see picture).
The html looks like this:
<div class="apps-item apps-no-border disabled">
<div class="apps-name">
<span>Interactive Event Diagrams</span>
</div>
<div class="divider">
<div class="apps-description">Interactive Event Diagrams is an indispensable online tool, allowing website visitors to view your meeting rooms and create their own customized event layouts according to their specific needs, all while using your venue’s available inventory. Users
can email and save diagrams or images for future reference.</div>
<div class="apps-image-preview">
<img alt="Interactive Event Diagrams" src="/Content/Images/AppsPreview/interactive_event_diagrams.png">
</div>
<div class="apps-action">Not Purchased</div>
<div class="overlay"></div>
</div>
</div>
Now if an app is purchased the overlay element is shaded gray in the html and is not view-able on screen. (Ex. no grey shading over Hotel Venue Explorer) I want to be able to check and see if the overlay is seen or not seen.
I've tried this:
elm = element.all(by.css('div.apps-item')).get(5);
expect(elm.$('div.overlay').isDisplayed()).toBeTruthy();
However the expect is returning false.
Other apps html, notice the grey over the overlay class
If your div.overlay is always present in the DOM, then its hard to check if its displayed because it will always be there in the DOM and your css and javascript might be handling the display property(like add overlay if its needed or don't add when its not needed). Also checking isDisplayed function for an empty html element doesn't work as far as i know.
In order to verify it you can check for css attributes that are responsible for the greying out functionality. Here's how -
elm = element.all(by.css('div.apps-item')).get(5);
//Use your css attribute that greys the apps-item div like height, width, color, etc...
expect(elm.$('div.overlay').getCssValue('background-color')).toEqual('grey');
expect(elm.$('div.overlay').getCssValue('width')).not.toBeNull(); //If you know the width then you can check for equality or greaterThan(someVal).
expect(elm.$('div.overlay').getCssValue('height')).not.toBeNull();
Hope it helps.

Expand/Collapse Text

The code below works fine with ONE Reveal/Hide Text process
<div class="reveal">Click Here to READ MORE...</div>
<div style="display:none;">
<div class="collapse" style="display:none;">Collapse Text</div>
However if this code is duplicated multiple times, the Collapse Text shows up and doesn't disappear and in fact conflicts with the Expand to reveal even more text instead of collapsing as it should.
In this http://jsfiddle.net/syEM3/4/ click on any of the Click Here to READ MORE...
Notice how the Collapse Text shows up at the bottom of the paragraphs and doesn't disappear. Click on the Collapse and it reveal more text.
How do I prevent this and getting to work as it should?
The two slideDown function calls are not specific to the .reveal and/or .collapse that you are currently doing. i.e.
$(".collapse").slideDown(100);
will find all the elements with the class .collapse on the page, and slide them down. irrespective of what element you just clicked.
I would change the slideDown call to be relavant to the element you just clicked i.e. something like this
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(this).next().next(".collapse").slideToggle(100);
});
in your code
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(".collapse").slideDown(100);
});
$('.collapse').click(function() {
$(this).slideUp(100);
$(this).prev().slideToggle();
$(".reveal").slideDown(100);
});
this two rows doesn’t do what you want as they act on all elements of the specified class
$(".reveal").slideDown(100);
$(".collapse").slideDown(100);
When you do $(".collapse").slideDown(100);, jQuery runs slideDown on everything with the .collapse class, not just the one that's related to your current this. To fix this, refer to the collapse based on its location to $(this).
Do do this, use something like $(this).siblings(".collapse").slideDown(100);
Note that this particular selector will only work if you enclose each text block in its own div. With each text element in its own div, like you have it now, .siblings(".collapse"), which selects all the siblings of $(this) with the collapse class, will still select both of the collapse elements.
Okay, I think you should take a different approach to your problem.
See, jQuery basically has two purposes:
Selecting one or more DOM elements from your HTML page
manipulate the selected elements in some way
This can be repeated multiple times, since jQuery functions are chainable (this means you can call function after function after function...).
If I understood your problem correctly, you are trying to build a list of blog posts and only display teasers of them.
After the user clicks the "read more" button, the complete article gets expanded.
Keep in mind: jQuery selects your elements very much like CSS would do. This makes it extremely easy to
come up with a query for certain elements, but you need to structure your HTML in a good way, like
you would do for formatting reasons.
So I suggest you should use this basic markup for each of your articles (heads up, HTML5 at work!):
<article class="article">
<section class="teaser">
Hey, I am a incredible teaser text! I just introduce you to the article.
</section>
<section class="full">
I am the articles body text. You should not see me initially.
</section>
</article>
You can replace the article and section elements with div elements if you like to.
And here is the CSS for this markup:
/* In case you want to display multiple articles underneath, separate them a bit */
.article{
margin-bottom: 50px;
}
/* we want the teaser to stand out a bit, so we format it bold */
.teaser{
font-weight: bold;
}
/* The article body should be a bit separated from the teaser */
.full{
padding-top: 10px;
}
/* This class is used to hide elements */
.hidden{
display: none;
}
The way we created the markup and CSS allows us to put multiple articles underneath.
Okay, you may have noticed: I completely omitted any "read more" or "collapse" buttons. This is done by intention.
If somebody visits the blog site with javascript disabled (maybe a search engine, or a old mobile which doesn't support JS or whatever),
the logic would be broken. Also, many text-snippets like "read more" and "collapse" are not relevant if they don't actually do anything and are not part of the article.
Initially, no article body is hidden, since we didn't apply the hidden css class anywhere. If we would
have embedded it in the HTML and someone really has no JavaScript, he would be unable to read anything.
Adding some jQuery magic
At the bottom of the page, we are embedding the jQuery library from the google CDN.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
This is a best practice and will normally speed up your page loading time. Since MANY websites are embedding
jQuery through this URL, chances are high that its already in the visitors browser cache and doesn't have
to be downloaded another time.
Notice that the http: at the beginning of the URL is omitted. This causes browsers to use the pages current protocol,
may it be http or https. If you would try and embed the jQuery lib via http protocol on a https website, some browsers will refuse to download the file from a unsecure connection.
After you included jQuery into the page, we are going to add our logic into a script tag. Normally we would
save the logic into a separate file (again caching and what not all), but this time a script block will do fine.
Finally some JavaScript
At first, we want to hide all elements with the css-class full, since only teasers should remain displayed. This is very easy with jQuery:
$('.full').hide();
The beginning of the script $('.full') tells jQuery: I need all elements with the CSS-class full. Then we call a function on that result, namingly hide() which purpose should be clear.
Okay, in the next step, we want to add some "read more" buttons, next to every teaser. Thats an easy task, too:
$('.teaser').after('<button class="more">Read more</button>');
We now select every element with the css-class teaser and append some HTML code after() each element - a button with the css-class more.
In the next step, we tell jQuery to observe clicks on every one of this freshly created buttons. When a user has clicked, we want to expand the next element with the css-class full after the clicked button.
$('.more').on('click', function(){
//"this" is a reference to the button element!
$(this).slideUp().next('.full').slideDown();
});
Phew, what did we do here?
First, we told jQuery that we wanted to manipulate this, which is a reference to the clicked button. Then we told
jQuery to hide that button (since its not needed anymore) slowly with slideUp().
We immediately continued telling jQuery what to do: Now take the next() element (with the css-class full) and make it visible by sliding it down with slideDown().
Thats the power of jQuerys chaining!
Hiding again
But wait, you wanted to be able to collapse the articles again! So we need a "collapse" button, too and
some more JavaScript:
$('.full').append('<button class="collapse">Collapse text</button>');
Note: we didn't use the after() function to add this button, but the append() function to place the button
INSIDE every element with the css-class full, rather than next to it. This is because we want the
collapse buttons to be hidden with the full texts, too.
Now we need to have some action when the user clicks one of those buttons, too:
$('.collapse').on('click', function(){
$(this).parent().slideUp().prev('.more').slideDown();
});
Now, this was easy: We start with the button element, move the focus to its parent() (which is the element that contains the full text) and tell jQuery to hide that element by sliding it up with slideUp().
Then we move the focus from the full-text container to its previous element with the css-class more, which is its expanding button that has been hidden when expanding the text. We slowly show that button again by calling slideDown().
Thats it :)
I've uploaded my example on jsBin.

Is there a jQuery plugin for making a step-by-step type presentation?

I was making a small thing in HTML and basically I have some "frames" like
<div id="frame_1">
...
</div>
<div id="frame_2">
...
</div>
...
Basically what I want is for only one frame to be visible at one time and to navigate between frames easily with previous and next buttons (navigation by frame number a plus, but not required)
Before I set out to write it myself I figured someone had already done it so has it been done?
You may want to check this out: http://meyerweb.com/eric/tools/s5/ :)

Categories