MVC - Can a partial view replace itself - model-view-controller

I have a strong typed partial view of a class that contains a list of image urls and the index of the current image. Now I need a "next" button that reloads my partial view with the updated index.
The point is that I don't want to save this list or index with javascript.
The partial view will render the image as follows:
<img id="imgMain" src="<%: Url.Content(Model.GetCurrentImageUri().RelativeUri.ToString()) %>" alt="" />
Thank you very much for your help!

The solution is to store the index of my array in a hidden field. Then I fetch the data with a ajax call and replace my partial view with the resulting data. That is all, very easy :)

Related

Can not input to EditableElement of CKEditor5

In CKEditor5, I tried implementing custom element to convert model to view for editing. Then, editable element(#ckeditor/ckeditor5-engine/src/view/editableelement) in container element(#ckeditor/ckeditor5-engine/src/view/containerelement) is focused on the parent container element and can not be edited.
For example, if it is implemented as follows:
buildModelConverter().for(editing.modelToView)
.fromElement('myElement')
.toElement(new ContainerElement('div', {}, [new EditableElement('h4')]));
The result of actual editing dom after inserting 'myElement' and keydown "abc". (I hope inputting text of "abc" to h4 tag but...)
<div>​​​​​​​
abc
<h4>
<br data-cke-filler="true">
</h4>
</div>
I also tried using widget for applying contenteditable attribute.
But, text couldn't be entered in h4.
<div class="ck-widget" contenteditable="false">​​​​​​​
<h4 class="ck-editable" contenteditable="true">
<br data-cke-filler="true">
</h4>
</div>
Is this bug, or I made mistake understanding of container element?
[Additional details]
I am assuming to make a widget plugin for ranking list.
First, the ranking list is structured by <ol> and <li> tags because of having multiple items.
I solved that by defining two schema such as "rankingList" and "rankingListItem",
so I realized dynamic elements using nested model elements.
const item1 = new ModelElement('rankingListItem');
const item2 = new ModelElement('rankingListItem');
const model = new ModelElement('rankingList', {}, [item1, item2]);
// and insert
Next, the item of ranking list has link, image, title and note.
Therefore, the ranking list item has the following DOM structure:
<ol><!-- apply toWidget -->
<li>
<a href="link[editable]">
<img src="image[editable]">
<h3>title[editable]</h3>
<p>notes[editable]</p>
</a>
</li>
...
</ol>
I expect the view element is the following:
const {ref, src, title, notes} = data; // how to get data?
const view = new ContainerElement('a', {ref}, [
new EmptyElement('img', {src}),
new EditableElement('h3', {}, new Text(title)),
new EditableElement('p', {}, new Text(title)),
]);
// maybe incorrect ...
In conclusion, I want to use editable view not to break defined DOM tree.
How can I realize it?
Thank you for describing your case. It means a lot for us at the moment to know how developers are using the editor and what are your expectations.
Unfortunately, this looks like a very complex feature. It also looks like it would need custom UI (to edit link url and image src -- unless they do not change after added to the editor). It seems that you struggle with two problems:
position mapping between the model and the view,
nested editables.
First, to answer your question about EditableElement - it seems to be correct to use them for h3 and p elements.
However, such complex feature needs custom converters. Converter builders (which you used) are dedicated to being used in simple cases, like element-to-element conversion, or attribute-to-attribute conversion.
Behind the nice API, build converter is a function factory, that creates one or multiple functions. Those are then added as callbacks to ModelConversionDispatcher (which editor.editing.modelToView is an instance of). ModelConversionDispatcher fires a series of events during the conversion, which is a process of translating a change in the model to the view.
As I've mentioned, you would have to write those converting functions by yourself.
Since this is too big of a subject for a detailed and thorough answer, I'll just briefly present you what you should be interested in. Unfortunately, there are no guides yet about creating custom converters from scratch. This is a very broad subject.
First, let me explain you from where most of your problems come from. As you already know, the editor has three layers: model (data), view (DOM-like structure) and DOM. Model is converted to view and view is rendered to DOM. Also, the other way, when you load data, DOM is converted to view and view is converted to model. This is why you need to provide model-to-view converter and view-to-model converter for your feature.
The important piece of this puzzle is engine.conversion.Mapper. Its role is to map elements and positions from model to view. As you already might have seen, the model might be quite different than the view. Correct position mapping between those is key. When you type a letter at caret position (in DOM), this position is mapped to model and the letter is inserted in the model and only then converted back to the view and DOM. If view-to-model position conversion is wrong, you will not be able to type, or really do anything, at that place.
Mapper is pretty simple on its own. All it needs is that you specify which view elements are bound to which model elements. For example, in the model you might have:
<listItem type="bulleted" indent="0">Foo</listItem>
<listItem type="bulleted" indent="1">Bar</listItem>
While in the view you have:
<ul>
<li>
Foo
<ul>
<li>Bar</li>
</ul>
</li>
</ul>
If the mapper knows that first listItem is bound with first <li> and the second listItem is bound with second <li>, then it is able to translate positions correctly.
Back to your case. Each converter has to provide data for Mapper. Since you used converter builder, the converters build by it already do this. But they are simple, so when you provide:
buildModelConverter().for(editing.modelToView)
.fromElement('myElement')
.toElement(new ContainerElement('div', {}, [new EditableElement('h4')]));
it is assumed, that myElement is bound with the <div>. So anything that is written inside that <div> will go straight to myElement and then will be rendered at the beginning of myElement:
<div>
<h4></h4>
</div>
Assuming that you just wrote x at <h4>, that position will be mapped to myElement offset 0 in the model and then rendered to view at the beginning of <div>.
Model:
<myElement>x</myElement>
View:
<div>x<h4></h4></div>
As you can see, in your case, it is <h4> which should be bound with myElement.
At the moment, we are during refactoring phase. One of the goals is providing more utility functions for converter builders. One of those utilities are converters for elements which have a wrapper element, like in that "div + h4" case above. This is also a case of image feature. The image is represented by <image> in model but it is <figure><img /></figure> in the view. You can look at ckeditor5-image to see how those converters look like now. We want to simplify them.
Unfortunately, your real case is even more complicated because you have multiple elements inside. CKE5 architecture should be able handle your case but you have to understand that this is almost impossible to write without proper guides.
If you want to tinker though, you should study ckeditor5-image repo. It won't be easy, but this is the best way to go. Image plugin together with ImageCaption are very similar to your case.
Model:
<image alt="x" src="y">
<caption>Foo</caption>
</image>
View:
<figure class="image">
<img alt="x" src="y" />
<figcaption>Foo</caption>
</figure>
While in your case, I'd see the model somewhere between those lines:
<rankItem imageSrc="x" linkUrl="y">
<rankTitle>Foo</rankTitle>
<rankNotes>Bar</rankNotes>
</rankItem>
And I'd make the view a bit heavier but it will be easier to write converters:
<li contenteditable="false">
<a href="y">
<img src="x" />
<span class="data">
<span class="title" contenteditable="true">Foo</span>
<span class="notes" contenteditable="true">Bar</span>
</span>
</a>
</li>
For rankTitle and rankNotes - base them on caption element (ckeditor5-image/src/imagecaption/imagecaptionengine.js).
For rankItem - base it on image element (ckeditor5-image/src/image/).
Once again - keep in mind that we are in the process of simplifying all of this. We want people to write their own features, even those complicated ones like yours. However, we are aware of how complex it is right now. That's why there are no docs at the moment - we are looking to change and simplify things.
And lastly - you could create that ranking list simpler, using Link plugin and elements build with converter builder:
rankList -> <ol class="rank">,
rankItem -> <li>,
rankImage -> <img />,
rankNotes -> <span class="notes">,
rankTitle -> <span class="title">.
However, it will be possible to mess it up because the whole structure will be editable.
Model:
<rankList>
<rankItem>
<rankImage linkHref="" />
<rankTitle>Foo</rankTitle>
<rankNotes>Bar</rankNotes>
</rankItem>
...
</rankList>
Where "Foo" and "Bar" also have linkHref attribute set.
View:
<ol class="rank">
<li>
<img src="" />
<span class="title">Title</span>
<span class="notes">Foo</span>
</li>
...
</ol>
Something like this, while far from perfect, should be much easier to write as long as we are before the refactor and before writing guides.
Of course you will also have to provide view-to-model converters. You might want to write them on your own (take a look at ckeditor5-list/src/converters.js viewModelConverter() -- although yours will be easier because it will be flat, not nested list). Or you can generate them through converter builder.
Maybe it will be possible to use the approach above (simpler) but use contentEditable attribute to control the structure. rankList would have to be converted to <ol> with contentEditable="false". Maybe you could somehow use toWidget for better selection handling, for example. rankNotes and rankTitle would have to be converted to element with contentEditable="true" (maybe use toWidgetEditable()).

Use MVC 3 ViewBag to dynamically change an image

I'm new to MVC 3 and still learning. I'm currently building a static 2 page website which has a different image on both pages but are located in the same place. I have been told that I can use the ViewBag to change this image depending on what page I am on, however I have not got a clue where to begin with applying this to my website.
Any help will be greatly appreciated. Thank You for your time.
On your controller's Action method,make ViewBag or ViewData like this
ViewBag.Imagename="nameofimage";
ViewData["img"]="nameofimage";
On your view, you can make img tag like this
<img src="#ViewBag.Imagename">
or if you have created ViewData
<img src="#ViewData["img"]">
For some reason, it did not work for me when I simply tried using
#ViewBag.BackgroundImage = "~/Content/images/image.jpg"
...
<img src="#ViewBag.BackgroundImage" />
But what did work for me was
#ViewBag.BackgroundImage = "image.jpg"
...
<img src = "~/Content/images/#(ViewBag.BackgroundImage)" />
personally i dont like specifying anything view related in controller, i am using naming convention between action method names and images:
<img src="~/Content/images/#(ViewContext.RouteData.Values["action"].ToString().ToLower())_img.jpg">
and picture name for index action method would be "index_img.jpg"
For those who want use full path in viewbag, use this
#ViewBag.BackgroundImage = "Content/images/image.jpg"
...
<img src="~/#ViewBag.BackgroundImage" />
The reason why hallordylo example not working with full path is missing "~/" in src="..." and this sign "~/" should not be inside ViewBag.

Display page and picture in one request in MVC 3

I wondered if it is possible to show picture in a view without calling an action. The picture is displayed by using the following Razor code:
<img class="photo" src="#Url.Action("GetImage", "Home", new { id = #Model.Id })" />
But retrieving picture from server requires an additional request.
Is it possible to "deploy" image in ViewBag and show it in view without calling server?
Thanks,
Is it possible to "deploy" image in ViewBag and show it in view without calling server?
You could use the Data URI scheme. But be careful as it might not be supported by all browsers.
Example:
<img class="photo" src="data:image/png;base64,iVBORw0KG....." alt="" />
where src attribute of the image contains the Base64 encoded image that could come from ViewBag or a view model.

Visualforce link component for rerendering?

Is there a visualforce component for links? I'd like a link () on my page which can trigger an ajax call to one of the functions in the controller and rerender an element on the page.
This is how I'm doing it right now, but I don't want it to be a button, I need a link:
There are two standard apex link components, an apex:outputLink and an apex:commandLink. Both render anchor tags in html. From what you are asking, it sounds like need the command link, but I've posted information about both of them here.
You can find out more about them in the Visualforce Developer's Guide.
The apex:outputLink should be used when you want to create a standard hyperlink:
This component is rendered in HTML as an anchor tag
with an href attribute. Like its HTML equivalent, the body of an
is the text or image that displays as the link. To
add query string parameters to a link, use nested
components.
<apex:outputLink value="https://www.salesforce.com"
id="theLink">www.salesforce.com</apex:outputLink>
The example above renders the following HTML:
<a id="theLink" name="theLink"
href="https://www.salesforce.com">www.salesforce.com</a>
The apex:commandLink is probably what you need.
... executes an action defined by a controller, and then either
refreshes the current page, or navigates to a different page based on
the PageReference variable that is returned by the action. An
apex:commandLink component must always be a child of an apex:form
component.
<apex:commandLink action="{!save}" value="Save" id="theCommandLink"/>
The example above renders the following HTML:
<a id="thePage:theForm:theCommandLink" href="#" onclick="generatedJs()">Save</a>

How to modify body class in MVC3 view page

Just wanted to add a class="myClass" in body tag. Is there any html helper or something else can do this in MVC3 view page? Please advise, thanks.
This is very similar to Aaron's solution, but doesn't have the weight of a section (which at least in my mind, are for larger blocks of content than a single string). The simplest way is to pass a variable with the ViewBag.
In your layout, just print out the class for the body tag, plus any other page specific variables (page title, extra css/js scripts, etc...)
_Layout.cshtml:
<html>
<title>#ViewBag.Title</title>#* Can easily add in per page titles too *#
<body class="#ViewBag.BodyClass">
#RenderBody()
</body>
</html>
Then, variables set in your view get passed upwards to the layout:
Index.cshtml:
#model MyViewModel
#{
ViewBag.Title = "This page title!";
ViewBag.BodyClass = "...";
}
While you may have full control of the HTML a solution was what was needed so here is one ;-)
In the _layout.cshtml page
<body class="#RenderSection("BodyClass", false)">
This will look for a section in all the child pages but says don't worry if it can't find one
Then in your child views just do this
#section BodyClass {productList}
Keep it on one line and then the outputted HTML will look fine, also you can then build up the class names as well.
#section BodyClass {productList generic}
This idea goes perfect with DOM-Ready page specific code, why not checkout
http://paulirish.com/2009/markup-based-unobtrusive-comprehensive-dom-ready-execution/
Or my extended version here
https://github.com/AaronLayton/H5BP-Core
My way lets you do page specific code, but allows you to keep all of the Javascript in separate pages so each page becomes easily manageable. The final step would be to concatenate and minify all the JS into 1 file ;-)

Resources