diff options
author | Peter Bengtsson <mail@peterbe.com> | 2021-07-15 12:59:34 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-15 12:59:34 -0400 |
commit | 3601b7bb982e958927e069715cfe07430bce7196 (patch) | |
tree | d305ecdbf80ce8126386a0d7886f70d915277c7c /files/es/learn | |
parent | 9ace67d06f2369e3c770e3a11e06e1c8cc9f66fd (diff) | |
download | translated-content-3601b7bb982e958927e069715cfe07430bce7196.tar.gz translated-content-3601b7bb982e958927e069715cfe07430bce7196.tar.bz2 translated-content-3601b7bb982e958927e069715cfe07430bce7196.zip |
delete pages that were never translated from en-US (es, part 1) (#1547)
Diffstat (limited to 'files/es/learn')
4 files changed, 0 insertions, 1938 deletions
diff --git a/files/es/learn/css/css_layout/positioning/index.html b/files/es/learn/css/css_layout/positioning/index.html deleted file mode 100644 index 3deb33b91f..0000000000 --- a/files/es/learn/css/css_layout/positioning/index.html +++ /dev/null @@ -1,469 +0,0 @@ ---- -title: Positioning -slug: Learn/CSS/CSS_layout/Positioning -translation_of: Learn/CSS/CSS_layout/Positioning ---- -<div>{{LearnSidebar}}</div> - -<div>{{PreviousMenuNext("Learn/CSS/CSS_layout/Floats", "Learn/CSS/CSS_layout/Practical_positioning_examples", "Learn/CSS/CSS_layout")}}</div> - -<p class="summary">El posicionamiento permite sacar elementos del flujo normal del diseño del documento, y hacer que se comporten de manera distinta, por ejemplo sentarse encima de otro o permanecer en el mismo lugar dentro de la ventana navegador. Este artículo explica los diferentes valores {{cssxref("position")}}, y como usarlos.</p> - -<table class="learn-box standard-table"> - <tbody> - <tr> - <th scope="row">Requisitos:</th> - <td>HTML básico (Aprende <a href="/en-US/docs/Learn/HTML/Introduction_to_HTML"> Introducción a HTML</a>), y una idea de cómo trabaja CSS (Aprende <a href="/en-US/docs/Learn/CSS/Introduction_to_CSS">Introducción a CSS</a>.)</td> - </tr> - <tr> - <th scope="row">Objetivos:</th> - <td>Entender como trabajar con posicionamiento CSS.</td> - </tr> - </tbody> -</table> - -<h2 id="Flujo_del_Documento">Flujo del Documento</h2> - -<p>El posicionamiento es un tema bastante complejo, así que antes de profundizar en el código volvamos a examinar y ampliar un poco la teoría del diseño para darnos una idea de cómo funciona esto.</p> - -<p>First of all, individual element boxes are laid out by taking the elements' content, then adding any padding, border and margin around them — it's that <a href="/en-US/docs/Learn/CSS/Introduction_to_CSS/Box_model">box model</a> thing again, which we looked at earlier. By default, a block level element's content is 100% of the width of its parent element, and as tall as its content. Inline elements are all tall as their content, and as wide as their content. You can't set width or height on inline elements — they just sit inside the content of block level elements. If you want to control the size of an inline element in this manner, you need to set it to behave like a block level element with <code>display: block;</code>.</p> - -<p>That explains individual elements, but what about how elements interact with one another? The <strong>normal layout flow</strong> (mentioned in the layout introduction article) is the system by which elements are placed inside the browser's viewport. By default, block level elements are laid out vertically in the viewport — each one will appear on a new line below the last one, and they will be separated by any margin that is set on them.</p> - -<p>Inline elements behave differently — they don't appear on new lines; instead, they sit on the same line as one another and any adjacent (or wrapped) text content, as long as there is space for them to do so inside the width of the parent block level element. If there isn't space, then the overflowing text or elements will move down to a new line.</p> - -<p>If two adjacent elements both have margin set on them and the two margins touch, the larger of the two remains, and the smaller one disappears — this is called <a href="/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing">margin collapsing</a>, and we have met this before too.</p> - -<p>Let's look at a simple example that explains all this:</p> - -<pre class="brush: html"><h1>Basic document flow</h1> - -<p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p> - -<p>By default we span 100% of the width of our parent element, and we are as tall as our child content. Our total width and height is our content + padding + border width/height.</p> - -<p>We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our margins, not both.</p> - -<p>inline elements <span>like this one</span> and <span>this one</span> sit on the same line as one another, and adjacent text nodes, if there is space on the same line. Overflowing inline elements will <span>wrap onto a new line if possible (like this one containing text)</span>, or just go on to a new line if not, much like this image will do: <img src="https://mdn.mozillademos.org/files/13360/long.jpg"></p></pre> - -<pre class="brush: css">body { - width: 500px; - margin: 0 auto; -} - -p { - background: aqua; - border: 3px solid blue; - padding: 10px; - margin: 10px; -} - -span { - background: red; - border: 1px solid black; -}</pre> - -<p>{{ EmbedLiveSample('Document_flow', '100%', 500) }}</p> - -<p>We will be revisiting this example a number of times as we go through this article, as we show the effects of the different positioning options available to us.</p> - -<p>We'd like you to follow along with the exercises on your local computer, if possible — grab a copy of <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/0_basic-flow.html">0_basic-flow.html</a></code> from our Github repo (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/0_basic-flow.html">source code here</a>) and use that as a starting point.</p> - -<h2 id="Introducing_positioning">Introducing positioning</h2> - -<p>The whole idea of positioning is to allow us to override the basic document flow behaviour described above, to produce interesting effects. What if you want to slightly alter the position of some boxes inside a layout from their default layout flow position, to give a slightly quirky, distressed feel? Positioning is your tool. Or if you want to create a UI element that floats over the top of other parts of the page, and/or always sits in the same place inside the browser window no matter how much the page is scrolled? Positioning makes such layout work possible.</p> - -<p>There are a number of different types of positioning that you can put into effect on HTML elements. To make a specific type of positioning active on an element, we use the {{cssxref("position")}} property.</p> - -<h3 id="Static_positioning">Static positioning</h3> - -<p>Static positioning is the default that every element gets — it just means "put the element into its normal position in the document layout flow — nothing special to see here."</p> - -<p>To demonstrate this, and get your example set up for future sections, first add a <code>class</code> of <code>positioned</code> to the second {{htmlelement("p")}} in the HTML:</p> - -<pre class="brush: html"><p class="positioned"> ... </p></pre> - -<p>Now add the following rule to the bottom of your CSS:</p> - -<pre class="brush: css">.positioned { - position: static; - background: yellow; -}</pre> - -<p>If you now save and refresh, you'll see no difference at all, except for the updated background color of the 2nd paragraph. This is fine — as we said before, static positioning is the default behaviour!</p> - -<div class="note"> -<p><strong>Note</strong>: You can see the example at this point live at <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/1_static-positioning.html">1_static-positioning.html</a></code> (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/1_static-positioning.html">see source code</a>).</p> -</div> - -<h3 id="Relative_positioning">Relative positioning</h3> - -<p>Relative positioning is the first position type we'll take a look at. This is very similar to static positioning, except that once the positioned element has taken its place in the normal layout flow, you can then modify its final position, including making it overlap other elements on the page. Go ahead and update the position declaration in your code:</p> - -<pre class="brush: css">position: relative;</pre> - -<p>If you save and refresh at this stage, you won't see a change in the result at all — so how do you modify the element's position? You need to use the {{cssxref("top")}}, {{cssxref("bottom")}}, {{cssxref("left")}}, and {{cssxref("right")}} properties, which we'll explain in the next section.</p> - -<h3 id="Introducing_top_bottom_left_and_right">Introducing top, bottom, left, and right</h3> - -<p>{{cssxref("top")}}, {{cssxref("bottom")}}, {{cssxref("left")}}, and {{cssxref("right")}} are used alongside {{cssxref("position")}} to specify exactly where to move the positioned element to. To try this out, add the following declarations to the <code>.positioned</code> rule in your CSS:</p> - -<pre>top: 30px; -left: 30px;</pre> - -<div class="note"> -<p><strong>Note</strong>: The values of these properties can take any <a href="/en-US/docs/Learn/CSS/Introduction_to_CSS/Values_and_units">units</a> you'd logically expect — pixels, mm, rems, %, etc.</p> -</div> - -<p>If you now save and refresh, you'll get a result something like this:</p> - -<div class="hidden"> -<pre class="brush: html"><h1>Relative positioning</h1> - -<p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p> - -<p class="positioned">By default we span 100% of the width of our parent element, and we are as tall as our child content. Our total width and height is our content + padding + border width/height.</p> - -<p>We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our margins, not both.</p> - -<p>inline elements <span>like this one</span> and <span>this one</span> sit on the same line as one another, and adjacent text nodes, if there is space on the same line. Overflowing inline elements <span>wrap onto a new line if possible — like this one containing text</span>, or just go on to a new line if not, much like this image will do: <img src="https://mdn.mozillademos.org/files/13360/long.jpg"></p></pre> - -<pre class="brush: css">body { - width: 500px; - margin: 0 auto; -} - -p { - background: aqua; - border: 3px solid blue; - padding: 10px; - margin: 10px; -} - -span { - background: red; - border: 1px solid black; -} - -.positioned { - position: relative; - background: yellow; - top: 30px; - left: 30px; -}</pre> -</div> - -<p>{{ EmbedLiveSample('Introducing_top_bottom_left_and_right', '100%', 500) }}</p> - -<p>Cool, huh? Ok, so this probably wasn't what you were expecting — why has it moved to the bottom and right if we specified top and left? Illogical as it may initially sound, this is just the way that relative positioning works — you need to think of an invisible force that pushes the side of the positioned box, moving it in the opposite direction. So for example, if you specify <code>top: 30px;</code>, a force pushes the top of the box, causing it to move downwards by 30px.</p> - -<div class="note"> -<p><strong>Note</strong>: You can see the example at this point live at <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/2_relative-positioning.html">2_relative-positioning.html</a></code> (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/2_relative-positioning.html">see source code</a>).</p> -</div> - -<h3 id="Absolute_positioning">Absolute positioning</h3> - -<p>Absolute positioning brings very different results. Let's try changing the position declaration in your code as follows:</p> - -<pre>position: absolute;</pre> - -<p>If you now save and refresh, you should see something like so:</p> - -<div class="hidden"> -<pre class="brush: html"><h1>Absolute positioning</h1> - -<p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p> - -<p class="positioned">By default we span 100% of the width of our parent element, and we are as tall as our child content. Our total width and height is our content + padding + border width/height.</p> - -<p>We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our margins, not both.</p> - -<p>inline elements <span>like this one</span> and <span>this one</span> sit on the same line as one another, and adjacent text nodes, if there is space on the same line. Overflowing inline elements <span>wrap onto a new line if possible — like this one containing text</span>, or just go on to a new line if not, much like this image will do: <img src="https://mdn.mozillademos.org/files/13360/long.jpg"></p></pre> - -<pre class="brush: css">body { - width: 500px; - margin: 0 auto; -} - -p { - background: aqua; - border: 3px solid blue; - padding: 10px; - margin: 10px; -} - -span { - background: red; - border: 1px solid black; -} - -.positioned { - position: absolute; - background: yellow; - top: 30px; - left: 30px; -}</pre> -</div> - -<p>{{ EmbedLiveSample('Absolute_positioning', '100%', 420) }}</p> - -<p>First of all, note that the gap where the positioned element should be in the document flow is no longer there — the first and third elements have closed together like it no longer exists! Well, in a way, this is true. An absolutely positioned element no longer exists in the normal document layout flow. Instead, it sits on its own layer separate from everything else. This is very useful — it means that we can create isolated UI features that don't interfere with the position of other elements on the page — for example popup information boxes and control menus, rollover panels, UI features that can be dragged and dropped anywhere on the page, and so on.</p> - -<p>Second, notice that the position of the element has changed — this is because {{cssxref("top")}}, {{cssxref("bottom")}}, {{cssxref("left")}}, and {{cssxref("right")}} behave in a different way with absolute positioning. Instead of specifying the direction the element should move in, they specify the distance the element should be from each containing element's sides. So in this case, we are saying that the absolutely positioned element should sit 30px from the top of the "containing element", and 30px from the left.</p> - -<div class="note"> -<p><strong>Note</strong>: You can use {{cssxref("top")}}, {{cssxref("bottom")}}, {{cssxref("left")}}, and {{cssxref("right")}} to resize elements if you need to. Try setting <code>top: 0; bottom: 0; left: 0; right: 0;</code> and <code>margin: 0;</code> on your positioned elements and see what happens! Put it back again afterwards...</p> -</div> - -<div class="note"> -<p><strong>Note</strong>: Yes, margins still affect positioned elements. Margin collapsing doesn't, however.</p> -</div> - -<div class="note"> -<p><strong>Note</strong>: You can see the example at this point live at <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/3_absolute-positioning.html">3_absolute-positioning.html</a></code> (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/3_absolute-positioning.html">see source code</a>).</p> -</div> - -<h3 id="Positioning_contexts">Positioning contexts</h3> - -<p>Which element is the "containing element" of an absolutely positioned element? By default, it is the {{htmlelement("html")}} element — the positioned element is nested inside the {{htmlelement("body")}} in the HTML source, but in the final layout, it is 30px away from the top and left of the edge of the page, which is the {{htmlelement("html")}} element. This is more accurately called the element's <strong>positioning context</strong>.</p> - -<p>We can change the <strong>positioning context</strong> — which element the absolutely positioned element is positioned relative to. This is done by setting positioning on one of the element's other ancestors — one of the elements it is nested inside (you can't position it relative to an element it is not nested inside). To demonstrate this, add the following declaration to your body rule:</p> - -<pre>position: relative;</pre> - -<p>This should give the following result:</p> - -<div class="hidden"> -<pre class="brush: html"><h1>Positioning context</h1> - -<p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p> - -<p class="positioned">Now I'm absolutely positioned relative to the <code>&lt;body&gt;</code> element, not the <code>&lt;html&gt;</code> element!</p> - -<p>We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our margins, not both.</p> - -<p>inline elements <span>like this one</span> and <span>this one</span> sit on the same line as one another, and adjacent text nodes, if there is space on the same line. Overflowing inline elements <span>wrap onto a new line if possible — like this one containing text</span>, or just go on to a new line if not, much like this image will do: <img src="https://mdn.mozillademos.org/files/13360/long.jpg"></p></pre> - -<pre class="brush: css">body { - width: 500px; - margin: 0 auto; - position: relative; -} - -p { - background: aqua; - border: 3px solid blue; - padding: 10px; - margin: 10px; -} - -span { - background: red; - border: 1px solid black; -} - -.positioned { - position: absolute; - background: yellow; - top: 30px; - left: 30px; -}</pre> -</div> - -<p>{{ EmbedLiveSample('Positioning_contexts', '100%', 420) }}</p> - -<p>The positioned element now sits relative to the {{htmlelement("body")}} element.</p> - -<div class="note"> -<p><strong>Note</strong>: You can see the example at this point live at <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/4_positioning-context.html">4_positioning-context.html</a></code> (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/4_positioning-context.html">see source code</a>).</p> -</div> - -<h3 id="Introducing_z-index">Introducing z-index</h3> - -<p>All this absolute positioning is good fun, but there is another thing we haven't considered yet — when elements start to overlap, what determines which elements appear on top of which other elements? In the example we've seen so far, we only have one positioned element in the positioning context, and it appears on the top, since positioned elements win over non-positioned elements. What about when we have more than one?</p> - -<p>Try adding the following to your CSS, to make the first paragraph absolutely positioned too:</p> - -<pre>p:nth-of-type(1) { - position: absolute; - background: lime; - top: 10px; - right: 30px; -}</pre> - -<p>At this point you'll see the first paragraph colored green, moved out of the document flow, and positioned a bit above from where it originally was. It is also stacked below the original <code>.positioned</code> paragraph, where the two overlap. This is because the <code>.positioned</code> paragraph is the second paragraph in the source order, and positioned elements later in the source order win over positioned elements earlier in the source order.</p> - -<p>Can you change the stacking order? Yes, you can, by using the {{cssxref("z-index")}} property. "z-index" is a reference to the z-axis. You may recall from previous points in the source where we discussed web pages using horizontal (x-axis) and vertical (y-axis) coordinates to work out positioning for things like background images and drop shadow offsets. (0,0) is at the top left of the page (or element), and the x- and y-axes run across to the right and down the page (for left to right languages, anyway.)</p> - -<p>Web pages also have a z-axis — an imaginary line that runs from the surface of your screen, towards your face (or whatever else you like to have in front of the screen). {{cssxref("z-index")}} values affect where positioned elements sit on that axis — positive values move them higher up the stack, and negative values move them lower down the stack. By default, positioned elements all have a <code>z-index</code> of auto, which is effectively 0.</p> - -<p>To change the stacking order, try adding the following declaration to your <code>p:nth-of-type(1)</code> rule:</p> - -<pre>z-index: 1;</pre> - -<p>You should now see the finished example:</p> - -<div class="hidden"> -<pre class="brush: html"><h1>z-index</h1> - -<p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p> - -<p class="positioned">Now I'm absolutely positioned relative to the <code>&lt;body&gt;</code> element, not the <code>&lt;html&gt;</code> element!</p> - -<p>We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our margins, not both.</p> - -<p>inline elements <span>like this one</span> and <span>this one</span> sit on the same line as one another, and adjacent text nodes, if there is space on the same line. Overflowing inline elements <span>wrap onto a new line if possible — like this one containing text</span>, or just go on to a new line if not, much like this image will do: <img src="https://mdn.mozillademos.org/files/13360/long.jpg"></p></pre> - -<pre class="brush: css">body { - width: 500px; - margin: 0 auto; - position: relative; -} - -p { - background: aqua; - border: 3px solid blue; - padding: 10px; - margin: 10px; -} - -span { - background: red; - border: 1px solid black; -} - -.positioned { - position: absolute; - background: yellow; - top: 30px; - left: 30px; -} - -p:nth-of-type(1) { - position: absolute; - background: lime; - top: 10px; - right: 30px; - z-index: 1; -} -</pre> -</div> - -<p>{{ EmbedLiveSample('Introducing_z-index', '100%', 400) }}</p> - -<p>Note that <code>z-index</code> only accepts unitless index values; you can't specify that you want one element to be 23 pixels up the Z-axis — it doesn't work like that. Higher values will go above lower values, and it is up to you what values you use. Using 2 and 3 would give the same effect as 300 and 40000.</p> - -<div class="note"> -<p><strong>Note</strong>: You can see the example at this point live at <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/5_z-index.html">5_z-index.html</a></code> (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/5_z-index.html">see source code</a>).</p> -</div> - -<h3 id="Fixed_positioning">Fixed positioning</h3> - -<p>There is one more type of positioning to cover — fixed. This works in exactly the same way as absolute positioning, with one key difference — whereas absolute positioning fixes an element in place relative to the {{htmlelement("html")}} element or its nearest positioned ancestor, fixed positioning fixes an element in place relative to the browser viewport itself. This means that you can create useful UI items that are fixed in place, like persisting navigation menus.</p> - -<p>Let's put together a simple example to show what we mean. First of all, delete the existing <code>p:nth-of-type(1)</code> and <code>.positioned</code> rules from your CSS.</p> - -<p>Now, update the <code>body</code> rule to remove the <code>position: relative;</code> declaration and add a fixed height, like so:</p> - -<pre>body { - width: 500px; - height: 1400px; - margin: 0 auto; -}</pre> - -<p>Now we're going to give the {{htmlelement("h1")}} element <code>position: fixed;</code>, and get it to sit at the top center of the viewport. Add the following rule to your CSS:</p> - -<pre>h1 { - position: fixed; - top: 0; - width: 500px; - margin: 0 auto; - background: white; - padding: 10px; -}</pre> - -<p>The <code>top: 0;</code> is required to make it stick to the top of the screen; we then give the heading the same width as the content column and use the faithful old <code>margin: 0 auto;</code> trick to center it. We then give it a white background and some padding, so the content won't be visible underneath it.</p> - -<p>If you save and refresh now, you'll see a fun little effect whereby the heading stays fixed, and the content appears to scroll up and disappear underneath it. But we could improve this more — at the moment some of the content starts off underneath the heading. This is because the positioned heading no longer appears in the document flow, so the rest of the content moves up to the top. We need to move it all down a bit; we can do this by setting some top margin on the first paragraph. Add this now:</p> - -<pre>p:nth-of-type(1) { - margin-top: 60px; -}</pre> - -<p>You should now see the finished example:</p> - -<div class="hidden"> -<pre class="brush: html"><h1>Fixed positioning</h1> - -<p>I am a basic block level element. My adjacent block level elements sit on new lines below me.</p> - -<p class="positioned">I'm not positioned any more...</p> - -<p>We are separated by our margins. Because of margin collapsing, we are separated by the width of one of our margins, not both.</p> - -<p>inline elements <span>like this one</span> and <span>this one</span> sit on the same line as one another, and adjacent text nodes, if there is space on the same line. Overflowing inline elements <span>wrap onto a new line if possible — like this one containing text</span>, or just go on to a new line if not, much like this image will do: <img src="https://mdn.mozillademos.org/files/13360/long.jpg"></p></pre> - -<pre class="brush: css">body { - width: 500px; - height: 1400px; - margin: 0 auto; -} - -p { - background: aqua; - border: 3px solid blue; - padding: 10px; - margin: 10px; -} - -span { - background: red; - border: 1px solid black; -} - -h1 { - position: fixed; - top: 0px; - width: 500px; - margin: 0 auto; - background: white; - padding: 10px; -} - -p:nth-of-type(1) { - margin-top: 60px; -}</pre> - -<p> </p> -</div> - -<p>{{ EmbedLiveSample('Fixed_positioning', '100%', 400) }}</p> - -<div class="note"> -<p><strong>Note</strong>: You can see the example at this point live at <code><a href="http://mdn.github.io/learning-area/css/css-layout/positioning/6_fixed-positioning.html">6_fixed-positioning.html</a></code> (<a href="https://github.com/mdn/learning-area/blob/master/css/css-layout/positioning/6_fixed-positioning.html">see source code</a>).</p> -</div> - -<h3 id="Experimental_position_sticky">Experimental: position sticky</h3> - -<p>There is a new positioning value available called <code>position: sticky</code>, support for which is not very widespread yet. This is basically a hybrid between relative and fixed position, which allows a positioned element to act like it is relatively positioned until it is scrolled to a certain threshold point (e.g. 10px from the top of the viewport), after which it becomes fixed. See our <a href="/en-US/docs/Web/CSS/position#Sticky_positioning">position: sticky reference entry</a> for more details and an example.</p> - -<h2 id="Summary">Summary</h2> - -<p>I'm sure you had fun playing with basic positioning — it is one of the essential tools behind creating complex CSS layouts and UI features. With that in mind, in the next article we'll have even more fun with positioning — there we'll go a step further and start to build up some real world useful things with it.</p> - -<p>{{PreviousMenuNext("Learn/CSS/CSS_layout/Floats", "Learn/CSS/CSS_layout/Practical_positioning_examples", "Learn/CSS/CSS_layout")}}</p> - -<p> </p> - -<h2 id="In_this_module">In this module</h2> - -<ul> - <li><a href="/en-US/docs/Learn/CSS/CSS_layout/Introduction">Introduction to CSS layout</a></li> - <li><a href="/en-US/docs/Learn/CSS/CSS_layout/Floats">Floats</a></li> - <li><a href="/en-US/docs/Learn/CSS/CSS_layout/Positioning">Positioning</a></li> - <li><a href="/en-US/docs/Learn/CSS/CSS_layout/Practical_positioning_examples">Practical positioning examples</a></li> - <li><a href="/en-US/docs/Learn/CSS/CSS_layout/Flexbox">Flexbox</a></li> - <li><a href="/en-US/docs/Learn/CSS/CSS_layout/Grids">Grids</a></li> -</ul> - -<p> </p> diff --git a/files/es/learn/javascript/client-side_web_apis/fetching_data/index.html b/files/es/learn/javascript/client-side_web_apis/fetching_data/index.html deleted file mode 100644 index ab34b8c319..0000000000 --- a/files/es/learn/javascript/client-side_web_apis/fetching_data/index.html +++ /dev/null @@ -1,373 +0,0 @@ ---- -title: Fetching data from the server -slug: Learn/JavaScript/Client-side_web_APIs/Fetching_data -translation_of: Learn/JavaScript/Client-side_web_APIs/Fetching_data ---- -<div>{{LearnSidebar}}</div> - -<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div> - -<p class="summary">Otra tarea muy común en páginas web y en aplicaciones es tomar elementos individuales de datos desde el servidor para actualizar secciones de la página web sin tener que cargar toda una nueva página. Este aparentemente pequeño detalle tiene un gran impacto en el desempeño y comportamiento de los sitios, por eso en este artículo, explicaremos el concepto y veremos las tecnologías que hacen esto posible, como ser XHLHttpRequest y FetchAPI. </p> - -<table class="learn-box standard-table"> - <tbody> - <tr> - <th scope="row">Prerequisitos:</th> - <td>JavaScript básico (ver <a href="/en-US/docs/Learn/JavaScript/First_steps">first steps</a>, <a href="/en-US/docs/Learn/JavaScript/Building_blocks">building blocks</a>, <a href="/en-US/docs/Learn/JavaScript/Objects">JavaScript objects</a>), <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">basics of Client-side APIs</a></td> - </tr> - <tr> - <th scope="row">Objetivo:</th> - <td>Aprender como extraer datos desde el servidor y usarlo para cargar contenido en la página web. </td> - </tr> - </tbody> -</table> - -<h2 id="Cúal_es_el_problema_aquí">Cúal es el problema aquí?</h2> - -<p>Originalmente cargar páginas en la web era simple — tu enviabas una solicitud de la página web al servidor, y si no había ningún problema, los servicios encargados de la página web la descargaban y mostraban en tu computadora.</p> - -<p><img alt="A basic representation of a web site architecture" src="https://mdn.mozillademos.org/files/6475/web-site-architechture@2x.png" style="display: block; height: 134px; margin: 0px auto; width: 484px;"></p> - -<p>El problema con este modelo es que si tu quieres actualizar cualquier parte de la página, por ejemplo, mostrar el nuevo set de productos o cargar una nueva página, tu tenías que cargar toda la página de nuevo. Esto es extremadamente un desperdicio y resultaba ser una experiencia pobre al usuario, especialmente cuando la página es más grande y más compleja. </p> - -<h3 id="Introduciendo_Ajax">Introduciendo Ajax</h3> - -<p>This led to the creation of technologies that allow web pages to request small chunks of data (such as <a href="/en-US/docs/Web/HTML">HTML</a>, {{glossary("XML")}}, <a href="/en-US/docs/Learn/JavaScript/Objects/JSON">JSON</a>, or plain text) and display them only when needed, helping to solve the problem described above.</p> - -<p>This is achieved by using APIs like {{domxref("XMLHttpRequest")}} or — more recently — the <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a>. These technologies allow web pages to directly handle making <a href="/en-US/docs/Web/HTTP">HTTP</a> requests for specific resources available on a server and formatting the resulting data as needed before it is displayed.</p> - -<div class="note"> -<p><strong>Note</strong>: In the early days, this general technique was known as <a href="https://developer.mozilla.org/en-US/docs/Glossary/Asynchronous">Asynchronous</a> JavaScript and XML (<a href="https://developer.mozilla.org/en-US/docs/Glossary/Ajax">Ajax</a>), because it tended to use {{domxref("XMLHttpRequest")}} to request XML data. This is normally not the case these days (you'd be more likely to use <code>XMLHttpRequest</code> or Fetch to request JSON), but the result is still the same, and the term "Ajax" is still often used to describe the technique.</p> -</div> - -<p><img alt="A simple modern architecture for web sites" src="https://mdn.mozillademos.org/files/6477/moderne-web-site-architechture@2x.png" style="display: block; height: 235px; margin: 0px auto; width: 559px;"></p> - -<p>The Ajax model involves using a web API as a proxy to more intelligently request data rather than just having the browser reload the entire page. Let's think about the significance of this:</p> - -<ol> - <li>Go to one of your favorite information-rich sites, like Amazon, YouTube, CNN, etc., and load it.</li> - <li>Now search for something, like a new product. The main content will change, but most of the surrounding information, like the header, footer, navigation menu, etc., will stay the same.</li> -</ol> - -<p>This is a really good thing because:</p> - -<ul> - <li>Page updates are a lot quicker and you don't have to wait for the page to refresh, meaning that the site feels faster and more responsive.</li> - <li>Less data is downloaded on each update, meaning less wasted bandwidth. This may not be such a big issue on a desktop on a broadband connection, but it's a major issue on mobile devices and in developing countries that don't have ubiquitous fast Internet service.</li> -</ul> - -<p>To speed things up even further, some sites also store assets and data on the user's computer when they are first requested, meaning that on subsequent visits they use the local versions instead of downloading fresh copies when the page is first loaded. The content is only reloaded from the server when it has been updated.</p> - -<p><img alt="A basic web app data flow architecture" src="https://mdn.mozillademos.org/files/6479/web-app-architecture@2x.png" style="display: block; height: 383px; margin: 0px auto; width: 562px;"></p> - -<h2 id="A_basic_Ajax_request">A basic Ajax request</h2> - -<p>Let's look at how such a request is handled, using both {{domxref("XMLHttpRequest")}} and <a href="/en-US/docs/Web/API/Fetch_API">Fetch</a>. For these examples, we'll request data out of a few different text files and use them to populate a content area.</p> - -<p>This series of files will act as our fake database; in a real application, we'd be more likely to use a server-side language like PHP, Python, or Node to request our data from a database. Here, however, we want to keep it simple and concentrate on the client-side part of this.</p> - -<h3 id="XMLHttpRequest">XMLHttpRequest</h3> - -<p><code>XMLHttpRequest</code> (which is frequently abbreviated to XHR) is a fairly old technology now — it was invented by Microsoft in the late '90s, and has been standardized across browsers for quite a long time.</p> - -<ol> - <li> - <p>To begin this example, make a local copy of <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/ajax-start.html">ajax-start.html</a> and the four text files — <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a> — in a new directory on your computer. In this example, we will load a different verse of the poem (which you may well recognize) via XHR when it's selected in the drop-down menu.</p> - </li> - <li> - <p>Just inside the {{htmlelement("script")}} element, add the following code. This stores a reference to the {{htmlelement("select")}} and {{htmlelement("pre")}} elements in variables and defines an {{domxref("GlobalEventHandlers.onchange","onchange")}} event handler function so that when the select's value is changed, its value is passed to an invoked function <code>updateDisplay()</code> as a parameter.</p> - - <pre class="brush: js">var verseChoose = document.querySelector('select'); -var poemDisplay = document.querySelector('pre'); - -verseChoose.onchange = function() { - var verse = verseChoose.value; - updateDisplay(verse); -};</pre> - </li> - <li> - <p>Let's define our <code>updateDisplay()</code> function. First of all, put the following beneath your previous code block — this is the empty shell of the function:</p> - - <pre class="brush: js">function updateDisplay(verse) { - -};</pre> - </li> - <li> - <p>We'll start our function by constructing a relative URL pointing to the text file we want to load, as we'll need it later. The value of the {{htmlelement("select")}} element at any time is the same as the text inside the selected {{htmlelement("option")}} (unless you specify a different value in a value attribute) — so for example "Verse 1". The corresponding verse text file is "verse1.txt", and is in the same directory as the HTML file, therefore just the file name will do.</p> - - <p>However, web servers tend to be case sensitive, and the file name doesn't have a space in it. To convert "Verse 1" to "verse1.txt" we need to convert the V to lower case, remove the space, and add .txt on the end. This can be done with {{jsxref("String.replace", "replace()")}}, {{jsxref("String.toLowerCase", "toLowerCase()")}}, and simple <a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Concatenating_strings">string concatenation</a>. Add the following lines inside your <code>updateDisplay()</code> function:</p> - - <pre class="brush: js">verse = verse.replace(" ", ""); -verse = verse.toLowerCase(); -var url = verse + '.txt';</pre> - </li> - <li> - <p>To begin creating an XHR request, you need to create a new request object using the {{domxref("XMLHttpRequest.XMLHttpRequest", "XMLHttpRequest()")}} constructor. You can call this object anything you like, but we'll call it <code>request</code> to keep things simple. Add the following below your previous lines:</p> - - <pre class="brush: js">var request = new XMLHttpRequest();</pre> - </li> - <li> - <p>Next, you need to use the {{domxref("XMLHttpRequest.open","open()")}} method to specify what <a href="/en-US/docs/Web/HTTP/Methods">HTTP request method</a> to use to request the resource from the network, and what its URL is. We'll just use the <code><a href="/en-US/docs/Web/HTTP/Methods/GET">GET</a></code> method here and set the URL as our <code>url</code> variable. Add this below your previous line:</p> - - <pre class="brush: js">request.open('GET', url);</pre> - </li> - <li> - <p>Next, we'll set the type of response we are expecting — which is defined by the request's {{domxref("XMLHttpRequest.responseType", "responseType")}} property — as <code>text</code>. This isn't strictly necessary here — XHR returns text by default — but it is a good idea to get into the habit of setting this in case you want to fetch other types of data in the future. Add this next:</p> - - <pre class="brush: js">request.responseType = 'text';</pre> - </li> - <li> - <p>Fetching a resource from the network is an {{glossary("asynchronous")}} operation, meaning that you have to wait for that operation to complete (e.g., the resource is returned from the network) before you can do anything with that response, otherwise, an error will be thrown. XHR allows you to handle this using its {{domxref("XMLHttpRequest.onload", "onload")}} event handler — this is run when the {{event("load")}} event fires (when the response has returned). When this has occurred, the response data will be available in the <code>response</code> property of the XHR request object.</p> - - <p>Add the following below your last addition. You'll see that inside the <code>onload</code> event handler we are setting the <code><a href="/en-US/docs/Web/API/Node/textContent">textContent</a></code> of the <code>poemDisplay</code> (the {{htmlelement("pre")}} element) to the value of the {{domxref("XMLHttpRequest.response", "request.response")}} property.</p> - - <pre class="brush: js">request.onload = function() { - poemDisplay.textContent = request.response; -};</pre> - </li> - <li> - <p>The above is all set up for the XHR request — it won't actually run until we tell it to, which is done using the {{domxref("XMLHttpRequest.send","send()")}} method. Add the following below your previous addition to complete the function:</p> - - <pre class="brush: js">request.send();</pre> - </li> - <li> - <p>One problem with the example as it stands is that it won't show any of the poem when it first loads. To fix this, add the following two lines at the bottom of your code (just above the closing <code></script></code> tag) to load verse 1 by default, and make sure the {{htmlelement("select")}} element always shows the correct value:</p> - - <pre class="brush: js">updateDisplay('Verse 1'); -verseChoose.value = 'Verse 1';</pre> - </li> -</ol> - -<h3 id="Serving_your_example_from_a_server">Serving your example from a server</h3> - -<p>Some browsers (including Chrome) will not run XHR requests if you just run the example from a local file. This is because of security restrictions (for more on web security, read <a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Website security</a>).</p> - -<p>To get around this, we need to test the example by running it through a local web server. To find out how to do this, read <a href="/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server">How do you set up a local testing server?</a></p> - -<h3 id="Fetch">Fetch</h3> - -<p>The Fetch API is basically a modern replacement for XHR; it was introduced in browsers recently to make asynchronous HTTP requests easier to do in JavaScript, both for developers and other APIs that build on top of Fetch.</p> - -<p>Let's convert the last example to use Fetch instead.</p> - -<ol> - <li> - <p>Make a copy of your previous finished example directory. (If you didn't work through the previous exercise, create a new directory and inside it make copies of <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">xhr-basic.html</a> and the four text files — <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a>.)</p> - </li> - <li> - <p>Inside the <code>updateDisplay()</code> function, find the XHR code:</p> - - <pre class="brush: js">var request = new XMLHttpRequest(); -request.open('GET', url); -request.responseType = 'text'; - -request.onload = function() { - poemDisplay.textContent = request.response; -}; - -request.send();</pre> - </li> - <li> - <p>Replace all the XHR code with this:</p> - - <pre class="brush: js">fetch(url).then(function(response) { - response.text().then(function(text) { - poemDisplay.textContent = text; - }); -});</pre> - </li> - <li> - <p>Load the example in your browser (running it through a web server) and it should work just the same as the XHR version, provided you are running a modern browser.</p> - </li> -</ol> - -<h4 id="So_what_is_going_on_in_the_Fetch_code">So what is going on in the Fetch code?</h4> - -<p>First of all, we invoke the {{domxref("WorkerOrWindowGlobalScope.fetch()","fetch()")}} method, passing it the URL of the resource we want to fetch. This is the modern equivalent of {{domxref("XMLHttpRequest.open","request.open()")}} in XHR, plus you don't need any equivalent to <code>.send()</code>.</p> - -<p>After that, you can see the {{jsxref("Promise.then",".then()")}} method chained onto the end of <code>fetch()</code> — this method is a part of {{jsxref("Promise","Promises")}}, a modern JavaScript feature for performing asynchronous operations. <code>fetch()</code> returns a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promise</a>, which resolves to the response sent back from the server — we use <code>.then()</code> to run some follow-up code after the promise resolves, which is the function we've defined inside it. This is the equivalent of the <code>onload</code> event handler in the XHR version.</p> - -<p>This function is automatically given the response from the server as a parameter when the <code>fetch()</code> promise resolves. Inside the function we grab the response and run its {{domxref("Body.text","text()")}} method, which basically returns the response as raw text. This is the equivalent of <code>request.responseType = 'text'</code> in the XHR version.</p> - -<p>You'll see that <code>text()</code> also returns a promise, so we chain another <code>.then()</code> onto it, inside of which we define a function to receive the raw text that the <code>text()</code> promise resolves to.</p> - -<p>Inside the inner promise's function, we do much the same as we did in the XHR version — set the {{htmlelement("pre")}} element's text content to the text value.</p> - -<h3 id="Aside_on_promises">Aside on promises</h3> - -<p>Promises are a bit confusing the first time you meet them, but don't worry too much about this for now. You'll get used to them after a while, especially as you learn more about modern JavaScript APIs — most of the newer ones are heavily based on promises.</p> - -<p>Let's look at the promise structure from above again to see if we can make some more sense of it:</p> - -<pre class="brush: js">fetch(url).then(function(response) { - response.text().then(function(text) { - poemDisplay.textContent = text; - }); -});</pre> - -<p>The first line is saying "fetch the resource located at URL" (<code>fetch(url)</code>) and "then run the specified function when the promise resolves" (<code>.then(function() { ... })</code>). "Resolve" means "finish performing the specified operation at some point in the future". The specified operation, in this case, is to fetch a resource from a specified URL (using an HTTP request), and return the response for us to do something with.</p> - -<p>Effectively, the function passed into <code>then()</code> is a chunk of code that won't run immediately. Instead, it will run at some point in the future when the response has been returned. Note that you could also choose to store your promise in a variable and chain {{jsxref("Promise.then",".then()")}} onto that instead. The code below would do the same thing:</p> - -<pre class="brush: js">var myFetch = fetch(url); - -myFetch.then(function(response) { - response.text().then(function(text) { - poemDisplay.textContent = text; - }); -});</pre> - -<p>Because the <code>fetch()</code> method returns a promise that resolves to the HTTP response, any function you define inside a <code>.then()</code> chained onto the end of it will automatically be given the response as a parameter. You can call the parameter anything you like — the below example would still work:</p> - -<pre class="brush: js">fetch(url).then(function(dogBiscuits) { - dogBiscuits.text().then(function(text) { - poemDisplay.textContent = text; - }); -});</pre> - -<p>But it makes more sense to call the parameter something that describes its contents.</p> - -<p>Now let's focus just on the function:</p> - -<pre class="brush: js">function(response) { - response.text().then(function(text) { - poemDisplay.textContent = text; - }); -}</pre> - -<p>The response object has a method {{domxref("Body.text","text()")}} that takes the raw data contained in the response body and turns it into plain text — the format we want it in. It also returns a promise (which resolves to the resulting text string), so here we use another {{jsxref("Promise.then",".then()")}}, inside of which we define another function that dictates what we want to do with that text string. We are just setting the <code><a href="/en-US/docs/Web/API/Node/textContent">textContent</a></code> property of our poem's {{htmlelement("pre")}} element to equal the text string, so this works out pretty simple.</p> - -<p>It is also worth noting that you can directly chain multiple promise blocks (<code>.then()</code> blocks, but there are other types too) onto the end of one another, passing the result of each block to the next block as you travel down the chain. This makes promises very powerful.</p> - -<p>The following block does the same thing as our original example, but is written in a different style:</p> - -<pre class="brush: js">fetch(url).then(function(response) { - return response.text() -}).then(function(text) { - poemDisplay.textContent = text; -});</pre> - -<p>Many developers like this style better, as it is flatter and arguably easier to read for longer promise chains — each subsequent promise comes after the previous one, rather than being inside the previous one (which can get unwieldy). The only other difference is that we've had to include a <code><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">return</a></code> statement in front of <code>response.text()</code>, to get it to pass its result on to the next link in the chain.</p> - -<h3 id="Which_mechanism_should_you_use">Which mechanism should you use?</h3> - -<p>This really depends on what project you are working on. XHR has been around for a long time now and has very good cross-browser support. Fetch and Promises, on the other hand, are a more recent addition to the web platform, although they're supported well across the browser landscape, with the exception of Internet Explorer.</p> - -<p>If you need to support older browsers, then an XHR solution might be preferable. If however you are working on a more progressive project and aren't as worried about older browsers, then Fetch could be a good choice.</p> - -<p>You should really learn both — Fetch will become more popular as Internet Explorer declines in usage (IE is no longer being developed, in favor of Microsoft's new Edge browser), but you might need XHR for a while yet.</p> - -<h2 id="A_more_complex_example">A more complex example</h2> - -<p>To round off the article, we'll look at a slightly more complex example that shows some more interesting uses of Fetch. We have created a sample site called The Can Store — it's a fictional supermarket that only sells canned goods. You can find this <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/">example live on GitHub</a>, and <a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/fetching-data/can-store">see the source code</a>.</p> - -<p><img alt="A fake ecommerce site showing search options in the left hand column, and product search results in the right hand column." src="https://mdn.mozillademos.org/files/14779/can-store.png" style="display: block; margin: 0 auto;"></p> - -<p>By default, the site displays all the products, but you can use the form controls in the left hand column to filter them by category, or search term, or both.</p> - -<p>There is quite a lot of complex code that deals with filtering the products by category and search terms, manipulating strings so the data displays correctly in the UI, etc. We won't discuss all of it in the article, but you can find extensive comments in the code (see <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-script.js">can-script.js</a>).</p> - -<p>We will however explain the Fetch code.</p> - -<p>The first block that uses Fetch can be found at the start of the JavaScript:</p> - -<pre class="brush: js">fetch('products.json').then(function(response) { - return response.json(); -}).then(function(json) { - products = json; - initialize(); -}).catch(function(err) { - console.log('Fetch problem: ' + err.message); -});</pre> - -<p>The <code>fetch()</code> function returns a <code>promise</code>. If this completes successfully, the function inside the first <code>.then()</code> block contains the <code>response</code> returned from the network.</p> - -<p>Inside this function we run {{domxref("Body.json","json()")}} on the response, not {{domxref("Body.text","text()")}}, as we want to return our response as structured JSON data, not plain text.</p> - -<p>Next, we chain another <code>.then()</code> onto the end of our first one, the success function that contains the <code>json</code> returned from the <code>response.json()</code> promise. We set this to be the value of the products global object, then run <code>initialize()</code>, which starts the process of displaying all the products in the user interface.</p> - -<p>To handle errors, we chain a <code>.catch()</code> block onto the end of the chain. This runs if the promise fails for some reason. Inside it, we include a function that is passed as a parameter, an <code>error</code> object. This <code>error</code> object can be used to report the nature of the error that has occurred, in this case we do it with a simple <code>console.log()</code>.</p> - -<p>However, a complete website would handle this error more gracefully by displaying a message on the user's screen and perhaps offering options to remedy the situation, but we don't need anything more than a simple <code>console.log()</code>.</p> - -<p>You can test the fail case yourself:</p> - -<ol> - <li>Make a local copy of the example files (download and unpack <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">the can-store ZIP file</a>).</li> - <li>Run the code through a web server (as described above, in {{anch("Serving your example from a server")}}).</li> - <li>Modify the path to the file being fetched, to something like 'produc.json' (make sure it is misspelled).</li> - <li>Now load the index file in your browser (via <code>localhost:8000</code>) and look in your browser developer console. You'll see a message similar to "Network request for products.json failed with response 404: File not found".</li> -</ol> - -<p>The second Fetch block can be found inside the <code>fetchBlob()</code> function:</p> - -<pre class="brush: js">fetch(url).then(function(response) { - return response.blob(); -}).then(function(blob) { - // Convert the blob to an object URL — this is basically an temporary internal URL - // that points to an object stored inside the browser - var objectURL = URL.createObjectURL(blob); - // invoke showProduct - showProduct(objectURL, product); -});</pre> - -<p>This works in much the same way as the previous one, except that instead of using {{domxref("Body.json","json()")}}, we use {{domxref("Body.blob","blob()")}}. In this case we want to return our response as an image file, and the data format we use for that is <a href="/en-US/docs/Web/API/Blob">Blob</a> (the term is an abbreviation of "Binary Large Object" and can basically be used to represent large file-like objects, such as images or video files).</p> - -<p>Once we've successfully received our blob, we create an object URL out of it using {{domxref("URL.createObjectURL()", "createObjectURL()")}}. This returns a temporary internal URL that points to an object referenced inside the browser. These are not very readable, but you can see what one looks like by opening up the Can Store app, Ctrl-/Right-clicking on an image, and selecting the "View image" option (which might vary slightly depending on what browser you are using). The object URL will be visible inside the address bar, and should be something like this:</p> - -<pre>blob:http://localhost:7800/9b75250e-5279-e249-884f-d03eb1fd84f4</pre> - -<h3 id="Challenge_An_XHR_version_of_the_Can_Store">Challenge: An XHR version of the Can Store</h3> - -<p>We'd like you to try converting the Fetch version of the app to use XHR as a useful bit of practice. Take a <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">copy of the ZIP file</a>, and try modifying the JavaScript as appropriate.</p> - -<p>Some helpful hints:</p> - -<ul> - <li>You might find the {{domxref("XMLHttpRequest")}} reference material useful.</li> - <li>You will basically need to use the same pattern as you saw earlier in the <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">XHR-basic.html</a> example.</li> - <li>You will, however, need to add the error handling we showed you in the Fetch version of the Can Store: - <ul> - <li>The response is found in <code>request.response</code> after the <code>load</code> event has fired, not in a promise <code>then()</code>.</li> - <li>About the best equivalent to Fetch's <code>response.ok</code> in XHR is to check whether {{domxref("XMLHttpRequest.status","request.status")}} is equal to 200, or if {{domxref("XMLHttpRequest.readyState","request.readyState")}} is equal to 4.</li> - <li>The properties for getting the status and status message are the same, but they are found on the <code>request</code> (XHR) object, not the <code>response</code> object.</li> - </ul> - </li> -</ul> - -<div class="note"> -<p><strong>Note</strong>: If you have trouble with this, feel free to check your code against the finished version on GitHub (<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store-xhr/can-script.js">see the source here</a>, and also <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store-xhr/">see it running live</a>).</p> -</div> - -<h2 id="Summary">Summary</h2> - -<p>This article shows how to start working with both XHR and Fetch to fetch data from the server.</p> - -<h2 id="See_also">See also</h2> - -<p>There are however a lot of different subjects discussed in this article, which has only really scratched the surface. For a lot more detail on these subjects, try the following articles:</p> - -<ul> - <li><a href="/en-US/docs/AJAX/Getting_Started">Ajax — Getting started</a></li> - <li><a href="/en-US/docs/Web/API/Fetch_API/Using_Fetch">Using Fetch</a></li> - <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Working with JSON data</a></li> - <li><a href="/en-US/docs/Web/HTTP/Overview">An overview of HTTP</a></li> - <li><a href="/en-US/docs/Learn/Server-side">Server-side website programming</a></li> -</ul> - -<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div> - -<div> -<h2 id="In_this_module">In this module</h2> - -<ul> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Introduction to web APIs</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Third party APIs</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Video and audio APIs</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Client-side storage</a></li> -</ul> -</div> diff --git a/files/es/learn/server-side/express_nodejs/mongoose/index.html b/files/es/learn/server-side/express_nodejs/mongoose/index.html deleted file mode 100644 index f5701c468a..0000000000 --- a/files/es/learn/server-side/express_nodejs/mongoose/index.html +++ /dev/null @@ -1,801 +0,0 @@ ---- -title: 'Express Tutorial 3: Utilizando bases de datos (con Mongoose)' -slug: Learn/Server-side/Express_Nodejs/mongoose -translation_of: Learn/Server-side/Express_Nodejs/mongoose ---- -<div>{{LearnSidebar}}</div> - -<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}</div> - -<p class="summary">Este artículo introduce brevemente las bases de datos así como su uso en aplicaciones Node/Express. Despues, profundiza en el uso específico de <a href="http://mongoosejs.com/">Mongoose</a> en el proyecto <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary</a>. Explica como se declaran y utilizan los esquemas modelo-objeto, los principales campos de datos y su validación básica. También muestra brevemente algunas de las muchas formas para acceder y modificar los datos.</p> - -<table class="learn-box standard-table"> - <tbody> - <tr> - <th scope="row">Prerequisitos</th> - <td><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial 2: Creando un esqueleto web</a></td> - </tr> - <tr> - <th scope="row">Objetivo:</th> - <td>Ser capaz de crear, diseñar y utilizar bases de datos en Node/Express utilizando Mongoose</td> - </tr> - </tbody> -</table> - -<h2 id="Overview">Overview</h2> - -<p>Library staff will use the Local Library website to store information about books and borrowers, while library members will use it to browse and search for books, find out whether there are any copies available, and then reserve or borrow them. In order to store and retrieve information efficiently, we will store it in a <em>database</em>.</p> - -<p>Las aplicaciones Express pueden utilizar diferentes bases de datos, y existen diferentes aproximaciones que se pueden utilizar para realizar operaciones CRUD (<strong>C</strong>reate, <strong>R</strong>ead, <strong>U</strong>pdate and <strong>D</strong>elete). Este tutorial proporciona una vista general sobre algunas de las opciones disponibles, para a continuación mostrar en detalle los mecanismos elegidos en particular.</p> - -<h3 id="What_databases_can_I_use">What databases can I use?</h3> - -<p><em>Express</em> apps can use any database supported by <em>Node</em> (<em>Express</em> itself doesn't define any specific additional behaviour/requirements for database management). There are <a href="https://expressjs.com/en/guide/database-integration.html">many popular options</a>, including PostgreSQL, MySQL, Redis, SQLite, and MongoDB.</p> - -<p>When choosing a database, you should consider things like time-to-productivity/learning curve, performance, ease of replication/backup, cost, community support, etc. While there is no single "best" database, almost any of the popular solutions should be more than acceptable for a small-to-medium-sized site like our Local Library.</p> - -<p>For more information on the options see: <a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> (Express docs).</p> - -<h3 id="What_is_the_best_way_to_interact_with_a_database">What is the best way to interact with a database?</h3> - -<p>There are two approaches for interacting with a database: </p> - -<ul> - <li>Using the databases' native query language (e.g. SQL)</li> - <li>Using an Object Data Model ("ODM") / Object Relational Model ("ORM"). An ODM/ORM represents the website's data as JavaScript objects, which are then mapped to the underlying database. Some ORMs are tied to a specific database, while others provide a database-agnostic backend.</li> -</ul> - -<p>The very best <em>performance</em> can be gained by using SQL, or whatever query language is supported by the database. ODM's are often slower because they use translation code to map between objects and the database format, which may not use the most efficient database queries (this is particularly true if the ODM supports different database backends, and must make greater compromises in terms of what database features are supported).</p> - -<p>The benefit of using an ORM is that programmers can continue to think in terms of JavaScript objects rather than database semantics — this is particularly true if you need to work with different databases (on either the same or different websites). They also provide an obvious place to perform validation and checking of data.</p> - -<div class="note"> -<p><strong>Tip:</strong> Using ODM/ORMs often results in lower costs for development and maintenance! Unless you're very familiar with the native query language or performance is paramount, you should strongly consider using an ODM.</p> -</div> - -<h3 id="What_ORMODM_should_I_use">What ORM/ODM should I use?</h3> - -<p>There are many ODM/ORM solutions available on the NPM package manager site (check out the <a href="https://www.npmjs.com/browse/keyword/odm">odm</a> and <a href="https://www.npmjs.com/browse/keyword/orm">orm</a> tags for a subset!).</p> - -<p>A few solutions that were popular at the time of writing are:</p> - -<ul> - <li><a href="https://www.npmjs.com/package/mongoose">Mongoose</a>: Mongoose is a <a href="https://www.mongodb.org/">MongoDB</a> object modeling tool designed to work in an asynchronous environment.</li> - <li><a href="https://www.npmjs.com/package/waterline">Waterline</a>: An ORM extracted from the Express-based <a href="http://sailsjs.com/">Sails</a> web framework. It provides a uniform API for accessing numerous different databases, including Redis, MySQL, LDAP, MongoDB, and Postgres.</li> - <li><a href="https://www.npmjs.com/package/bookshelf">Bookshelf</a>: Features both promise-based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. Works with PostgreSQL, MySQL, and SQLite3.</li> - <li><a href="https://www.npmjs.com/package/objection">Objection</a>: Makes it as easy as possible to use the full power of SQL and the underlying database engine (supports SQLite3, Postgres, and MySQL).</li> - <li> - <p><a href="https://www.npmjs.com/package/sequelize">Sequelize</a> is a promise-based ORM for Node.js and io.js. It supports the dialects PostgreSQL, MySQL, MariaDB, SQLite and MSSQL and features solid transaction support, relations, read replication and more.</p> - </li> -</ul> - -<p>As a general rule you should consider both the features provided and the "community activity" (downloads, contributions, bug reports, quality of documentation, etc.) when selecting a solution. At time of writing Mongoose is by far the most popular ORM, and is a reasonable choice if you're using MongoDB for your database.</p> - -<h3 id="Using_Mongoose_and_MongoDb_for_the_LocalLibrary">Using Mongoose and MongoDb for the LocalLibrary</h3> - -<p>For the <em>Local Library</em> example (and the rest of this topic) we're going to use the <a href="https://www.npmjs.com/package/mongoose">Mongoose ODM</a> to access our library data. Mongoose acts as a front end to <a href="https://www.mongodb.com/what-is-mongodb">MongoDB</a>, an open source <a href="https://en.wikipedia.org/wiki/NoSQL">NoSQL</a> database that uses a document-oriented data model. A “collection” of “documents”, in a MongoDB database, <a href="https://docs.mongodb.com/manual/core/databases-and-collections/#collections">is analogous to</a> a “table” of “rows” in a relational database.</p> - -<p>This ODM and database combination is extremely popular in the Node community, partially because the document storage and query system looks very much like JSON, and is hence familiar to JavaScript developers.</p> - -<div class="note"> -<p><strong>Tip:</strong> You don't need to know MongoDB in order to use Mongoose, although parts of the <a href="http://mongoosejs.com/docs/guide.html">Mongoose documentation</a> <em>are</em> easier to use and understand if you are already familiar with MongoDB.</p> -</div> - -<p>The rest of this tutorial shows how to define and access the Mongoose schema and models for the <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary website</a> example.</p> - -<h2 id="Designing_the_LocalLibrary_models">Designing the LocalLibrary models</h2> - -<p>Before you jump in and start coding the models, it's worth taking a few minutes to think about what data we need to store and the relationships between the different objects.</p> - -<p>We know that we need to store information about books (title, summary, author, genre, ISBN) and that we might have multiple copies available (with globally unique ids, availability statuses, etc.). We might need to store more information about the author than just their name, and there might be multiple authors with the same or similar names. We want to be able to sort information based on book title, author, genre, and category.</p> - -<p>When designing your models it makes sense to have separate models for every "object" (group of related information). In this case the obvious objects are books, book instances, and authors.</p> - -<p>You might also want to use models to represent selection-list options (e.g. like a drop down list of choices), rather than hard coding the choices into the website itself — this is recommended when all the options aren't known up front or may change. The obvious candidate for a model of this type is the book genre (e.g. Science Fiction, French Poetry, etc.)</p> - -<p>Once we've decided on our models and fields, we need to think about the relationships between them.</p> - -<p>With that in mind, the UML association diagram below shows the models we'll define in this case (as boxes). As discussed above, we've created models for book (the generic details of the book), book instance (status of specific physical copies of the book available in the system), and author. We have also decided to have a model for genre, so that values can be created dynamically. We've decided not to have a model for the <code>BookInstance:status</code> — we will hard code the acceptable values because we don't expect these to change. Within each of the boxes you can see the model name, the field names and types, and also the methods and their return types.</p> - -<p>The diagram also shows the relationships between the models, including their <em>multiplicities</em>. The multiplicities are the numbers on the diagram showing the numbers (maximum and minimum) of each model that may be present in the relationship. For example, the connecting line between the boxes shows that <code>Book</code> and a <code>Genre</code> are related. The numbers close to the <code>Book</code> model show that a Genre must have zero or more <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">Book</span></font> (as many as you like), while the numbers on the other end of the line next to the <code>Genre</code> show that it can have zero or more associated genre.</p> - -<div class="note"> -<p><strong>Note</strong>: As discussed in our <a href="#related_documents">Mongoose primer</a> below it is often better to have the field that defines the relationship between the documents/models in just <em>one</em> model (you can still find the reverse relationship by searching for the associated <code>_id</code> in the other model). Below we have chosen to define the relationship between Book/Genre and Book/Author in the Book schema, and the relationship between the Book/BookInstance in the BookInstance Schema. This choice was somewhat arbitrary — we could equally well have had the field in the other schema.</p> -</div> - -<p><img alt="Mongoose Library Model with correct cardinality" src="https://mdn.mozillademos.org/files/15645/Library%20Website%20-%20Mongoose_Express.png" style="height: 620px; width: 737px;"></p> - -<div class="note"> -<p><strong>Note</strong>: The next section provides a basic primer explaining how models are defined and used. As you read it, consider how we will construct each of the models in the diagram above.</p> -</div> - -<h2 id="Mongoose_primer">Mongoose primer</h2> - -<p>This section provides an overview of how to connect Mongoose to a MongoDB database, how to define a schema and a model, and how to make basic queries. </p> - -<div class="note"> -<p><strong>Note:</strong> This primer is "heavily influenced" by the <a href="https://www.npmjs.com/package/mongoose">Mongoose quick start</a> on <em>npm</em> and the <a href="http://mongoosejs.com/docs/guide.html">official documentation</a>.</p> -</div> - -<h3 id="Installing_Mongoose_and_MongoDB">Installing Mongoose and MongoDB</h3> - -<p>Mongoose is installed in your project (<strong>package.json</strong>) like any other dependency — using NPM. To install it, use the following command inside your project folder:</p> - -<pre class="brush: bash"><code>npm install mongoose</code> -</pre> - -<p>Installing <em>Mongoose</em> adds all its dependencies, including the MongoDB database driver, but it does not install MongoDB itself. If you want to install a MongoDB server then you can <a href="https://www.mongodb.com/download-center">download installers from here</a> for various operating systems and install it locally. You can also use cloud-based MongoDB instances.</p> - -<div class="note"> -<p><strong>Note:</strong> For this tutorial we'll be using the mLab cloud-based <em>database as a service</em> <a href="https://mlab.com/plans/pricing/">sandbox tier</a> to provide the database. This is suitable for development, and makes sense for the tutorial because it makes "installation" operating system independent (database-as-a-service is also one approach you might well use for your production database).</p> -</div> - -<h3 id="Connecting_to_MongoDB">Connecting to MongoDB</h3> - -<p><em>Mongoose</em> requires a connection to a MongoDB database. You can <code>require()</code> and connect to a locally hosted database with <code>mongoose.connect()</code>, as shown below.</p> - -<pre class="brush: js">//Import the mongoose module -var mongoose = require('mongoose'); - -//Set up default mongoose connection -var mongoDB = 'mongodb://127.0.0.1/my_database'; -mongoose.connect(mongoDB); -// Get Mongoose to use the global promise library -mongoose.Promise = global.Promise; -//Get the default connection -var db = mongoose.connection; - -//Bind connection to error event (to get notification of connection errors) -db.on('error', console.error.bind(console, 'MongoDB connection error:'));</pre> - -<p>You can get the default <code>Connection</code> object with <code>mongoose.connection</code>. Once connected, the open event is fired on the <code>Connection</code> instance.</p> - -<div class="note"> -<p><strong>Tip:</strong> If you need to create additional connections you can use <code>mongoose.createConnection()</code>. This takes the same form of database URI (with host, database, port, options etc.) as <code>connect()</code> and returns a <code>Connection</code> object).</p> -</div> - -<h3 id="Defining_and_creating_models">Defining and creating models</h3> - -<p>Models are <em>defined </em>using the <code>Schema</code> interface. The Schema allows you to define the fields stored in each document along with their validation requirements and default values. In addition, you can define static and instance helper methods to make it easier to work with your data types, and also virtual properties that you can use like any other field, but which aren't actually stored in the database (we'll discuss a bit further below).</p> - -<p>Schemas are then "compiled" into models using the <code>mongoose.model()</code> method. Once you have a model you can use it to find, create, update, and delete objects of the given type.</p> - -<div class="note"> -<p><strong>Note:</strong> Each model maps to a <em>collection</em> of <em>documents</em> in the MongoDB database. The documents will contain the fields/schema types defined in the model <code>Schema</code>.</p> -</div> - -<h4 id="Defining_schemas">Defining schemas</h4> - -<p>The code fragment below shows how you might define a simple schema. First you <code>require()</code> mongoose, then use the Schema constructor to create a new schema instance, defining the various fields inside it in the constructor's object parameter.</p> - -<pre class="brush: js">//Require Mongoose -var mongoose = require('mongoose'); - -//Define a schema -var Schema = mongoose.Schema; - -var SomeModelSchema = new Schema({ - a_string: String, - a_date: Date -}); -</pre> - -<p>In the case above we just have two fields, a string and a date. In the next sections we will show some of the other field types, validation, and other methods.</p> - -<h4 id="Creating_a_model">Creating a model</h4> - -<p>Models are created from schemas using the <code>mongoose.model()</code> method:</p> - -<pre class="brush: js">// Define schema -var Schema = mongoose.Schema; - -var SomeModelSchema = new Schema({ - a_string: String, - a_date: Date -}); - -<strong>// Compile model from schema -var SomeModel = mongoose.model('SomeModel', SomeModelSchema );</strong></pre> - -<p>The first argument is the singular name of the collection that will be created for your model (Mongoose will create the database collection for the above model <em>SomeModel</em> above), and the second argument is the schema you want to use in creating the model.</p> - -<div class="note"> -<p><strong>Note:</strong> Once you've defined your model classes you can use them to create, update, or delete records, and to run queries to get all records or particular subsets of records. We'll show you how to do this in the <a href="#Using_models">Using models</a> section, and when we create our views.</p> -</div> - -<h4 id="Tipos_de_esquema_(campos)">Tipos de esquema (campos)</h4> - -<p>Un esquema puede tener un número de campos arbitrario — cada uno representa un campo en los documentos almacenados en <em>MongoDB</em>. A continuación se muestra un ejemplo de esquema con varios de los tipos de campos más comunes y cómo se declaran.</p> - -<pre class="brush: js">var schema = new Schema( -{ - name: <strong>String</strong>, - binary: <strong>Buffer</strong>, - living: <strong>Boolean</strong>, - updated: { type: <strong>Date</strong>, default: Date.now }, - age: { type: <strong>Number</strong>, min: 18, max: 65, required: true }, - mixed: <strong>Schema.Types.Mixed</strong>, - _someId: <strong>Schema.Types.ObjectId</strong>, - array: <strong>[]</strong>, - ofString: [<strong>String</strong>], // You can also have an array of each of the other types too. - nested: { stuff: { type: <strong>String</strong>, lowercase: true, trim: true } } -})</pre> - -<p>Most of the <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> (the descriptors after “type:” or after field names) are self explanatory. The exceptions are:</p> - -<ul> - <li><code>ObjectId</code>: Represents specific instances of a model in the database. For example, a book might use this to represent its author object. This will actually contain the unique ID (<code>_id</code>) for the specified object. We can use the <code>populate()</code> method to pull in the associated information when needed.</li> - <li><a href="http://mongoosejs.com/docs/schematypes.html#mixed">Mixed</a>: An arbitrary schema type.</li> - <li><font face="Consolas, Liberation Mono, Courier, monospace">[]</font>: An array of items. You can perform JavaScript array operations on these models (push, pop, unshift, etc.). The examples above show an array of objects without a specified type and an array of <code>String</code> objects, but you can have an array of any type of object.</li> -</ul> - -<p>The code also shows both ways of declaring a field:</p> - -<ul> - <li>Field <em>name</em> and <em>type</em> as a key-value pair (i.e. as done with fields <code>name</code>, <code>binary </code>and <code>living</code>).</li> - <li>Field <em>name</em> followed by an object defining the <code>type</code>, and any other <em>options</em> for the field. Options include things like: - <ul> - <li>default values.</li> - <li>built-in validators (e.g. max/min values) and custom validation functions.</li> - <li>Whether the field is required</li> - <li>Whether <code>String</code> fields should automatically be set to lowercase, uppercase, or trimmed (e.g. <code>{ type: <strong>String</strong>, lowercase: true, trim: true }</code>)</li> - </ul> - </li> -</ul> - -<p>For more information about options see <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> (Mongoose docs).</p> - -<h4 id="Validation">Validation</h4> - -<p>Mongoose provides built-in and custom validators, and synchronous and asynchronous validators. It allows you to specify both the acceptable range or values and the error message for validation failure in all cases.</p> - -<p>The built-in validators include:</p> - -<ul> - <li>All <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> have the built-in <a href="http://mongoosejs.com/docs/api.html#schematype_SchemaType-required">required</a> validator. This is used to specify whether the field must be supplied in order to save a document.</li> - <li><a href="http://mongoosejs.com/docs/api.html#schema-number-js">Numbers</a> have <a href="http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-min">min</a> and <a href="http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-max">max</a> validators.</li> - <li><a href="http://mongoosejs.com/docs/api.html#schema-string-js">Strings</a> have: - <ul> - <li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-enum">enum</a>: specifies the set of allowed values for the field.</li> - <li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-match">match</a>: specifies a regular expression that the string must match.</li> - <li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-maxlength">maxlength</a> and <a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-minlength">minlength</a> for the string.</li> - </ul> - </li> -</ul> - -<p>The example below (slightly modified from the Mongoose documents) shows how you can specify some of the validator types and error messages:</p> - -<pre class="brush: js"><code> - var breakfastSchema = new Schema({ - eggs: { - type: Number, - min: [6, 'Too few eggs'], - max: 12, - required: [true, 'Why no eggs?'] - }, - drink: { - type: String, - enum: ['Coffee', 'Tea', 'Water',] - } - }); -</code></pre> - -<p>For complete information on field validation see <a href="http://mongoosejs.com/docs/validation.html">Validation</a> (Mongoose docs).</p> - -<h4 id="Virtual_properties">Virtual properties</h4> - -<p>Virtual properties are document properties that you can get and set but that do not get persisted to MongoDB. The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage. The example in the documentation constructs (and deconstructs) a full name virtual property from a first and last name field, which is easier and cleaner than constructing a full name every time one is used in a template.</p> - -<div class="note"> -<p><strong>Note:</strong> We will use a virtual property in the library to define a unique URL for each model record using a path and the record's <code>_id</code> value.</p> -</div> - -<p>For more information see <a href="http://mongoosejs.com/docs/guide.html#virtuals">Virtuals</a> (Mongoose documentation).</p> - -<h4 id="Methods_and_query_helpers">Methods and query helpers</h4> - -<p>A schema can also have <a href="http://mongoosejs.com/docs/guide.html#methods">instance methods</a>, <a href="http://mongoosejs.com/docs/guide.html#statics">static methods</a>, and <a href="http://mongoosejs.com/docs/guide.html#query-helpers">query helpers</a>. The instance and static methods are similar, but with the obvious difference that an instance method is associated with a particular record and has access to the current object. Query helpers allow you to extend mongoose's <a href="http://mongoosejs.com/docs/queries.html">chainable query builder API</a> (for example, allowing you to add a query "byName" in addition to the <code>find()</code>, <code>findOne()</code> and <code>findById()</code> methods).</p> - -<h3 id="Using_models">Using models</h3> - -<p>Once you've created a schema you can use it to create models. The model represents a collection of documents in the database that you can search, while the model's instances represent individual documents that you can save and retrieve.</p> - -<p>We provide a brief overview below. For more information see: <a href="http://mongoosejs.com/docs/models.html">Models</a> (Mongoose docs).</p> - -<h4 id="Creating_and_modifying_documents">Creating and modifying documents</h4> - -<p>To create a record you can define an instance of the model and then call <code>save()</code>. The examples below assume SomeModel is a model (with a single field "name") that we have created from our schema.</p> - -<pre class="brush: js"><code>// Create an instance of model SomeModel -var awesome_instance = new </code>SomeModel<code>({ name: 'awesome' }); - -// Save the new model instance, passing a callback -awesome_instance.save(function (err) { - if (err) return handleError(err); - // saved! -}); -</code></pre> - -<p>Creation of records (along with updates, deletes, and queries) are asynchronous operations — you supply a callback that is called when the operation completes. The API uses the error-first argument convention, so the first argument for the callback will always be an error value (or null). If the API returns some result, this will be provided as the second argument.</p> - -<p>You can also use <code>create()</code> to define the model instance at the same time as you save it. The callback will return an error for the first argument and the newly-created model instance for the second argument.</p> - -<pre class="brush: js">SomeModel<code>.create({ name: 'also_awesome' }, function (err, awesome_instance) { - if (err) return handleError(err); - // saved! -});</code></pre> - -<p>Every model has an associated connection (this will be the default connection when you use <code>mongoose.model()</code>). You create a new connection and call <code>.model()</code> on it to create the documents on a different database.</p> - -<p>You can access the fields in this new record using the dot syntax, and change the values. You have to call <code>save()</code> or <code>update()</code> to store modified values back to the database.</p> - -<pre class="brush: js">// Access model field values using dot notation -console.log(<code>awesome_instance.name</code>); //should log '<code>also_awesome</code>' - -// Change record by modifying the fields, then calling save(). -<code>awesome_instance</code>.name="New cool name"; -<code>awesome_instance.save(function (err) { - if (err) return handleError(err); // saved! - });</code> -</pre> - -<h4 id="Searching_for_records">Searching for records</h4> - -<p>You can search for records using query methods, specifying the query conditions as a JSON document. The code fragment below shows how you might find all athletes in a database that play tennis, returning just the fields for athlete <em>name</em> and <em>age</em>. Here we just specify one matching field (sport) but you can add more criteria, specify regular expression criteria, or remove the conditions altogether to return all athletes.</p> - -<pre class="brush: js"><code>var Athlete = mongoose.model('Athlete', yourSchema); - -// find all athletes who play tennis, selecting the 'name' and 'age' fields -Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) { - if (err) return handleError(err); - // 'athletes' contains the list of athletes that match the criteria. -})</code></pre> - -<p>If you specify a callback, as shown above, the query will execute immediately. The callback will be invoked when the search completes.</p> - -<div class="note"> -<p><strong>Note:</strong> All callbacks in Mongoose use the pattern <code>callback(error, result)</code>. If an error occurs executing the query, the <code>error</code> parameter will contain an error document, and <code>result</code> will be null. If the query is successful, the <code>error</code> parameter will be null, and the <code>result</code> will be populated with the results of the query.</p> -</div> - -<p>If you don't specify a callback then the API will return a variable of type <a href="http://mongoosejs.com/docs/api.html#query-js">Query</a>. You can use this query object to build up your query and then execute it (with a callback) later using the <code>exec()</code> method.</p> - -<pre class="brush: js"><code>// find all athletes that play tennis -var query = Athlete.find({ 'sport': 'Tennis' }); - -// selecting the 'name' and 'age' fields -query.select('name age'); - -// limit our results to 5 items -query.limit(5); - -// sort by age -query.sort({ age: -1 }); - -// execute the query at a later time -query.exec(function (err, athletes) { - if (err) return handleError(err); - // athletes contains an ordered list of 5 athletes who play Tennis -})</code></pre> - -<p>Above we've defined the query conditions in the <code>find()</code> method. We can also do this using a <code>where()</code> function, and we can chain all the parts of our query together using the dot operator (.) rather than adding them separately. The code fragment below is the same as our query above, with an additional condition for the age.</p> - -<pre><code>Athlete. - find(). - where('sport').equals('Tennis'). - where('age').gt(17).lt(50). //Additional where query - limit(5). - sort({ age: -1 }). - select('name age'). - exec(callback); // where callback is the name of our callback function.</code></pre> - -<p>The <a href="http://mongoosejs.com/docs/api.html#query_Query-find">find()</a> method gets all matching records, but often you just want to get one match. The following methods query for a single record:</p> - -<ul> - <li><code><a href="http://mongoosejs.com/docs/api.html#model_Model.findById">findById()</a></code>: Finds the document with the specified <code>id</code> (every document has a unique <code>id</code>).</li> - <li><code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOne">findOne()</a></code>: Finds a single document that matches the specified criteria.</li> - <li><code><a href="http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove">findByIdAndRemove()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate">findByIdAndUpdate()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOneAndRemove">findOneAndRemove()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate">findOneAndUpdate()</a></code>: Finds a single document by <code>id</code> or criteria and either update or remove it. These are useful convenience functions for updating and removing records.</li> -</ul> - -<div class="note"> -<p><strong>Note:</strong> There is also a <code><a href="http://mongoosejs.com/docs/api.html#model_Model.count">count()</a></code> method that you can use to get the number of items that match conditions. This is useful if you want to perform a count without actually fetching the records.</p> -</div> - -<p>There is a lot more you can do with queries. For more information see: <a href="http://mongoosejs.com/docs/queries.html">Queries</a> (Mongoose docs).</p> - -<h4 id="Working_with_related_documents_—_population">Working with related documents — population</h4> - -<p>You can create references from one document/model instance to another using the <code>ObjectId</code> schema field, or from one document to many using an array of <code>ObjectIds</code>. The field stores the id of the related model. If you need the actual content of the associated document, you can use the <code><a href="http://mongoosejs.com/docs/api.html#query_Query-populate">populate()</a></code> method in a query to replace the id with the actual data.</p> - -<p>For example, the following schema defines authors and stories. Each author can have multiple stories, which we represent as an array of <code>ObjectId</code>. Each story can have a single author. The "ref" (highlighted in bold below) tells the schema which model can be assigned to this field.</p> - -<pre class="brush: js"><code>var mongoose = require('mongoose') - , Schema = mongoose.Schema - -var authorSchema = Schema({ - name : String, - stories : [{ type: Schema.Types.ObjectId, <strong>ref</strong>: 'Story' }] -}); - -var storySchema = Schema({ - author : { type: Schema.Types.ObjectId, <strong>ref</strong>: 'Author' }, - title : String -}); - -var Story = mongoose.model('Story', storySchema); -var Author = mongoose.model('Author', authorSchema);</code></pre> - -<p>We can save our references to the related document by assigning the <code>_id</code> value. Below we create an author, then a story, and assign the author id to our stories author field.</p> - -<pre class="brush: js"><code>var bob = new Author({ name: 'Bob Smith' }); - -bob.save(function (err) { - if (err) return handleError(err); - - //Bob now exists, so lets create a story - var story = new Story({ - title: "Bob goes sledding", - author: bob._id // assign the _id from the our author Bob. This ID is created by default! - }); - - story.save(function (err) { - if (err) return handleError(err); - // Bob now has his story - }); -});</code></pre> - -<p>Our story document now has an author referenced by the author document's ID. In order to get the author information in our story results we use <code>populate()</code>, as shown below.</p> - -<pre class="brush: js"><code>Story -.findOne({ title: 'Bob goes sledding' }) -.populate('author') //This populates the author id with actual author information! -.exec(function (err, story) { - if (err) return handleError(err); - console.log('The author is %s', story.author.name); - // prints "The author is Bob Smith" -});</code></pre> - -<div class="note"> -<p><strong>Note:</strong> Astute readers will have noted that we added an author to our story, but we didn't do anything to add our story to our author's <code>stories</code> array. How then can we get all stories by a particular author? One way would be to add our author to the stories array, but this would result in us having two places where the information relating authors and stories needs to be maintained.</p> - -<p>A better way is to get the <code>_id</code> of our <em>author</em>, then use <code>find()</code> to search for this in the author field across all stories.</p> - -<pre class="brush: js"><code>Story -.find({ author : bob._id }) -.exec(function (err, stories) { - if (err) return handleError(err); - // returns all stories that have Bob's id as their author. -});</code> -</pre> -</div> - -<p>This is almost everything you need to know about working with related items<em> for this tutorial</em>. For more detailed information see <a href="http://mongoosejs.com/docs/populate.html">Population</a> (Mongoose docs).</p> - -<h3 id="One_schemamodel_per_file">One schema/model per file</h3> - -<p>While you can create schemas and models using any file structure you like, we highly recommend defining each model schema in its own module (file), exporting the method to create the model. This is shown below:</p> - -<pre class="brush: js"><code>// File: ./models/somemodel.js - -//Require Mongoose -var mongoose = require('mongoose'); - -//Define a schema -var Schema = mongoose.Schema; - -var SomeModelSchema = new Schema({ - a_string : String, - a_date : Date, -}); - -<strong>//Export function to create "SomeModel" model class -module.exports = mongoose.model('SomeModel', SomeModelSchema );</strong></code></pre> - -<p>You can then require and use the model immediately in other files. Below we show how you might use it to get all instances of the model.</p> - -<pre class="brush: js"><code>//Create a SomeModel model just by requiring the module -var SomeModel = require('../models/somemodel') - -// Use the SomeModel object (model) to find all SomeModel records -SomeModel.find(callback_function);</code></pre> - -<h2 id="Setting_up_the_MongoDB_database">Setting up the MongoDB database</h2> - -<p>Now that we understand something of what Mongoose can do and how we want to design our models, it's time to start work on the <em>LocalLibrary</em> website. The very first thing we want to do is set up a MongoDb database that we can use to store our library data.</p> - -<p>For this tutorial we're going to use <a href="https://mlab.com/welcome/">mLab</a>'s free cloud-hosted "<a href="https://mlab.com/plans/pricing/">sandbox</a>" database. This database tier is not considered suitable for production websites because it has no redundancy, but it is great for development and prototyping. We're using it here because it is free and easy to set up, and because mLab is a popular <em>database as a service</em> vendor that you might reasonably choose for your production database (other popular choices at the time of writing include <a href="https://www.compose.com/">Compose</a>, <a href="https://scalegrid.io/pricing.html">ScaleGrid</a> and <a href="https://www.mongodb.com/cloud/atlas">MongoDB Atlas</a>).</p> - -<div class="note"> -<p><strong>Note:</strong> If you prefer you can set up a MongoDb database locally by downloading and installing the <a href="https://www.mongodb.com/download-center">appropriate binaries for your system</a>. The rest of the instructions in this article would be similar, except for the database URL you would specify when connecting.</p> -</div> - -<p>You will first need to <a href="https://mlab.com/signup/">create an account</a> with mLab (this is free, and just requires that you enter basic contact details and acknowledge their terms of service). </p> - -<p>After logging in, you'll be taken to the <a href="https://mlab.com/home">home</a> screen:</p> - -<ol> - <li>Click <strong>Create New</strong> in the <em>MongoDB Deployments</em> section.<img alt="" src="https://mdn.mozillademos.org/files/14446/mLabCreateNewDeployment.png" style="height: 415px; width: 1000px;"></li> - <li>This will open the <em>Cloud Provider Selection </em>screen.<br> - <img alt="MLab - screen for new deployment" src="https://mdn.mozillademos.org/files/15661/mLab_new_deployment_form_v2.png" style="height: 931px; width: 1297px;"><br> - - <ul> - <li>Select the SANDBOX (Free) plan from the Plan Type section. </li> - <li>Select any provider from the <em>Cloud Provider </em>section. Different providers offer different regions (displayed below the selected plan type).</li> - <li>Click the <strong>Continue</strong> button.</li> - </ul> - </li> - <li>This will open the <em>Select Region</em> screen. - <p><img alt="Select new region screen" src="https://mdn.mozillademos.org/files/15662/mLab_new_deployment_select_region_v2.png" style="height: 570px; width: 1293px;"></p> - - <ul> - <li> - <p>Select the region closest to you and then <strong>Continue</strong>.</p> - </li> - </ul> - </li> - <li> - <p>This will open the <em>Final Details</em> screen.<br> - <img alt="New deployment database name" src="https://mdn.mozillademos.org/files/15663/mLab_new_deployment_final_details.png" style="height: 569px; width: 1293px;"></p> - - <ul> - <li> - <p>Enter the name for the new database as <code>local_library</code> and then select <strong>Continue</strong>.</p> - </li> - </ul> - </li> - <li> - <p>This will open the <em>Order Confirmation</em> screen.<br> - <img alt="Order confirmation screen" src="https://mdn.mozillademos.org/files/15664/mLab_new_deployment_order_confirmation.png" style="height: 687px; width: 1290px;"></p> - - <ul> - <li> - <p>Click <strong>Submit Order</strong> to create the database.</p> - </li> - </ul> - </li> - <li> - <p>You will be returned to the home screen. Click on the new database you just created to open its details screen. As you can see the database has no collections (data).<br> - <img alt="mLab - Database details screen" src="https://mdn.mozillademos.org/files/15665/mLab_new_deployment_database_details.png" style="height: 700px; width: 1398px;"><br> - <br> - The URL that you need to use to access your database is displayed on the form above (shown for this database circled above). In order to use this you need to create a database user that you can specify in the URL.</p> - </li> - <li>Click the <strong>Users</strong> tab and select the <strong>Add database user</strong> button.</li> - <li>Enter a username and password (twice), and then press <strong>Create</strong>. Do not select <em>Make read only</em>.<br> - <img alt="" src="https://mdn.mozillademos.org/files/14454/mLab_database_users.png" style="height: 204px; width: 600px;"></li> -</ol> - -<p>You have now created the database, and have an URL (with username and password) that can be used to access it. This will look something like: <code>mongodb://your_user_namer:your_password@ds119748.mlab.com:19748/local_library</code>.</p> - -<h2 id="Install_Mongoose">Install Mongoose</h2> - -<p>Open a command prompt and navigate to the directory where you created your <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">skeleton Local Library website</a>. Enter the following command to install Mongoose (and its dependencies) and add it to your <strong>package.json</strong> file, unless you have already done so when reading the <a href="#Installing_Mongoose_and_MongoDB">Mongoose Primer</a> above.</p> - -<pre class="brush: bash">npm install mongoose -</pre> - -<h2 id="Connect_to_MongoDB">Connect to MongoDB</h2> - -<p>Open <strong>/app.js</strong> (in the root of your project) and copy the following text below where you declare the <em>Express application object</em> (after the line <code>var app = express();</code>). Replace the database url string ('<em>insert_your_database_url_here</em>') with the location URL representing your own database (i.e. using the information <a href="#Setting_up_the_MongoDB_database">from mLab</a>).</p> - -<pre class="brush: js">//Set up mongoose connection -var mongoose = require('mongoose'); -var mongoDB = '<em>insert_your_database_url_here</em>'; -mongoose.connect(mongoDB); -mongoose.Promise = global.Promise; -var db = mongoose.connection; -db.on('error', console.error.bind(console, 'MongoDB connection error:'));</pre> - -<p>As discussed <a href="#Connecting_to_MongoDB">in the Mongoose primer above</a>, this code creates the default connection to the database and binds to the error event (so that errors will be printed to the console). </p> - -<h2 id="Defining_the_LocalLibrary_Schema">Defining the LocalLibrary Schema</h2> - -<p>We will define a separate module for each model, as <a href="#One_schemamodel_per_file">discussed above</a>. Start by creating a folder for our models in the project root (<strong>/models</strong>) and then create separate files for each of the models:</p> - -<pre>/express-locallibrary-tutorial //the project root - <strong>/models</strong> - <strong>author.js</strong> - <strong>book.js</strong> - <strong>bookinstance.js</strong> - <strong>genre.js</strong> -</pre> - -<h3 id="Author_model">Author model</h3> - -<p>Copy the <code>Author</code> schema code shown below and paste it into your <strong>./models/author.js</strong> file. The scheme defines an author has having <code>String</code> SchemaTypes for the first and family names, that are required and have a maximum of 100 characters, and <code>Date</code> fields for the date of birth and death.</p> - -<pre class="brush: js">var mongoose = require('mongoose'); - -var Schema = mongoose.Schema; - -var AuthorSchema = new Schema( - { - first_name: {type: String, required: true, max: 100}, - family_name: {type: String, required: true, max: 100}, - date_of_birth: {type: Date}, - date_of_death: {type: Date}, - } -); - -<strong>// Virtual for author's full name -AuthorSchema -.virtual('name') -.get(function () { - return this.family_name + ', ' + this.first_name; -}); - -// Virtual for author's lifespan -AuthorSchema -</strong>.virtual('lifespan') -.get(function () { - return (this.date_of_death.getYear() - this.date_of_birth.getYear()).toString(); -}); - -// Virtual for author's URL -AuthorSchema -.virtual('url') -.get(function () { - return '/catalog/author/' + this._id; -}); - -//Export model -module.exports = mongoose.model('Author', AuthorSchema); - -</pre> - -<p>We've also declared a <a href="#Virtual_properties">virtual</a> for the AuthorSchema named "url" that returns the absolute URL required to get a particular instance of the model — we'll use the property in our templates whenever we need to get a link to a particular author.</p> - -<div class="note"> -<p><strong>Note:</strong> Declaring our URLs as a virtual in the schema is a good idea because then the URL for an item only ever needs to be changed in one place.<br> - At this point, a link using this URL wouldn't work, because we haven't got any routes handling code for individual model instances. We'll set those up in a later article!</p> -</div> - -<p>At the end of the module we export the model.</p> - -<h3 id="Book_model">Book model</h3> - -<p>Copy the <code>Book</code> schema code shown below and paste it into your <strong>./models/book.js</strong> file. Most of this is similar to the author model — we've declared a schema with a number of string fields and a virtual for getting the URL of specific book records, and we've exported the model.</p> - -<pre class="brush: js">var mongoose = require('mongoose'); - -var Schema = mongoose.Schema; - -var BookSchema = new Schema( - { - title: {type: String, required: true}, - <strong> author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},</strong> - summary: {type: String, required: true}, - isbn: {type: String, required: true}, - <strong> genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]</strong> - } -); - -// Virtual for book's URL -BookSchema -.virtual('url') -.get(function () { - return '/catalog/book/' + this._id; -}); - -//Export model -module.exports = mongoose.model('Book', BookSchema); -</pre> - -<p>The main difference here is that we've created two references to other models:</p> - -<ul> - <li>author is a reference to a single <code>Author</code> model object, and is required.</li> - <li>genre is a reference to an array of <code>Genre</code> model objects. We haven't declared this object yet!</li> -</ul> - -<h3 id="BookInstance_model">BookInstance model</h3> - -<p>Finally, copy the <code>BookInstance</code> schema code shown below and paste it into your <strong>./models/bookinstance.js</strong> file. The <code>BookInstance</code> represents a specific copy of a book that someone might borrow, and includes information about whether the copy is available or on what date it is expected back, "imprint" or version details.</p> - -<pre class="brush: js">var mongoose = require('mongoose'); - -var Schema = mongoose.Schema; - -var BookInstanceSchema = new Schema( - { - book: { type: Schema.Types.ObjectId, ref: 'Book', required: true }, //reference to the associated book - imprint: {type: String, required: true}, - status: {type: String, required: true, <strong>enum: ['Available', 'Maintenance', 'Loaned', 'Reserved']</strong>, <strong>default: 'Maintenance'</strong>}, - due_back: {type: Date, <strong>default: Date.now</strong>} - } -); - -// Virtual for bookinstance's URL -BookInstanceSchema -.virtual('url') -.get(function () { - return '/catalog/bookinstance/' + this._id; -}); - -//Export model -module.exports = mongoose.model('BookInstance', BookInstanceSchema);</pre> - -<p>The new things we show here are the field options:</p> - -<ul> - <li><code>enum</code>: This allows us to set the allowed values of a string. In this case, we use it to specify the availability status of our books (using an enum means that we can prevent mis-spellings and arbitrary values for our status)</li> - <li><code>default</code>: We use default to set the default status for newly created bookinstances to maintenance and the default <code>due_back</code> date to <code>now</code> (note how you can call the Date function when setting the date!)</li> -</ul> - -<p>Everything else should be familiar from our previous schema.</p> - -<h3 id="Genre_model_-_challenge!">Genre model - challenge!</h3> - -<p>Open your <strong>./models/genre.js</strong> file and create a schema for storing genres (the category of book, e.g. whether it is fiction or non-fiction, romance or military history, etc).</p> - -<p>The definition will be very similar to the other models:</p> - -<ul> - <li>The model should have a <code>String</code> SchemaType called <code>name</code> to describe the genre.</li> - <li>This name should be required and have between 3 and 100 characters.</li> - <li>Declare a <a href="#Virtual_properties">virtual</a> for the genre's URL, named <code>url</code>.</li> - <li>Export the model.</li> -</ul> - -<h2 id="Testing_—_create_some_items">Testing — create some items</h2> - -<p>That's it. We now have all models for the site set up!</p> - -<p>In order to test the models (and to create some example books and other items that we can use in our next articles) we'll now run an <em>independent</em> script to create items of each type:</p> - -<ol> - <li>Download (or otherwise create) the file <a href="https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js">populatedb.js</a> inside your <em>express-locallibrary-tutorial</em> directory (in the same level as <code>package.json</code>). - - <div class="note"> - <p><strong>Note:</strong> You don't need to know how <a href="https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js">populatedb.js</a> works; it just adds sample data into the database.</p> - </div> - </li> - <li>Enter the following commands in the project root to install the <em>async</em> module that is required by the script (we'll discuss this in later tutorials, ) - <pre class="brush: bash">npm install async</pre> - </li> - <li>Run the script using node in your command prompt, passing in the URL of your <em>MongoDB</em> database (the same one you replaced the <em>insert_your_database_url_here </em>placeholder with, inside <code>app.js</code> earlier): - <pre class="brush: bash">node populatedb <your mongodb url></pre> - </li> - <li>The script should run through to completion, displaying items as it creates them in the terminal.</li> -</ol> - -<div class="note"> -<p><strong>Tip:</strong> Go to your database on <a href="https://mlab.com/home">mLab</a>. You should now be able to drill down into individual collections of Books, Authors, Genres and BookInstances, and check out individual documents.</p> -</div> - -<h2 id="Summary">Summary</h2> - -<p>In this article, we've learned a bit about databases and ORMs on Node/Express, and a lot about how Mongoose schema and models are defined. We then used this information to design and implement <code>Book</code>, <code>BookInstance</code>, <code>Author</code> and <code>Genre</code> models for the <em>LocalLibrary</em> website.</p> - -<p>Last of all we tested our models by creating a number of instances (using a standalone script). In the next article we'll look at creating some pages to display these objects.</p> - -<h2 id="See_also">See also</h2> - -<ul> - <li><a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> (Express docs)</li> - <li><a href="http://mongoosejs.com/">Mongoose website</a> (Mongoose docs)</li> - <li><a href="http://mongoosejs.com/docs/guide.html">Mongoose Guide</a> (Mongoose docs)</li> - <li><a href="http://mongoosejs.com/docs/validation.html">Validation</a> (Mongoose docs)</li> - <li><a href="http://mongoosejs.com/docs/schematypes.html">Schema Types</a> (Mongoose docs)</li> - <li><a href="http://mongoosejs.com/docs/models.html">Models</a> (Mongoose docs)</li> - <li><a href="http://mongoosejs.com/docs/queries.html">Queries</a> (Mongoose docs)</li> - <li><a href="http://mongoosejs.com/docs/populate.html">Population</a> (Mongoose docs)</li> -</ul> - -<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}</p> - -<p> </p> - -<h2 id="In_this_module">In this module</h2> - -<ul> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> - <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> -</ul> - -<p> </p> diff --git a/files/es/learn/tools_and_testing/client-side_javascript_frameworks/vue_getting_started/index.html b/files/es/learn/tools_and_testing/client-side_javascript_frameworks/vue_getting_started/index.html deleted file mode 100644 index 3acb3d4039..0000000000 --- a/files/es/learn/tools_and_testing/client-side_javascript_frameworks/vue_getting_started/index.html +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Primeros pasos con Vue -slug: Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started -translation_of: Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started -original_slug: >- - Learn/Herramientas_y_pruebas/Lado-del-cliente_JavaScript_frameworks/Vue_primeros_pasos ---- -<div>{{LearnSidebar}}</div> - -<div>{{PreviousMenuNext("Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources","Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component", "Learn/Tools_and_testing/Client-side_JavaScript_frameworks")}}</div> - -<p class="summary">Ahora vamos a introducir Vue, el tercero de nuestros frameworks. En este articulo vamos a ver un poco del background de Vue, aprenderemos cómo instalarlo y a crear un nuevo proyecto, estudiar la estructura de alto nivel de todo el proyecto y un componente individual, sabremos como correr el proyecto localmente, y tenerlo preparado para empezar a construir nuestro ejemplo.</p> - -<table class="learn-box standard-table"> - <tbody> - <tr> - <th scope="row">Pre-requisitos:</th> - <td> - <p>Familiaridad con los motores de los lenguajes <a href="/en-US/docs/Learn/HTML">HTML</a>, <a href="/en-US/docs/Learn/CSS">CSS</a>, y <a href="/en-US/docs/Learn/JavaScript">JavaScript</a> languages, conocimiento del <a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line">terminal/command line</a>.</p> - - <p>Los componentes Vue son escritos como una combinacion de objectos Javascript que administran los datos de la app y una sintaxis de plantilla basada en HTML que se enlaza con la estructura DOM subyacente. Para la instalación, y para usar algunas de las caracteristicas mas avanzadas de Vue (como Componentes de archivos simples o renderizado de funciones), vas a necesitar un terminar con node + npm instalados.</p> - </td> - </tr> - <tr> - <th scope="row">Objetivo:</th> - <td>Configurar un entorno de desarrollo local de Vue, crear una app de inicio y entender los principios de su funcionamiento.</td> - </tr> - </tbody> -</table> - -<h2 id="Un_Vue_más_claro">Un Vue más claro</h2> - -<p>Vue es un framework moderno de Javascript que proveé facilidades muy utiles para el mejoramiento progresivo- al contrario de otros frameworks, puedes usar Vue para mejorar un HTML exstente. Esto permite usar Vue como un remplazo agregado para una libreria como <a href="/en-US/docs/Glossary/jQuery">JQuery</a>.</p> - -<p>Habiendo dicho esto, tambien puedes usar Vue para escribir completamente una aplicación de una sola página(SPAs).This allows you to create markup managed entirely by Vue, which can improve developer experience and performance when dealing with complex applications. It also allows you to take advantage of libraries for client-side routing and state management when you need to. Additionally, Vue takes a "middle ground" approach to tooling like client-side routing and state management. While the Vue core team maintains suggested libraries for these functions, they are not directly bundled into Vue. This allows you to select a different routing/state management library if they better fit your application.</p> - -<p>In addition to allowing you to progressively integrate Vue into your applications, Vue also provides a progressive approach to writing markup. Like most frameworks, Vue lets you create reusable blocks of markup via components. Most of the time, Vue components are written using a special HTML template syntax. When you need more control than the HTML syntax allows, you can write JSX or plain JavaScript functions to define your components.</p> - -<p>As you work through this tutorial, you might want to keep the <a href="https://vuejs.org/v2/guide/">Vue guide</a> and <a href="https://vuejs.org/v2/api/">API documentation</a> open in other tabs, so you can refer to them if you want more information on any sub topic.<br> - For a good (but potentially biased) comparison between Vue and many of the other frameworks, see <a href="https://vuejs.org/v2/guide/comparison.html">Vue Docs: Comparison with Other Frameworks</a>.</p> - -<h2 id="Installation">Installation</h2> - -<p>To use Vue in an existing site, you can drop one of the following <code><a href="/en-US/docs/Web/HTML/Element/script"><script></a></code> elements onto a page. This allows you to start using Vue on existing sites, which is why Vue prides itself on being a progressive framework. This is a great option when migrating an existing project using a library like JQuery to Vue. With this method, you can use a lot of the core features of Vue, such as the attributes, custom components, and data-management.</p> - -<ul> - <li> - <p>Development Script (Unoptimized, but includes console warnings. Great for development</p> - - <pre class="brush: html notranslate"><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></pre> - </li> - <li> - <p>Production Script (Optimized version, minimal console warnings. It is recommended that you specify a version number when including Vue on your site so that any framework updates do not break your live site without you knowing.)</p> - - <pre class="brush: html notranslate"><script src="https://cdn.jsdelivr.net/npm/vue@2"></script></pre> - </li> -</ul> - -<p>However, this approach has some limitations. To build more complex apps, you’ll want to use the <a href="https://www.npmjs.com/package/vue">Vue NPM package</a>. This will let you use advanced features of Vue and take advantage of bundlers like WebPack. To make building apps with Vue easier, there is a CLI to streamline the development process. To use the npm package & the CLI you will need:</p> - -<ol> - <li>Node.js 8.11+ installed.</li> - <li>npm or yarn.</li> -</ol> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: If you don't have the above installed, find out <a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line#Adding_powerups">more about installing npm and Node.js</a> here.</p> -</div> - -<p>To install the CLI, run the following command in your terminal:</p> - -<pre class="brush: bash notranslate">npm install --global @vue/cli</pre> - -<p>Or if you'd prefer to use yarn:</p> - -<pre class="brush: bash notranslate">yarn global add @vue/cli</pre> - -<p>Once installed, to initialize a new project you can then open a terminal in the directory you want to create the project in, and run <code>vue create <project-name></code>. The CLI will then give you a list of project configurations you can use. There are a few preset ones, and you can make your own. These options let you configure things like TypeScript, linting, vue-router, testing, and more.</p> - -<p>We’ll look at using this below.</p> - -<h2 id="Initializing_a_new_project">Initializing a new project</h2> - -<p>To explore various features of Vue, we will be building up a sample todo list app. We'll begin by using the Vue CLI to create a new app framework to build our app into. Follow the steps below:</p> - -<ol> - <li>In terminal, <code>cd</code> to where you'd like to create your sample app, then run <code>vue create moz-todo-vue</code>.</li> - <li>Use the arrow keys and <kbd>Enter</kbd> to select the "Manually select features" option.</li> - <li>The first menu you’ll be presented with allows you to choose which features you want to include in your project. Make sure that "Babel" and "Linter / Formatter" are selected. If they are not, use the arrow keys and the space bar to toggle them on. Once they are selected, press <kbd>Enter</kbd> to proceed.</li> - <li>Next you’ll select a config for the linter / formatter. Navigate to "Eslint with error prevention only" and hit <kbd>Enter</kbd> again. This will help us catch common errors, but not be overly opinionated.</li> - <li>Next you are asked to configure what kind of automated linting we want. Select "Lint on save". This will check for errors when we save a file inside the project. Hit <kbd>Enter</kbd> to continue.</li> - <li>Now, you will select how we want your config files to be managed. "In dedicated config files" will put your config settings for things like ESLint into their own, dedicated files. The other option, "In package.json", will put all of your config settings into the app's <code>package.json</code> file. Select "In dedicated config files" and push <kbd>Enter</kbd>.</li> - <li>Finally, you are asked if you want to save this as a preset for future options. This is entirely up to you. If you like these settings over the existing presets and want to use them again, type <kbd>y</kbd> , otherwise type <kbd>n</kbd>.</li> -</ol> - -<p>The CLI will now begin scaffolding out your project, and installing all of your dependencies.</p> - -<p>If you've never run the Vue CLI before, you'll get one more question — you'll be asked to choose a package manager. You can use the arrow keys to select which one you prefer. The Vue CLI will default to this package manager from now on. If you need to use a different package manager after this, you can pass in a flag <code>--packageManager=<package-manager></code>, when you run <code>vue create</code>. So if you wanted to create the <code>moz-todo-vue</code> project with npm and you'd previously chosen yarn, you’d run <code>vue create moz-todo-vue --packageManager=npm</code>.</p> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: We've not gone over all of the options here, but you can <a href="https://cli.vuejs.org">find more information on the CLI</a> in the Vue docs.</p> -</div> - -<h2 id="Project_structure">Project structure</h2> - -<p>If everything went successfully, the CLI should have created a series of files and directories for your project. The most significant ones are as follows:</p> - -<ul> - <li><code>.eslintrc.js</code>: This is a config file for <a href="https://eslint.org/">eslint</a>. You can use this to manage your linting rules.</li> - <li><code>babel.config.js</code>: This is the config file for <a href="https://babeljs.io/">Babel</a>, which transforms modern JavaScript features being used in development code into older syntax that is more cross-browser compatible in production code. You can register additional babel plugins in this file.</li> - <li><code>.browserslistrc</code>: This is a config for <a href="https://github.com/browserslist/browserslist">Browserslist</a>. You can use this to control which browsers your tooling optimizes for.</li> - <li><code>public</code>: This directory contains static assets that are published, but not processed by <a href="https://webpack.js.org/">Webpack</a> during build (with one exception; <code>index.html</code> gets some processing). - <ul> - <li><code>favicon.ico</code>: This is the favicon for your app. Currently, it's the Vue logo.</li> - <li><code>index.html</code>: This is the template for your app. Your Vue app is run from this HTML page, and you can use lodash template syntax to interpolate values into it. - <div class="note"><strong>Note</strong>: this is not the template for managing the layout of your application — this template is for managing static HTML that sits outside of your Vue app. Editing this file typically only occurs in advanced use cases.</div> - </li> - </ul> - </li> - <li><code>src</code>: This directory contains the core of your Vue app. - <ul> - <li><code>main.js</code>: this is the entry point to your application. Currently, this file initializes your Vue application and signifies which HTML element in the <code>index.html</code> file your app should be attached to. This file is often where you register global components or additional Vue libraries.</li> - <li><code>App.vue</code>: this is the top-level component in your Vue app. See below for more explanation of Vue components.</li> - <li><code>components</code>: this directory is where you keep your components. Currently it just has one example component.</li> - <li><code>assets</code>: This directory is for storing static assets like CSS and images. Because these files are in the source directory, they can be processed by Webpack. This means you can use pre-processors like <a href="https://sass-lang.com/">Sass/SCSS</a> or <a href="https://stylus-lang.com/">Stylus</a>.</li> - </ul> - </li> -</ul> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: Depending on the options you select when creating a new project, there might be other directories present (for example, if you choose a router, you will also have a <code>views</code> directory).</p> -</div> - -<h2 id=".vue_files_single_file_components">.vue files (single file components)</h2> - -<p>Like in many front-end frameworks, components are a central part of building apps in Vue. These components let you break a large application into discrete building blocks that can be created and managed separately, and transfer data between each other as required. These small blocks can help you reason about and test your code.</p> - -<p>While some frameworks encourage you to separate your template, logic, and styling code into separate files, Vue takes the opposite approach. Using <a href="https://vuejs.org/v2/guide/single-file-components.html">Single File Components</a>, Vue lets you group your templates, corresponding script, and CSS all together in a single file ending in <code>.vue</code>. These files are processed by a JS build tool (such as Webpack), which means you can take advantage of build-time tooling in your project. This allows you to use tools like Babel, TypeScript, SCSS and more to create more sophisticated components.</p> - -<p>As a bonus, projects created with the Vue CLI are configured to use <code>.vue</code> files with Webpack out of the box. In fact, if you look inside the <code>src</code> folder in the project we created with the CLI, you'll see your first <code>.vue</code> file: <code>App.vue</code>.</p> - -<p>Let's explore this now.</p> - -<h3 id="App.vue">App.vue</h3> - -<p>Open your <code>App.vue</code> file — you’ll see that it has three parts: <code><template></code>, <code><script></code>, and <code><style></code>, which contain the component’s template, scripting, and styling information. All Single File Components share this same basic structure.</p> - -<p><code><template></code> contains all the markup structure and display logic of your component. Your template can contain any valid HTML, as well as some Vue-specific syntax that we'll cover later.</p> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: By setting the <code>lang</code> attribute on the <code><template></code> tag, you can use Pug template syntax instead of standard HTML — <code><template lang="pug"></code>. We'll stick to standard HTML through this tutorial, but it is worth knowing that this is possible.</p> -</div> - -<p><code><script></code> contains all of the non-display logic of your component. Most importantly, your <code><script></code> tag needs to have a default exported JS object. This object is where you locally register components, define component inputs (props), handle local state, define methods, and more. Your build step will process this object and transform it (with your template) into a Vue component with a <code>render()</code> function.</p> - -<p>In the case of <code>App.vue</code>, our default export sets the name of the component to <code>app</code> and registers the <code>HelloWorld</code> component by adding it into the <code>components</code> property. When you register a component in this way, you're registering it locally. Locally registered components can only be used inside the components that register them, so you need to import and register them in every component file that uses them. This can be useful for bundle splitting/tree shaking since not every page in your app necessarily needs every component.</p> - -<pre class="brush: js notranslate">import HelloWorld from './components/HelloWorld.vue'; - -export default { - name: 'app', - components: { - //You can register components locally here. - HelloWorld - } -};</pre> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: If you want to use <a href="https://www.typescriptlang.org/">TypeScript</a> syntax, you need to set the <code>lang</code> attribute on the <code><script></code> tag to signify to the compiler that you're using TypeScript — <code><script lang="ts"></code>.</p> -</div> - -<p><code><style></code> is where you write your CSS for the component. If you add a <code>scoped</code> attribute — <code><style scoped></code> — Vue will scope the styles to the contents of your SFC. This works similar to CSS-in-JS solutions, but allows you to just write plain CSS.</p> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: If you select a CSS pre-processor when creating the project via the CLI, you can add a <code>lang</code> attribute to the <code><style></code> tag so that the contents can be processed by Webpack at build time. For example, <code><style lang="scss"></code> will allow you to use SCSS syntax in your styling information.</p> -</div> - -<h2 id="Running_the_app_locally">Running the app locally</h2> - -<p>The Vue CLI comes with a built-in development server. This allows you to run your app locally so you can test it easily without needing to configure a server yourself. The CLI adds a <code>serve</code> command to the project’s <code>package.json</code> file as an npm script, so you can easily run it.</p> - -<p>In your terminal, try running <code>npm run serve</code> (or <code>yarn serve</code> if you prefer yarn). Your terminal should output something like the following:</p> - -<pre class="notranslate">INFO Starting development server... -98% after emitting CopyPlugin - - DONE Compiled successfully in 18121ms - - App running at: - - Local: <http://localhost:8080/> - - Network: <http://192.168.1.9:8080/> - - Note that the development build is not optimized. - To create a production build, run npm run build.</pre> - -<p>If you navigate to the “local” address in a new browser tab (this should be something like <code>http://localhost:8080</code> as stated above, but may vary based on your setup), you should see your app. Right now, it should contain a welcome message, a link to the Vue documentation, links to the plugins you added when you initialized the app with your CLI, and some other useful links to the Vue community and ecosystem.</p> - -<p><img alt="default vue app render, with vue logo, welcome message, and some documentation links" src="https://mdn.mozillademos.org/files/17240/vue-default-app.png" style="border-style: solid; border-width: 1px; height: 779px; width: 1600px;"></p> - -<h2 id="Making_a_couple_of_changes">Making a couple of changes</h2> - -<p>Let's make our first change to the app — we’ll delete the Vue logo. Open the <code>App.vue</code> file, and delete the <code><a href="/en-US/docs/Web/HTML/Element/img"><img></a></code> element from the template section:</p> - -<pre class="brush: html notranslate"><span class="author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90z8h7gz67ziz76zcz77zz80zz71zncfz69zz69ziaz82zz71zz72zhz77zz122zz90z14mcyd"><img alt="Vue logo" src="./assets/logo.png"></span></pre> - -<p>If your server is still running, you should see the logo removed from the rendered site almost instantly. Let’s also remove the <code>HelloWorld</code> component from our template.</p> - -<p>First of all delete this line:</p> - -<pre class="brush: html notranslate"><HelloWorld msg="Welcome to Your Vue.js App"/></pre> - -<p>If you save your <code>App.vue</code> file now, the rendered app will throw an error because we’ve registered the component but are not using it. We also need to remove the lines from inside the <code><script></code> element that import and register the component:</p> - -<p>Delete these lines now:</p> - -<pre class="brush: js notranslate">import HelloWorld from './components/HelloWorld.vue'</pre> - -<pre class="brush: js notranslate">components: { - HelloWorld -}</pre> - -<p>Your rendered app should no longer show an error, just a blank page, as we currently have no visible content inside <code><template></code>.</p> - -<p>Let’s add a new <code><h1></code> inside <code><div id="app"></code>. Since we’re going to be creating a todo list app below, let's set our header text to "To-Do List". Add it like so:</p> - -<pre class="brush: html notranslate"><template> - <div id="app"> - <h1>To-Do List</h1> - </div> -</template></pre> - -<p><code>App.vue</code> will now show our heading, as you'd expect.</p> - -<h2 id="Summary">Summary</h2> - -<p>Let's leave this here for now. We've learnt about some of the ideas behind Vue, created some scaffolding for our example app to live inside, inspected it, and made a few preliminary changes.</p> - -<p>With a basic introduction out of the way, we'll now go further and build up our sample app, a basic Todo list application that allows us to store a list of items, check them off when done, and filter the list by all, complete, and incomplete todos.</p> - -<p>In the next article we'll build our first custom component, and look at some important concepts such as passing props into it and saving its data state.</p> - -<p>{{PreviousMenuNext("Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources","Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component", "Learn/Tools_and_testing/Client-side_JavaScript_frameworks")}}</p> - -<h2 id="In_this_module">In this module</h2> - -<ul> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction">Introduction to client-side frameworks</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features">Framework main features</a></li> - <li>React - <ul> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">Getting started with React</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning">Beginning our React todo list</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components">Componentizing our React app</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state">React interactivity: Events and state</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering">React interactivity: Editing, filtering, conditional rendering</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility">Accessibility in React</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources">React resources</a></li> - </ul> - </li> - <li>Ember - <ul> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started">Getting started with Ember</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization">Ember app structure and componentization</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state">Ember interactivity: Events, classes and state</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer">Ember Interactivity: Footer functionality, conditional rendering</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing">Routing in Ember</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources">Ember resources and troubleshooting</a></li> - </ul> - </li> - <li>Vue - <ul> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started">Getting started with Vue</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component">Creating our first Vue component</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists">Rendering a list of Vue components</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models">Adding a new todo form: Vue events, methods, and models</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling">Styling Vue components with CSS</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties">Using Vue computed properties</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering">Vue conditional rendering: editing existing todos</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management">Focus management with Vue refs</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources">Vue resources</a></li> - </ul> - </li> - <li>Svelte - <ul> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started">Getting started with Svelte</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning">Starting our Svelte Todo list app</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props">Dynamic behavior in Svelte: working with variables and props</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components">Componentizing our Svelte app</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility">Advanced Svelte: Reactivity, lifecycle, accessibility</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores">Working with Svelte stores</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript">TypeScript support in Svelte</a></li> - <li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next">Deployment and next steps</a></li> - </ul> - </li> -</ul> |